Merge branch 'master' of https://github.com/Erope/nezha
This commit is contained in:
commit
e6385773ba
7
.github/workflows/agent.yml
vendored
7
.github/workflows/agent.yml
vendored
@ -10,6 +10,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
- name: Download UPX
|
||||||
|
run: |
|
||||||
|
wget https://github.com/upx/upx/releases/download/v3.95/upx-3.95-amd64_linux.tar.xz
|
||||||
|
tar --strip-components=1 -xf upx-3.95-amd64_linux.tar.xz && sudo mv upx /usr/bin/
|
||||||
|
git reset --hard
|
||||||
|
git clean -f -d
|
||||||
|
upx --version
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v2
|
uses: goreleaser/goreleaser-action@v2
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
1
.github/workflows/dashboard.yml
vendored
1
.github/workflows/dashboard.yml
vendored
@ -10,6 +10,7 @@ on:
|
|||||||
- "script/**"
|
- "script/**"
|
||||||
- "*.md"
|
- "*.md"
|
||||||
- ".*"
|
- ".*"
|
||||||
|
- ".github/workflows/agent.yml"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
|
@ -15,11 +15,19 @@ builds:
|
|||||||
- 386
|
- 386
|
||||||
- amd64
|
- amd64
|
||||||
- mips
|
- mips
|
||||||
- mips64
|
|
||||||
gomips:
|
gomips:
|
||||||
- softfloat
|
- softfloat
|
||||||
|
ignore:
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm64
|
||||||
main: ./cmd/agent
|
main: ./cmd/agent
|
||||||
binary: nezha-agent
|
binary: nezha-agent
|
||||||
|
# 为路由器、开发板缩小二进制体积
|
||||||
|
hooks:
|
||||||
|
post:
|
||||||
|
- upx --best "{{.Path}}"
|
||||||
checksum:
|
checksum:
|
||||||
name_template: "checksums.txt"
|
name_template: "checksums.txt"
|
||||||
snapshot:
|
snapshot:
|
||||||
|
36
README.md
36
README.md
@ -1,13 +1,14 @@
|
|||||||
<div align="center" style="background-color: white">
|
<div align="center" style="background-color: white">
|
||||||
<img width="500" style="max-width:100%" src="https://raw.githubusercontent.com/naiba/nezha/master/resource/static/brand.png" title="哪吒监控">
|
<img width="500" style="max-width:100%" src="https://raw.githubusercontent.com/naiba/nezha/master/resource/static/brand.png" title="哪吒监控">
|
||||||
<br><br>
|
<br><br>
|
||||||
<img src="https://img.shields.io/github/workflow/status/naiba/nezha/Dashboard%20image?label=Dash%20v0.4.15&logo=github&style=for-the-badge"> <img src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/workflow/status/naiba/nezha/Agent%20release?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.4.10-brightgreen?style=for-the-badge&logo=linux">
|
<img src="https://img.shields.io/github/workflow/status/naiba/nezha/Dashboard%20image?label=Dash%20v0.7.1&logo=github&style=for-the-badge"> <img src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/workflow/status/naiba/nezha/Agent%20release?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.5.0-brightgreen?style=for-the-badge&logo=linux">
|
||||||
<br>
|
<br>
|
||||||
<p>:trollface: 哪吒监控 一站式轻监控轻运维系统。支持系统状态、HTTP(SSL 证书变更、即将到期、到期)、TCP、Ping 监控报警,命令批量执行和计划任务。</p>
|
<p>:trollface: 哪吒监控 一站式轻监控轻运维系统。支持系统状态、HTTP(SSL 证书变更、即将到期、到期)、TCP、Ping 监控报警,命令批量执行和计划任务。</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
\>> QQ 交流群: ~~955957790~~ 已解散,**自2021年3月26起不再提供技术支持,接受PR。**<br>
|
\>> 交流论坛:[打杂社区](https://daza.net/c/nezha) (Lemmy)
|
||||||
\>> 交流论坛:正在选型搭建中…… 欢迎想建设社区的铁子与奶爸取得联系。
|
|
||||||
|
\>> QQ 交流群:872069346 **加群要求:已搭建好哪吒监控 & 有 2+ 服务器**
|
||||||
|
|
||||||
\>> [我们的用户](https://www.google.com/search?q="powered+by+哪吒监控%7C哪吒面板"&filter=0) (Google)
|
\>> [我们的用户](https://www.google.com/search?q="powered+by+哪吒监控%7C哪吒面板"&filter=0) (Google)
|
||||||
|
|
||||||
@ -17,7 +18,7 @@
|
|||||||
|
|
||||||
## 安装脚本
|
## 安装脚本
|
||||||
|
|
||||||
**推荐配置:** 安装前解析 _两个域名_ 到面板服务器,一个作为 _公开访问_ ,可以 **接入 CDN**,比如 (status.nai.ba);另外一个作为安装 Agent 时连接 Dashboard 使用,**不能接入 CDN** 直接暴露面板主机 IP,比如(randomdashboard.nai.ba)。
|
**推荐配置:** 安装前准备 _两个域名_,一个可以 **接入 CDN** 作为 _公开访问_,比如 (status.nai.ba);另外一个解析到面板服务器作为 Agent 连接 Dashboard 使用,**不能接入 CDN** 直接暴露面板主机 IP,比如(randomdashboard.nai.ba)。
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.sh -o nezha.sh && chmod +x nezha.sh
|
curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.sh -o nezha.sh && chmod +x nezha.sh
|
||||||
@ -58,11 +59,13 @@ URL 里面也可放置占位符,请求时会进行简单的字符串替换。
|
|||||||
1. 添加通知方式
|
1. 添加通知方式
|
||||||
|
|
||||||
- server 酱示例
|
- server 酱示例
|
||||||
|
|
||||||
- 名称:server 酱
|
- 名称:server 酱
|
||||||
- URL:https://sc.ftqq.com/SCUrandomkeys.send?text=#NEZHA#
|
- URL:https://sc.ftqq.com/SCUrandomkeys.send?text=#NEZHA#
|
||||||
- 请求方式: GET
|
- 请求方式: GET
|
||||||
- 请求类型: 默认
|
- 请求类型: 默认
|
||||||
- Body: 空
|
- Body: 空
|
||||||
|
|
||||||
- wxpusher 示例,需要关注你的应用
|
- wxpusher 示例,需要关注你的应用
|
||||||
|
|
||||||
- 名称: wxpusher
|
- 名称: wxpusher
|
||||||
@ -72,6 +75,7 @@ URL 里面也可放置占位符,请求时会进行简单的字符串替换。
|
|||||||
- Body: `{"appToken":"你的appToken","topicIds":[],"content":"#NEZHA#","contentType":"1","uids":["你的uid"]}`
|
- Body: `{"appToken":"你的appToken","topicIds":[],"content":"#NEZHA#","contentType":"1","uids":["你的uid"]}`
|
||||||
|
|
||||||
- telegram 示例 [@haitau](https://github.com/haitau) 贡献
|
- telegram 示例 [@haitau](https://github.com/haitau) 贡献
|
||||||
|
|
||||||
- 名称:telegram 机器人消息通知
|
- 名称:telegram 机器人消息通知
|
||||||
- URL:https://api.telegram.org/botXXXXXX/sendMessage?chat_id=YYYYYY&text=#NEZHA#
|
- URL:https://api.telegram.org/botXXXXXX/sendMessage?chat_id=YYYYYY&text=#NEZHA#
|
||||||
- 请求方式: GET
|
- 请求方式: GET
|
||||||
@ -121,7 +125,17 @@ URL 里面也可放置占位符,请求时会进行简单的字符串替换。
|
|||||||
</style>
|
</style>
|
||||||
```
|
```
|
||||||
|
|
||||||
- 默认主题修改 LOGO、移除版权示例(来自 [@iLay1678](https://github.com/iLay1678),欢迎 PR)
|
- DayNight 主题更改进度条颜色示例(来自 [@hyt-allen-xu](https://github.com/hyt-allen-xu))
|
||||||
|
|
||||||
|
```
|
||||||
|
<style>
|
||||||
|
.ui.fine.progress> .progress-bar {
|
||||||
|
background-color: #00a7d0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
- 默认主题修改 LOGO、移除版权示例(来自 [@iLay1678](https://github.com/iLay1678))
|
||||||
|
|
||||||
```
|
```
|
||||||
<style>
|
<style>
|
||||||
@ -147,6 +161,18 @@ URL 里面也可放置占位符,请求时会进行简单的字符串替换。
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- DayNight 移除版权示例(来自 [@hyt-allen-xu](https://github.com/hyt-allen-xu))
|
||||||
|
|
||||||
|
```
|
||||||
|
<script>
|
||||||
|
window.onload = function(){
|
||||||
|
var footer=document.querySelector("div.footer-container")
|
||||||
|
footer.innerHTML="©2021 你的名字 & Powered by 你的名字"
|
||||||
|
footer.style.visibility="visible"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
- hotaru 主题更改背景图片示例
|
- hotaru 主题更改背景图片示例
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -9,9 +9,9 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/blang/semver"
|
"github.com/blang/semver"
|
||||||
@ -35,11 +35,10 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
reporting bool
|
|
||||||
client pb.NezhaServiceClient
|
client pb.NezhaServiceClient
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
delayWhenError = time.Second * 10 // Agent 重连间隔
|
delayWhenError = time.Second * 10 // Agent 重连间隔
|
||||||
updateCh = make(chan struct{}, 0) // Agent 自动更新间隔
|
updateCh = make(chan struct{}) // Agent 自动更新间隔
|
||||||
httpClient = &http.Client{
|
httpClient = &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
@ -56,17 +55,16 @@ func doSelfUpdate() {
|
|||||||
updateCh <- struct{}{}
|
updateCh <- struct{}{}
|
||||||
}()
|
}()
|
||||||
v := semver.MustParse(version)
|
v := semver.MustParse(version)
|
||||||
log.Println("Check update", v)
|
println("Check update", v)
|
||||||
latest, err := selfupdate.UpdateSelf(v, "naiba/nezha")
|
latest, err := selfupdate.UpdateSelf(v, "naiba/nezha")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Binary update failed:", err)
|
println("Binary update failed:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if latest.Version.Equals(v) {
|
if latest.Version.Equals(v) {
|
||||||
// latest version is the same as current version. It means current binary is up to date.
|
println("Current binary is up to date", version)
|
||||||
log.Println("Current binary is the latest version", version)
|
|
||||||
} else {
|
} else {
|
||||||
log.Println("Successfully updated to version", latest.Version)
|
println("Upgrade successfully", latest.Version)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,7 +79,7 @@ func main() {
|
|||||||
|
|
||||||
var debug bool
|
var debug bool
|
||||||
flag.String("i", "", "unused 旧Agent配置兼容")
|
flag.String("i", "", "unused 旧Agent配置兼容")
|
||||||
flag.BoolVar(&debug, "d", false, "允许不安全连接")
|
flag.BoolVar(&debug, "d", false, "开启调试信息")
|
||||||
flag.StringVar(&server, "s", "localhost:5555", "管理面板RPC端口")
|
flag.StringVar(&server, "s", "localhost:5555", "管理面板RPC端口")
|
||||||
flag.StringVar(&clientSecret, "p", "", "Agent连接Secret")
|
flag.StringVar(&clientSecret, "p", "", "Agent连接Secret")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -121,18 +119,18 @@ func run() {
|
|||||||
var conn *grpc.ClientConn
|
var conn *grpc.ClientConn
|
||||||
|
|
||||||
retry := func() {
|
retry := func() {
|
||||||
log.Println("Error to close connection ...")
|
println("Error to close connection ...")
|
||||||
if conn != nil {
|
if conn != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
time.Sleep(delayWhenError)
|
time.Sleep(delayWhenError)
|
||||||
log.Println("Try to reconnect ...")
|
println("Try to reconnect ...")
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
conn, err = grpc.Dial(server, grpc.WithInsecure(), grpc.WithPerRPCCredentials(&auth))
|
conn, err = grpc.Dial(server, grpc.WithInsecure(), grpc.WithPerRPCCredentials(&auth))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("grpc.Dial err: %v", err)
|
println("grpc.Dial err: ", err)
|
||||||
retry()
|
retry()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -140,26 +138,26 @@ func run() {
|
|||||||
// 第一步注册
|
// 第一步注册
|
||||||
_, err = client.ReportSystemInfo(ctx, monitor.GetHost().PB())
|
_, err = client.ReportSystemInfo(ctx, monitor.GetHost().PB())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("client.ReportSystemInfo err: %v", err)
|
println("client.ReportSystemInfo err: ", err)
|
||||||
retry()
|
retry()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// 执行 Task
|
// 执行 Task
|
||||||
tasks, err := client.RequestTask(ctx, monitor.GetHost().PB())
|
tasks, err := client.RequestTask(ctx, monitor.GetHost().PB())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("client.RequestTask err: %v", err)
|
println("client.RequestTask err: ", err)
|
||||||
retry()
|
retry()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = receiveTasks(tasks)
|
err = receiveTasks(tasks)
|
||||||
log.Printf("receiveTasks exit to main: %v", err)
|
println("receiveTasks exit to main: ", err)
|
||||||
retry()
|
retry()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func receiveTasks(tasks pb.NezhaService_RequestTaskClient) error {
|
func receiveTasks(tasks pb.NezhaService_RequestTaskClient) error {
|
||||||
var err error
|
var err error
|
||||||
defer log.Printf("receiveTasks exit %v => %v", time.Now(), err)
|
defer println("receiveTasks exit", time.Now(), "=>", err)
|
||||||
for {
|
for {
|
||||||
var task *pb.Task
|
var task *pb.Task
|
||||||
task, err = tasks.Recv()
|
task, err = tasks.Recv()
|
||||||
@ -179,14 +177,18 @@ func doTask(task *pb.Task) {
|
|||||||
start := time.Now()
|
start := time.Now()
|
||||||
resp, err := httpClient.Get(task.GetData())
|
resp, err := httpClient.Get(task.GetData())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
result.Delay = float32(time.Now().Sub(start).Microseconds()) / 1000.0
|
// 检查 HTTP Response 状态
|
||||||
|
result.Delay = float32(time.Since(start).Microseconds()) / 1000.0
|
||||||
if resp.StatusCode > 399 || resp.StatusCode < 200 {
|
if resp.StatusCode > 399 || resp.StatusCode < 200 {
|
||||||
err = errors.New("\n应用错误:" + resp.Status)
|
err = errors.New("\n应用错误:" + resp.Status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if strings.HasPrefix(task.GetData(), "https://") {
|
// 检查 SSL 证书信息
|
||||||
c := cert.NewCert(task.GetData()[8:])
|
serviceUrl, err := url.Parse(task.GetData())
|
||||||
|
if err == nil {
|
||||||
|
if serviceUrl.Scheme == "https" {
|
||||||
|
c := cert.NewCert(serviceUrl.Host)
|
||||||
if c.Error != "" {
|
if c.Error != "" {
|
||||||
result.Data = "SSL证书错误:" + c.Error
|
result.Data = "SSL证书错误:" + c.Error
|
||||||
} else {
|
} else {
|
||||||
@ -197,6 +199,10 @@ func doTask(task *pb.Task) {
|
|||||||
result.Successful = true
|
result.Successful = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
result.Data = "URL解析错误:" + err.Error()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// HTTP 请求失败
|
||||||
result.Data = err.Error()
|
result.Data = err.Error()
|
||||||
}
|
}
|
||||||
case model.TaskTypeICMPPing:
|
case model.TaskTypeICMPPing:
|
||||||
@ -219,7 +225,7 @@ func doTask(task *pb.Task) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
conn.Write([]byte("ping\n"))
|
conn.Write([]byte("ping\n"))
|
||||||
conn.Close()
|
conn.Close()
|
||||||
result.Delay = float32(time.Now().Sub(start).Microseconds()) / 1000.0
|
result.Delay = float32(time.Since(start).Microseconds()) / 1000.0
|
||||||
result.Successful = true
|
result.Successful = true
|
||||||
} else {
|
} else {
|
||||||
result.Data = err.Error()
|
result.Data = err.Error()
|
||||||
@ -260,9 +266,9 @@ func doTask(task *pb.Task) {
|
|||||||
result.Data = string(output)
|
result.Data = string(output)
|
||||||
result.Successful = true
|
result.Successful = true
|
||||||
}
|
}
|
||||||
result.Delay = float32(time.Now().Sub(startedAt).Seconds())
|
result.Delay = float32(time.Since(startedAt).Seconds())
|
||||||
default:
|
default:
|
||||||
log.Printf("Unknown action: %v", task)
|
println("Unknown action: ", task)
|
||||||
}
|
}
|
||||||
client.ReportTask(ctx, &result)
|
client.ReportTask(ctx, &result)
|
||||||
}
|
}
|
||||||
@ -270,13 +276,13 @@ func doTask(task *pb.Task) {
|
|||||||
func reportState() {
|
func reportState() {
|
||||||
var lastReportHostInfo time.Time
|
var lastReportHostInfo time.Time
|
||||||
var err error
|
var err error
|
||||||
defer log.Printf("reportState exit %v => %v", time.Now(), err)
|
defer println("reportState exit", time.Now(), "=>", err)
|
||||||
for {
|
for {
|
||||||
if client != nil {
|
if client != nil {
|
||||||
monitor.TrackNetworkSpeed()
|
monitor.TrackNetworkSpeed()
|
||||||
_, err = client.ReportSystemState(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)
|
println("reportState error", 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)) {
|
||||||
@ -286,3 +292,9 @@ func reportState() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func println(v ...interface{}) {
|
||||||
|
if dao.Conf.Debug {
|
||||||
|
log.Println(v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package monitor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -17,12 +18,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var netInSpeed, netOutSpeed, netInTransfer, netOutTransfer, lastUpdate uint64
|
var netInSpeed, netOutSpeed, netInTransfer, netOutTransfer, lastUpdate uint64
|
||||||
|
var expectDiskFsTypes = []string{
|
||||||
|
"apfs", "ext4", "ext3", "ext2", "f2fs", "reiserfs", "jfs", "btrfs", "fuseblk", "zfs", "simfs", "ntfs", "fat32", "exfat", "xfs",
|
||||||
|
}
|
||||||
|
var excludeNetInterfaces = []string{
|
||||||
|
"lo", "tun", "docker", "veth", "br-", "vmbr", "vnet", "kube",
|
||||||
|
}
|
||||||
|
var getMacDiskNo = regexp.MustCompile(`\/dev\/disk(\d)s.*`)
|
||||||
|
|
||||||
func GetHost() *model.Host {
|
func GetHost() *model.Host {
|
||||||
hi, _ := host.Info()
|
hi, _ := host.Info()
|
||||||
var cpuType string
|
var cpuType string
|
||||||
if hi.VirtualizationSystem != "" {
|
if hi.VirtualizationSystem != "" {
|
||||||
cpuType = "Virtual"
|
cpuType = "Vrtual"
|
||||||
} else {
|
} else {
|
||||||
cpuType = "Physical"
|
cpuType = "Physical"
|
||||||
}
|
}
|
||||||
@ -37,15 +45,15 @@ func GetHost() *model.Host {
|
|||||||
}
|
}
|
||||||
mv, _ := mem.VirtualMemory()
|
mv, _ := mem.VirtualMemory()
|
||||||
ms, _ := mem.SwapMemory()
|
ms, _ := mem.SwapMemory()
|
||||||
u, _ := disk.Usage("/")
|
diskTotal, _ := getDiskTotalAndUsed()
|
||||||
|
|
||||||
return &model.Host{
|
return &model.Host{
|
||||||
Platform: hi.OS,
|
Platform: hi.OS,
|
||||||
PlatformVersion: hi.PlatformVersion,
|
PlatformVersion: hi.PlatformVersion,
|
||||||
CPU: cpus,
|
CPU: cpus,
|
||||||
MemTotal: mv.Total,
|
MemTotal: mv.Total,
|
||||||
DiskTotal: u.Total,
|
|
||||||
SwapTotal: ms.Total,
|
SwapTotal: ms.Total,
|
||||||
|
DiskTotal: diskTotal,
|
||||||
Arch: hi.KernelArch,
|
Arch: hi.KernelArch,
|
||||||
Virtualization: hi.VirtualizationSystem,
|
Virtualization: hi.VirtualizationSystem,
|
||||||
BootTime: hi.BootTime,
|
BootTime: hi.BootTime,
|
||||||
@ -57,23 +65,19 @@ func GetHost() *model.Host {
|
|||||||
|
|
||||||
func GetState(delay int64) *model.HostState {
|
func GetState(delay int64) *model.HostState {
|
||||||
hi, _ := host.Info()
|
hi, _ := host.Info()
|
||||||
// Memory
|
|
||||||
mv, _ := mem.VirtualMemory()
|
mv, _ := mem.VirtualMemory()
|
||||||
ms, _ := mem.SwapMemory()
|
ms, _ := mem.SwapMemory()
|
||||||
// CPU
|
|
||||||
var cpuPercent float64
|
var cpuPercent float64
|
||||||
cp, err := cpu.Percent(time.Second*time.Duration(delay), false)
|
cp, err := cpu.Percent(time.Second*time.Duration(delay), false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cpuPercent = cp[0]
|
cpuPercent = cp[0]
|
||||||
}
|
}
|
||||||
// Disk
|
_, diskUsed := getDiskTotalAndUsed()
|
||||||
u, _ := disk.Usage("/")
|
|
||||||
|
|
||||||
return &model.HostState{
|
return &model.HostState{
|
||||||
CPU: cpuPercent,
|
CPU: cpuPercent,
|
||||||
MemUsed: mv.Used,
|
MemUsed: mv.Used,
|
||||||
SwapUsed: ms.Used,
|
SwapUsed: ms.Used,
|
||||||
DiskUsed: u.Used,
|
DiskUsed: diskUsed,
|
||||||
NetInTransfer: atomic.LoadUint64(&netInTransfer),
|
NetInTransfer: atomic.LoadUint64(&netInTransfer),
|
||||||
NetOutTransfer: atomic.LoadUint64(&netOutTransfer),
|
NetOutTransfer: atomic.LoadUint64(&netOutTransfer),
|
||||||
NetInSpeed: atomic.LoadUint64(&netInSpeed),
|
NetInSpeed: atomic.LoadUint64(&netInSpeed),
|
||||||
@ -84,10 +88,15 @@ func GetState(delay int64) *model.HostState {
|
|||||||
|
|
||||||
func TrackNetworkSpeed() {
|
func TrackNetworkSpeed() {
|
||||||
var innerNetInTransfer, innerNetOutTransfer uint64
|
var innerNetInTransfer, innerNetOutTransfer uint64
|
||||||
nc, err := net.IOCounters(false)
|
nc, err := net.IOCounters(true)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
innerNetInTransfer += nc[0].BytesRecv
|
for _, v := range nc {
|
||||||
innerNetOutTransfer += nc[0].BytesSent
|
if isListContainsStr(excludeNetInterfaces, v.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
innerNetInTransfer += v.BytesRecv
|
||||||
|
innerNetOutTransfer += v.BytesSent
|
||||||
|
}
|
||||||
now := uint64(time.Now().Unix())
|
now := uint64(time.Now().Unix())
|
||||||
diff := now - atomic.LoadUint64(&lastUpdate)
|
diff := now - atomic.LoadUint64(&lastUpdate)
|
||||||
if diff > 0 {
|
if diff > 0 {
|
||||||
@ -99,3 +108,40 @@ func TrackNetworkSpeed() {
|
|||||||
atomic.StoreUint64(&lastUpdate, now)
|
atomic.StoreUint64(&lastUpdate, now)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDiskTotalAndUsed() (total uint64, used uint64) {
|
||||||
|
diskList, _ := disk.Partitions(false)
|
||||||
|
devices := make(map[string]string)
|
||||||
|
countedDiskForMac := make(map[string]struct{})
|
||||||
|
for _, d := range diskList {
|
||||||
|
fsType := strings.ToLower(d.Fstype)
|
||||||
|
// 不统计 K8s 的虚拟挂载点:https://github.com/shirou/gopsutil/issues/1007
|
||||||
|
if devices[d.Device] == "" && isListContainsStr(expectDiskFsTypes, fsType) && !strings.Contains(d.Mountpoint, "/var/lib/kubelet") {
|
||||||
|
devices[d.Device] = d.Mountpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for device, mountPath := range devices {
|
||||||
|
diskUsageOf, _ := disk.Usage(mountPath)
|
||||||
|
// 这里是针对 Mac 机器的处理,https://github.com/giampaolo/psutil/issues/1509
|
||||||
|
matches := getMacDiskNo.FindStringSubmatch(device)
|
||||||
|
if len(matches) == 2 {
|
||||||
|
if _, has := countedDiskForMac[matches[1]]; !has {
|
||||||
|
countedDiskForMac[matches[1]] = struct{}{}
|
||||||
|
total += diskUsageOf.Total
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
total += diskUsageOf.Total
|
||||||
|
}
|
||||||
|
used += diskUsageOf.Used
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isListContainsStr(list []string, str string) bool {
|
||||||
|
for i := 0; i < len(list); i++ {
|
||||||
|
if strings.Contains(list[i], str) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -3,7 +3,6 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -80,79 +79,8 @@ func (p *commonPage) checkViewPassword(c *gin.Context) {
|
|||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceItem struct {
|
|
||||||
Monitor model.Monitor
|
|
||||||
TotalUp uint64
|
|
||||||
TotalDown uint64
|
|
||||||
CurrentUp uint64
|
|
||||||
CurrentDown uint64
|
|
||||||
Delay *[30]float32
|
|
||||||
Up *[30]int
|
|
||||||
Down *[30]int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *commonPage) service(c *gin.Context) {
|
func (p *commonPage) service(c *gin.Context) {
|
||||||
var msm map[uint64]*ServiceItem
|
msm := dao.ServiceSentinelShared.LoadStats()
|
||||||
|
|
||||||
var cached bool
|
|
||||||
if _, has := c.Get(model.CtxKeyAuthorizedUser); !has {
|
|
||||||
data, has := dao.Cache.Get(model.CacheKeyServicePage)
|
|
||||||
if has {
|
|
||||||
log.Println("use cache")
|
|
||||||
msm = data.(map[uint64]*ServiceItem)
|
|
||||||
cached = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cached {
|
|
||||||
msm = make(map[uint64]*ServiceItem)
|
|
||||||
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)
|
|
||||||
|
|
||||||
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},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 整合数据
|
|
||||||
todayStatus := make(map[uint64][]bool)
|
|
||||||
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)
|
|
||||||
} else {
|
|
||||||
todayStatus[mhs[i].MonitorID] = append(todayStatus[mhs[i].MonitorID], mhs[i].Successful)
|
|
||||||
}
|
|
||||||
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]++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 当日最后 20 个采样作为当前状态
|
|
||||||
for _, m := range msm {
|
|
||||||
for i := len(todayStatus[m.Monitor.ID]) - 1; i >= 0 && i >= (len(todayStatus[m.Monitor.ID])-1-20); i-- {
|
|
||||||
if todayStatus[m.Monitor.ID][i] {
|
|
||||||
m.CurrentUp++
|
|
||||||
} else {
|
|
||||||
m.CurrentDown++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 未登录人员缓存十分钟
|
|
||||||
dao.Cache.Set(model.CacheKeyServicePage, msm, time.Minute*10)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "theme-"+dao.Conf.Site.Theme+"/service", mygin.CommonEnvironment(c, gin.H{
|
c.HTML(http.StatusOK, "theme-"+dao.Conf.Site.Theme+"/service", mygin.CommonEnvironment(c, gin.H{
|
||||||
"Title": "服务状态",
|
"Title": "服务状态",
|
||||||
"Services": msm,
|
"Services": msm,
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.cloudfoundry.org/bytefmt"
|
"code.cloudfoundry.org/bytefmt"
|
||||||
|
"github.com/gin-contrib/pprof"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/naiba/nezha/pkg/mygin"
|
"github.com/naiba/nezha/pkg/mygin"
|
||||||
@ -15,10 +16,11 @@ import (
|
|||||||
|
|
||||||
func ServeWeb(port uint) {
|
func ServeWeb(port uint) {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
r := gin.Default()
|
||||||
if dao.Conf.Debug {
|
if dao.Conf.Debug {
|
||||||
gin.SetMode(gin.DebugMode)
|
gin.SetMode(gin.DebugMode)
|
||||||
|
pprof.Register(r)
|
||||||
}
|
}
|
||||||
r := gin.Default()
|
|
||||||
r.Use(mygin.RecordPath)
|
r.Use(mygin.RecordPath)
|
||||||
r.SetFuncMap(template.FuncMap{
|
r.SetFuncMap(template.FuncMap{
|
||||||
"tf": func(t time.Time) string {
|
"tf": func(t time.Time) string {
|
||||||
@ -54,7 +56,7 @@ func ServeWeb(port uint) {
|
|||||||
}
|
}
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
// 这是从未在线的情况
|
// 这是从未在线的情况
|
||||||
return 1 / float32(b) * 100
|
return 0.00001 / float32(b) * 100
|
||||||
}
|
}
|
||||||
return float32(a) / float32(b) * 100
|
return float32(a) / float32(b) * 100
|
||||||
},
|
},
|
||||||
@ -67,7 +69,7 @@ func ServeWeb(port uint) {
|
|||||||
}
|
}
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
// 这是从未在线的情况
|
// 这是从未在线的情况
|
||||||
return 1 / float32(b) * 100
|
return 0.00001 / float32(b) * 100
|
||||||
}
|
}
|
||||||
return float32(a) / float32(b) * 100
|
return float32(a) / float32(b) * 100
|
||||||
},
|
},
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
"github.com/naiba/nezha/model"
|
"github.com/naiba/nezha/model"
|
||||||
"github.com/naiba/nezha/pkg/mygin"
|
"github.com/naiba/nezha/pkg/mygin"
|
||||||
"github.com/naiba/nezha/service/dao"
|
"github.com/naiba/nezha/service/dao"
|
||||||
"golang.org/x/oauth2"
|
|
||||||
"golang.org/x/oauth2/github"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type guestPage struct {
|
type guestPage struct {
|
||||||
@ -27,31 +25,22 @@ func (gp *guestPage) serve() {
|
|||||||
|
|
||||||
gr.GET("/login", gp.login)
|
gr.GET("/login", gp.login)
|
||||||
|
|
||||||
var endPoint oauth2.Endpoint
|
|
||||||
|
|
||||||
if dao.Conf.Oauth2.Type == model.ConfigTypeGitee {
|
|
||||||
endPoint = oauth2.Endpoint{
|
|
||||||
AuthURL: "https://gitee.com/oauth/authorize",
|
|
||||||
TokenURL: "https://gitee.com/oauth/token",
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
endPoint = github.Endpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
oauth := &oauth2controller{
|
oauth := &oauth2controller{
|
||||||
oauth2Config: &oauth2.Config{
|
|
||||||
ClientID: dao.Conf.Oauth2.ClientID,
|
|
||||||
ClientSecret: dao.Conf.Oauth2.ClientSecret,
|
|
||||||
Scopes: []string{},
|
|
||||||
Endpoint: endPoint,
|
|
||||||
},
|
|
||||||
r: gr,
|
r: gr,
|
||||||
}
|
}
|
||||||
oauth.serve()
|
oauth.serve()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gp *guestPage) login(c *gin.Context) {
|
func (gp *guestPage) login(c *gin.Context) {
|
||||||
|
LoginType := "GitHub"
|
||||||
|
RegistrationLink := "https://github.com/join"
|
||||||
|
if dao.Conf.Oauth2.Type == model.ConfigTypeGitee {
|
||||||
|
LoginType = "Gitee"
|
||||||
|
RegistrationLink = "https://gitee.com/signup"
|
||||||
|
}
|
||||||
c.HTML(http.StatusOK, "dashboard/login", mygin.CommonEnvironment(c, gin.H{
|
c.HTML(http.StatusOK, "dashboard/login", mygin.CommonEnvironment(c, gin.H{
|
||||||
"Title": "登录",
|
"Title": "登录",
|
||||||
|
"LoginType": LoginType,
|
||||||
|
"RegistrationLink": RegistrationLink,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -73,6 +74,7 @@ func (ma *memberAPI) delete(c *gin.Context) {
|
|||||||
case "monitor":
|
case "monitor":
|
||||||
err = dao.DB.Delete(&model.Monitor{}, "id = ?", id).Error
|
err = dao.DB.Delete(&model.Monitor{}, "id = ?", id).Error
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
dao.ServiceSentinelShared.OnMonitorDelete(id)
|
||||||
err = dao.DB.Delete(&model.MonitorHistory{}, "monitor_id = ?", id).Error
|
err = dao.DB.Delete(&model.MonitorHistory{}, "monitor_id = ?", id).Error
|
||||||
}
|
}
|
||||||
case "cron":
|
case "cron":
|
||||||
@ -194,6 +196,8 @@ type monitorForm struct {
|
|||||||
Name string
|
Name string
|
||||||
Target string
|
Target string
|
||||||
Type uint8
|
Type uint8
|
||||||
|
Notify string
|
||||||
|
SkipServersRaw string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
||||||
@ -202,9 +206,11 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
|||||||
err := c.ShouldBindJSON(&mf)
|
err := c.ShouldBindJSON(&mf)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
m.Name = mf.Name
|
m.Name = mf.Name
|
||||||
m.Target = mf.Target
|
m.Target = strings.TrimSpace(mf.Target)
|
||||||
m.Type = mf.Type
|
m.Type = mf.Type
|
||||||
m.ID = mf.ID
|
m.ID = mf.ID
|
||||||
|
m.SkipServersRaw = mf.SkipServersRaw
|
||||||
|
m.Notify = mf.Notify == "on"
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if m.ID == 0 {
|
if m.ID == 0 {
|
||||||
@ -219,6 +225,8 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
|||||||
Message: fmt.Sprintf("请求错误:%s", err),
|
Message: fmt.Sprintf("请求错误:%s", err),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
dao.ServiceSentinelShared.OnMonitorUpdate()
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, model.Response{
|
c.JSON(http.StatusOK, model.Response{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
|
@ -39,11 +39,9 @@ func (mp *memberPage) server(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mp *memberPage) monitor(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{
|
c.HTML(http.StatusOK, "dashboard/monitor", mygin.CommonEnvironment(c, gin.H{
|
||||||
"Title": "服务监控",
|
"Title": "服务监控",
|
||||||
"Monitors": monitors,
|
"Monitors": dao.ServiceSentinelShared.Monitors(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -12,6 +11,7 @@ import (
|
|||||||
"github.com/google/go-github/github"
|
"github.com/google/go-github/github"
|
||||||
GitHubAPI "github.com/google/go-github/github"
|
GitHubAPI "github.com/google/go-github/github"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
GitHubOauth2 "golang.org/x/oauth2/github"
|
||||||
|
|
||||||
"github.com/naiba/nezha/model"
|
"github.com/naiba/nezha/model"
|
||||||
"github.com/naiba/nezha/pkg/mygin"
|
"github.com/naiba/nezha/pkg/mygin"
|
||||||
@ -20,7 +20,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type oauth2controller struct {
|
type oauth2controller struct {
|
||||||
oauth2Config *oauth2.Config
|
|
||||||
r gin.IRoutes
|
r gin.IRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,39 +28,58 @@ func (oa *oauth2controller) serve() {
|
|||||||
oa.r.GET("/oauth2/callback", oa.callback)
|
oa.r.GET("/oauth2/callback", oa.callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oa *oauth2controller) fillRedirectURL(c *gin.Context) {
|
func (oa *oauth2controller) getCommonOauth2Config(c *gin.Context) *oauth2.Config {
|
||||||
|
var endPoint oauth2.Endpoint
|
||||||
|
if dao.Conf.Oauth2.Type == model.ConfigTypeGitee {
|
||||||
|
endPoint = oauth2.Endpoint{
|
||||||
|
AuthURL: "https://gitee.com/oauth/authorize",
|
||||||
|
TokenURL: "https://gitee.com/oauth/token",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
endPoint = GitHubOauth2.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oauth2.Config{
|
||||||
|
ClientID: dao.Conf.Oauth2.ClientID,
|
||||||
|
ClientSecret: dao.Conf.Oauth2.ClientSecret,
|
||||||
|
Scopes: []string{},
|
||||||
|
Endpoint: endPoint,
|
||||||
|
RedirectURL: oa.getRedirectURL(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oa *oauth2controller) getRedirectURL(c *gin.Context) string {
|
||||||
schame := "http://"
|
schame := "http://"
|
||||||
if strings.HasPrefix(c.Request.Referer(), "https://") {
|
if strings.HasPrefix(c.Request.Referer(), "https://") {
|
||||||
schame = "https://"
|
schame = "https://"
|
||||||
}
|
}
|
||||||
oa.oauth2Config.RedirectURL = schame + c.Request.Host + "/oauth2/callback"
|
return schame + c.Request.Host + "/oauth2/callback"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oa *oauth2controller) login(c *gin.Context) {
|
func (oa *oauth2controller) login(c *gin.Context) {
|
||||||
oa.fillRedirectURL(c)
|
|
||||||
state := utils.RandStringBytesMaskImprSrcUnsafe(6)
|
state := utils.RandStringBytesMaskImprSrcUnsafe(6)
|
||||||
dao.Cache.Set(fmt.Sprintf("%s%s", model.CacheKeyOauth2State, c.ClientIP()), state, 0)
|
dao.Cache.Set(fmt.Sprintf("%s%s", model.CacheKeyOauth2State, c.ClientIP()), state, 0)
|
||||||
url := oa.oauth2Config.AuthCodeURL(state, oauth2.AccessTypeOnline)
|
url := oa.getCommonOauth2Config(c).AuthCodeURL(state, oauth2.AccessTypeOnline)
|
||||||
c.Redirect(http.StatusFound, url)
|
c.Redirect(http.StatusFound, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oa *oauth2controller) callback(c *gin.Context) {
|
func (oa *oauth2controller) callback(c *gin.Context) {
|
||||||
oa.fillRedirectURL(c)
|
|
||||||
var err error
|
var err error
|
||||||
// 验证登录跳转时的 State
|
// 验证登录跳转时的 State
|
||||||
state, ok := dao.Cache.Get(fmt.Sprintf("%s%s", model.CacheKeyOauth2State, c.ClientIP()))
|
state, ok := dao.Cache.Get(fmt.Sprintf("%s%s", model.CacheKeyOauth2State, c.ClientIP()))
|
||||||
if !ok || state.(string) != c.Query("state") {
|
if !ok || state.(string) != c.Query("state") {
|
||||||
err = errors.New("非法的登录方式")
|
err = errors.New("非法的登录方式")
|
||||||
}
|
}
|
||||||
|
oauth2Config := oa.getCommonOauth2Config(c)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
var otk *oauth2.Token
|
var otk *oauth2.Token
|
||||||
if err == nil {
|
if err == nil {
|
||||||
otk, err = oa.oauth2Config.Exchange(ctx, c.Query("code"))
|
otk, err = oauth2Config.Exchange(ctx, c.Query("code"))
|
||||||
}
|
}
|
||||||
var client *GitHubAPI.Client
|
var client *GitHubAPI.Client
|
||||||
if err == nil {
|
if err == nil {
|
||||||
oc := oa.oauth2Config.Client(ctx, otk)
|
oc := oauth2Config.Client(ctx, otk)
|
||||||
if dao.Conf.Oauth2.Type == "gitee" {
|
if dao.Conf.Oauth2.Type == model.ConfigTypeGitee {
|
||||||
client, err = GitHubAPI.NewEnterpriseClient("https://gitee.com/api/v5/", "https://gitee.com/api/v5/", oc)
|
client, err = GitHubAPI.NewEnterpriseClient("https://gitee.com/api/v5/", "https://gitee.com/api/v5/", oc)
|
||||||
} else {
|
} else {
|
||||||
client = GitHubAPI.NewClient(oc)
|
client = GitHubAPI.NewClient(oc)
|
||||||
@ -71,7 +89,6 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
gu, _, err = client.Users.Get(ctx, "")
|
gu, _, err = client.Users.Get(ctx, "")
|
||||||
}
|
}
|
||||||
log.Printf("%+v", gu)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
@ -52,6 +52,7 @@ func initSystem() {
|
|||||||
dao.DB.AutoMigrate(model.Server{}, model.User{},
|
dao.DB.AutoMigrate(model.Server{}, model.User{},
|
||||||
model.Notification{}, model.AlertRule{}, model.Monitor{},
|
model.Notification{}, model.AlertRule{}, model.Monitor{},
|
||||||
model.MonitorHistory{}, model.Cron{})
|
model.MonitorHistory{}, model.Cron{})
|
||||||
|
dao.NewServiceSentinel()
|
||||||
|
|
||||||
loadServers() //加载服务器列表
|
loadServers() //加载服务器列表
|
||||||
loadCrons() //加载计划任务
|
loadCrons() //加载计划任务
|
||||||
@ -109,6 +110,6 @@ func loadCrons() {
|
|||||||
func main() {
|
func main() {
|
||||||
go controller.ServeWeb(dao.Conf.HTTPPort)
|
go controller.ServeWeb(dao.Conf.HTTPPort)
|
||||||
go rpc.ServeRPC(dao.Conf.GRPCPort)
|
go rpc.ServeRPC(dao.Conf.GRPCPort)
|
||||||
go rpc.DispatchTask(time.Minute * 3)
|
go rpc.DispatchTask(time.Second * 30)
|
||||||
dao.AlertSentinelStart()
|
dao.AlertSentinelStart()
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"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"
|
"github.com/naiba/nezha/service/dao"
|
||||||
rpcService "github.com/naiba/nezha/service/rpc"
|
rpcService "github.com/naiba/nezha/service/rpc"
|
||||||
@ -28,9 +27,8 @@ func ServeRPC(port uint) {
|
|||||||
func DispatchTask(duration time.Duration) {
|
func DispatchTask(duration time.Duration) {
|
||||||
var index uint64 = 0
|
var index uint64 = 0
|
||||||
for {
|
for {
|
||||||
var tasks []model.Monitor
|
|
||||||
var hasAliveAgent bool
|
var hasAliveAgent bool
|
||||||
dao.DB.Find(&tasks)
|
tasks := dao.ServiceSentinelShared.Monitors()
|
||||||
dao.SortedServerLock.RLock()
|
dao.SortedServerLock.RLock()
|
||||||
startedAt := time.Now()
|
startedAt := time.Now()
|
||||||
for i := 0; i < len(tasks); i++ {
|
for i := 0; i < len(tasks); i++ {
|
||||||
@ -41,7 +39,9 @@ func DispatchTask(duration time.Duration) {
|
|||||||
}
|
}
|
||||||
hasAliveAgent = false
|
hasAliveAgent = false
|
||||||
}
|
}
|
||||||
if dao.SortedServerList[index].TaskStream == nil {
|
// 1. 如果此任务不可使用此服务器请求,跳过这个服务器(有些 IPv6 only 开了 NAT64 的机器请求 IPv4 总会出问题)
|
||||||
|
// 2. 如果服务器不在线,跳过这个服务器
|
||||||
|
if tasks[i].SkipServers[dao.SortedServerList[index].ID] || dao.SortedServerList[index].TaskStream == nil {
|
||||||
i--
|
i--
|
||||||
index++
|
index++
|
||||||
continue
|
continue
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/genkiroid/cert"
|
||||||
"github.com/go-ping/ping"
|
"github.com/go-ping/ping"
|
||||||
"github.com/naiba/nezha/pkg/utils"
|
"github.com/naiba/nezha/pkg/utils"
|
||||||
"github.com/shirou/gopsutil/v3/cpu"
|
"github.com/shirou/gopsutil/v3/cpu"
|
||||||
@ -20,8 +21,8 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
// icmp()
|
// icmp()
|
||||||
// tcpping()
|
// tcpping()
|
||||||
// httpWithSSLInfo()
|
httpWithSSLInfo()
|
||||||
sysinfo()
|
// sysinfo()
|
||||||
// cmdExec()
|
// cmdExec()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ func tcpping() {
|
|||||||
}
|
}
|
||||||
conn.Write([]byte("ping\n"))
|
conn.Write([]byte("ping\n"))
|
||||||
conn.Close()
|
conn.Close()
|
||||||
fmt.Println(time.Now().Sub(start).Microseconds(), float32(time.Now().Sub(start).Microseconds())/1000.0)
|
fmt.Println(time.Since(start).Microseconds(), float32(time.Since(start).Microseconds())/1000.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sysinfo() {
|
func sysinfo() {
|
||||||
@ -53,7 +54,6 @@ func sysinfo() {
|
|||||||
for model, count := range cpuModelCount {
|
for model, count := range cpuModelCount {
|
||||||
cpus = append(cpus, fmt.Sprintf("%s %d %s Core", model, count, cpuType))
|
cpus = append(cpus, fmt.Sprintf("%s %d %s Core", model, count, cpuType))
|
||||||
}
|
}
|
||||||
log.Println(cpus)
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
// 硬盘信息,不使用的原因是会重复统计 Linux、Mac
|
// 硬盘信息,不使用的原因是会重复统计 Linux、Mac
|
||||||
dparts, _ := disk.Partitions(false)
|
dparts, _ := disk.Partitions(false)
|
||||||
@ -73,11 +73,12 @@ func httpWithSSLInfo() {
|
|||||||
httpClient := &http.Client{Transport: transCfg, CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
httpClient := &http.Client{Transport: transCfg, CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
return http.ErrUseLastResponse
|
return http.ErrUseLastResponse
|
||||||
}}
|
}}
|
||||||
resp, err := httpClient.Get("http://mail.nai.ba")
|
url := "https://ops.naibahq.com"
|
||||||
fmt.Println(err, resp.StatusCode)
|
resp, err := httpClient.Get(url)
|
||||||
|
fmt.Println(err, resp)
|
||||||
// SSL 证书信息获取
|
// SSL 证书信息获取
|
||||||
// c := cert.NewCert("expired-ecc-dv.ssl.com")
|
c := cert.NewCert(url[8:])
|
||||||
// fmt.Println(c.Error)
|
fmt.Println(c.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func icmp() {
|
func icmp() {
|
||||||
|
9
go.mod
9
go.mod
@ -7,8 +7,9 @@ require (
|
|||||||
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/genkiroid/cert v0.0.0-20191007122723-897560fbbe50
|
||||||
|
github.com/gin-contrib/pprof v1.3.0
|
||||||
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/go-ping/ping v0.0.0-20210407214646-e4e642a95741
|
||||||
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
|
||||||
@ -17,14 +18,16 @@ require (
|
|||||||
github.com/p14yground/go-github-selfupdate v1.2.3-0.20210119020835-db3523c6834b
|
github.com/p14yground/go-github-selfupdate v1.2.3-0.20210119020835-db3523c6834b
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v3 v3.20.11
|
github.com/shirou/gopsutil/v3 v3.21.3
|
||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/viper v1.7.1
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.6.1
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||||
|
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
|
||||||
|
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 // indirect
|
||||||
google.golang.org/grpc v1.33.1
|
google.golang.org/grpc v1.33.1
|
||||||
google.golang.org/protobuf v1.25.0
|
google.golang.org/protobuf v1.25.0
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
gopkg.in/yaml.v2 v2.2.8
|
||||||
gorm.io/driver/sqlite v1.1.4
|
gorm.io/driver/sqlite v1.1.4
|
||||||
gorm.io/gorm v1.20.8
|
gorm.io/gorm v1.21.8
|
||||||
)
|
)
|
||||||
|
63
go.sum
63
go.sum
@ -63,7 +63,6 @@ github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
|
|||||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -74,15 +73,17 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
|
||||||
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 h1:vLwmYBduhnWWqShoUGbVgDulhcLdanoYtCQxYMzwaqQ=
|
||||||
github.com/genkiroid/cert v0.0.0-20191007122723-897560fbbe50/go.mod h1:Pb7nyGYAfDyE/IkU6AJeRshIFko0wJC9cOqeYzYQffk=
|
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/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=
|
||||||
|
github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0=
|
||||||
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=
|
||||||
|
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
@ -93,8 +94,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-20210407214646-e4e642a95741 h1:b0sLP++Tsle+s57tqg5sUk1/OQsC6yMCciVeqNzOcwU=
|
||||||
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663/go.mod h1:35JbSyV/BYqHwwRA6Zr1uVDm1637YlNOU61wI797NPI=
|
github.com/go-ping/ping v0.0.0-20210407214646-e4e642a95741/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=
|
||||||
@ -106,7 +107,6 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO
|
|||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@ -121,7 +121,6 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
|||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
@ -131,19 +130,16 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
|||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
@ -200,8 +196,9 @@ github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7V
|
|||||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
|
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
|
||||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
|
||||||
|
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||||
@ -282,8 +279,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
|
|||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/shirou/gopsutil/v3 v3.20.11 h1:NeVf1K0cgxsWz+N3671ojRptdgzvp7BXL3KV21R0JnA=
|
github.com/shirou/gopsutil/v3 v3.21.3 h1:wgcdAHZS2H6qy4JFewVTtqfiYxFzCeEJod/mLztdPG8=
|
||||||
github.com/shirou/gopsutil/v3 v3.20.11/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4=
|
github.com/shirou/gopsutil/v3 v3.21.3/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
@ -305,7 +302,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
@ -313,6 +309,10 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s
|
|||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw=
|
github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw=
|
||||||
github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE=
|
github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.4 h1:HT8SVixZd3IzLdfs/xlpq0jeSfTX57g1v6wB1EuzV7M=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek=
|
||||||
|
github.com/tklauser/numcpus v0.2.1 h1:ct88eFm+Q7m2ZfXJdan1xYoXKlmwsfP+k88q05KvlZc=
|
||||||
|
github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
@ -380,7 +380,6 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
@ -395,7 +394,6 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
@ -403,14 +401,13 @@ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/
|
|||||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
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/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/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 h1:0PC75Fz/kyMGhL0e1QnypqK2kQMqKt9csD1GnMJR+Zk=
|
||||||
|
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
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=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -445,34 +442,33 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw=
|
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5 h1:iCaAy5bMeEvwANu3YnJfWwI0kWAGkEa2RXPdweI/ysk=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
|
||||||
|
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@ -521,7 +517,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
|
|||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -543,10 +538,8 @@ google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSr
|
|||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||||
@ -557,7 +550,6 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn
|
|||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
@ -576,7 +568,6 @@ google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfG
|
|||||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
@ -589,7 +580,6 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
|
|||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
@ -610,7 +600,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
|||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@ -624,9 +613,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
|
|||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
@ -635,8 +622,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM=
|
gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM=
|
||||||
gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
|
gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
|
||||||
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||||
gorm.io/gorm v1.20.8 h1:iToaOdZgjNvlc44NFkxfLa3U9q63qwaxt0FdNCiwOMs=
|
gorm.io/gorm v1.21.8 h1:2CEwZSzogdhsKPlJ9OvBKTdlWIpELXb6HbfLfMNhSYI=
|
||||||
gorm.io/gorm v1.20.8/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
gorm.io/gorm v1.21.8/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
12
model/api.go
Normal file
12
model/api.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type ServiceItemResponse struct {
|
||||||
|
Monitor Monitor
|
||||||
|
TotalUp uint64
|
||||||
|
TotalDown uint64
|
||||||
|
CurrentUp uint64
|
||||||
|
CurrentDown uint64
|
||||||
|
Delay *[30]float32
|
||||||
|
Up *[30]int
|
||||||
|
Down *[30]int
|
||||||
|
}
|
@ -1,7 +1,10 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
pb "github.com/naiba/nezha/proto"
|
pb "github.com/naiba/nezha/proto"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -17,6 +20,10 @@ type Monitor struct {
|
|||||||
Name string
|
Name string
|
||||||
Type uint8
|
Type uint8
|
||||||
Target string
|
Target string
|
||||||
|
SkipServersRaw string
|
||||||
|
Notify bool
|
||||||
|
|
||||||
|
SkipServers map[uint64]bool `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Monitor) PB() *pb.Task {
|
func (m *Monitor) PB() *pb.Task {
|
||||||
@ -26,3 +33,15 @@ func (m *Monitor) PB() *pb.Task {
|
|||||||
Data: m.Target,
|
Data: m.Target,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Monitor) AfterFind(tx *gorm.DB) error {
|
||||||
|
var skipServers []uint64
|
||||||
|
if err := json.Unmarshal([]byte(m.SkipServersRaw), &skipServers); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.SkipServers = make(map[uint64]bool)
|
||||||
|
for i := 0; i < len(skipServers); i++ {
|
||||||
|
m.SkipServers[skipServers[i]] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
.nb-container {
|
.nb-container {
|
||||||
padding-top: 75px;
|
padding-top: 75px;
|
||||||
min-height: 100%;
|
min-height: 100vh;
|
||||||
padding-bottom: 55px;
|
padding-bottom: 55px;
|
||||||
margin-bottom: -47px;
|
margin-bottom: -47px;
|
||||||
}
|
}
|
||||||
|
@ -1,273 +1,371 @@
|
|||||||
function readableBytes(bytes) {
|
function readableBytes(bytes) {
|
||||||
var i = Math.floor(Math.log(bytes) / Math.log(1024)),
|
var i = Math.floor(Math.log(bytes) / Math.log(1024)),
|
||||||
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||||
return (bytes / Math.pow(1024, i)).toFixed(0) + ' ' + sizes[i];
|
return (bytes / Math.pow(1024, i)).toFixed(0) + " " + sizes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmBtn = $('.mini.confirm.modal .positive.button')
|
const confirmBtn = $(".mini.confirm.modal .positive.button");
|
||||||
|
|
||||||
function showConfirm(title, content, callFn, extData) {
|
function showConfirm(title, content, callFn, extData) {
|
||||||
const modal = $('.mini.confirm.modal')
|
const modal = $(".mini.confirm.modal");
|
||||||
modal.children('.header').text(title)
|
modal.children(".header").text(title);
|
||||||
modal.children('.content').text(content)
|
modal.children(".content").text(content);
|
||||||
if (confirmBtn.hasClass('loading')) {
|
if (confirmBtn.hasClass("loading")) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
modal.modal({
|
modal
|
||||||
|
.modal({
|
||||||
closable: true,
|
closable: true,
|
||||||
onApprove: function () {
|
onApprove: function () {
|
||||||
confirmBtn.toggleClass('loading')
|
confirmBtn.toggleClass("loading");
|
||||||
callFn(extData)
|
callFn(extData);
|
||||||
return false
|
return false;
|
||||||
}
|
},
|
||||||
}).modal('show')
|
})
|
||||||
|
.modal("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
function showFormModal(modelSelector, formID, URL, getData) {
|
function showFormModal(modelSelector, formID, URL, getData) {
|
||||||
$(modelSelector).modal({
|
$(modelSelector)
|
||||||
|
.modal({
|
||||||
closable: true,
|
closable: true,
|
||||||
onApprove: function () {
|
onApprove: function () {
|
||||||
let success = false
|
let success = false;
|
||||||
const btn = $(modelSelector + ' .positive.button')
|
const btn = $(modelSelector + " .positive.button");
|
||||||
const form = $(modelSelector + ' form')
|
const form = $(modelSelector + " form");
|
||||||
if (btn.hasClass('loading')) {
|
if (btn.hasClass("loading")) {
|
||||||
return success
|
return success;
|
||||||
}
|
}
|
||||||
form.children('.message').remove()
|
form.children(".message").remove();
|
||||||
btn.toggleClass('loading')
|
btn.toggleClass("loading");
|
||||||
const data = getData ? getData() : $(formID).serializeArray().reduce(function (obj, item) {
|
const data = getData
|
||||||
|
? getData()
|
||||||
|
: $(formID)
|
||||||
|
.serializeArray()
|
||||||
|
.reduce(function (obj, item) {
|
||||||
// ID 类的数据
|
// ID 类的数据
|
||||||
if ((item.name.endsWith('_id') ||
|
if (
|
||||||
item.name === 'id' || item.name === 'ID' ||
|
item.name.endsWith("_id") ||
|
||||||
item.name === 'RequestType' || item.name === 'RequestMethod' ||
|
item.name === "id" ||
|
||||||
item.name === 'DisplayIndex' || item.name === 'Type')) {
|
item.name === "ID" ||
|
||||||
|
item.name === "RequestType" ||
|
||||||
|
item.name === "RequestMethod" ||
|
||||||
|
item.name === "DisplayIndex" ||
|
||||||
|
item.name === "Type"
|
||||||
|
) {
|
||||||
obj[item.name] = parseInt(item.value);
|
obj[item.name] = parseInt(item.value);
|
||||||
} else {
|
} else {
|
||||||
obj[item.name] = item.value;
|
obj[item.name] = item.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.name == 'ServersRaw') {
|
if (item.name.endsWith("ServersRaw")) {
|
||||||
if (item.value.length > 2) {
|
if (item.value.length > 2) {
|
||||||
obj[item.name] = '[' + item.value.substr(3, item.value.length - 1) + ']'
|
obj[item.name] = JSON.stringify(
|
||||||
|
[...item.value.matchAll(/\d+/gm)].map((k) =>
|
||||||
|
parseInt(k[0])
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
$.post(URL, JSON.stringify(data)).done(function (resp) {
|
$.post(URL, JSON.stringify(data))
|
||||||
|
.done(function (resp) {
|
||||||
if (resp.code == 200) {
|
if (resp.code == 200) {
|
||||||
if (resp.message) {
|
if (resp.message) {
|
||||||
$.suiAlert({
|
$.suiAlert({
|
||||||
title: '操作成功',
|
title: "操作成功",
|
||||||
type: 'success',
|
type: "success",
|
||||||
description: resp.message,
|
description: resp.message,
|
||||||
time: '3',
|
time: "3",
|
||||||
position: 'top-center',
|
position: "top-center",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
window.location.reload()
|
window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
form.append(`<div class="ui negative message"><div class="header">操作失败</div><p>` + resp.message + `</p></div>`)
|
form.append(
|
||||||
|
`<div class="ui negative message"><div class="header">操作失败</div><p>` +
|
||||||
|
resp.message +
|
||||||
|
`</p></div>`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}).fail(function (err) {
|
})
|
||||||
form.append(`<div class="ui negative message"><div class="header">网络错误</div><p>` + err.responseText + `</p></div>`)
|
.fail(function (err) {
|
||||||
}).always(function () {
|
form.append(
|
||||||
btn.toggleClass('loading')
|
`<div class="ui negative message"><div class="header">网络错误</div><p>` +
|
||||||
|
err.responseText +
|
||||||
|
`</p></div>`
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.always(function () {
|
||||||
|
btn.toggleClass("loading");
|
||||||
});
|
});
|
||||||
return success
|
return success;
|
||||||
}
|
},
|
||||||
}).modal('show')
|
})
|
||||||
|
.modal("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
function addOrEditAlertRule(rule) {
|
function addOrEditAlertRule(rule) {
|
||||||
const modal = $('.rule.modal')
|
const modal = $(".rule.modal");
|
||||||
modal.children('.header').text((rule ? '修改' : '添加') + '报警规则')
|
modal.children(".header").text((rule ? "修改" : "添加") + "报警规则");
|
||||||
modal.find('.positive.button').html(rule ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>')
|
modal
|
||||||
modal.find('input[name=ID]').val(rule ? rule.ID : null)
|
.find(".positive.button")
|
||||||
modal.find('input[name=Name]').val(rule ? rule.Name : null)
|
.html(
|
||||||
modal.find('textarea[name=RulesRaw]').val(rule ? rule.RulesRaw : null)
|
rule ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>'
|
||||||
|
);
|
||||||
|
modal.find("input[name=ID]").val(rule ? rule.ID : null);
|
||||||
|
modal.find("input[name=Name]").val(rule ? rule.Name : null);
|
||||||
|
modal.find("textarea[name=RulesRaw]").val(rule ? rule.RulesRaw : null);
|
||||||
if (rule && rule.Enable) {
|
if (rule && rule.Enable) {
|
||||||
modal.find('.ui.rule-enable.checkbox').checkbox('set checked')
|
modal.find(".ui.rule-enable.checkbox").checkbox("set checked");
|
||||||
} else {
|
} else {
|
||||||
modal.find('.ui.rule-enable.checkbox').checkbox('set unchecked')
|
modal.find(".ui.rule-enable.checkbox").checkbox("set unchecked");
|
||||||
}
|
}
|
||||||
showFormModal('.rule.modal', '#ruleForm', '/api/alert-rule')
|
showFormModal(".rule.modal", "#ruleForm", "/api/alert-rule");
|
||||||
}
|
}
|
||||||
|
|
||||||
function addOrEditNotification(notification) {
|
function addOrEditNotification(notification) {
|
||||||
const modal = $('.notification.modal')
|
const modal = $(".notification.modal");
|
||||||
modal.children('.header').text((notification ? '修改' : '添加') + '通知方式')
|
modal.children(".header").text((notification ? "修改" : "添加") + "通知方式");
|
||||||
modal.find('.positive.button').html(notification ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>')
|
modal
|
||||||
modal.find('input[name=ID]').val(notification ? notification.ID : null)
|
.find(".positive.button")
|
||||||
modal.find('input[name=Name]').val(notification ? notification.Name : null)
|
.html(
|
||||||
modal.find('input[name=URL]').val(notification ? notification.URL : null)
|
notification
|
||||||
modal.find('textarea[name=RequestBody]').val(notification ? notification.RequestBody : null)
|
? '修改<i class="edit icon"></i>'
|
||||||
modal.find('select[name=RequestMethod]').val(notification ? notification.RequestMethod : 1)
|
: '添加<i class="add icon"></i>'
|
||||||
modal.find('select[name=RequestType]').val(notification ? notification.RequestType : 1)
|
);
|
||||||
|
modal.find("input[name=ID]").val(notification ? notification.ID : null);
|
||||||
|
modal.find("input[name=Name]").val(notification ? notification.Name : null);
|
||||||
|
modal.find("input[name=URL]").val(notification ? notification.URL : null);
|
||||||
|
modal
|
||||||
|
.find("textarea[name=RequestBody]")
|
||||||
|
.val(notification ? notification.RequestBody : null);
|
||||||
|
modal
|
||||||
|
.find("select[name=RequestMethod]")
|
||||||
|
.val(notification ? notification.RequestMethod : 1);
|
||||||
|
modal
|
||||||
|
.find("select[name=RequestType]")
|
||||||
|
.val(notification ? notification.RequestType : 1);
|
||||||
if (notification && notification.VerifySSL) {
|
if (notification && notification.VerifySSL) {
|
||||||
modal.find('.ui.nf-ssl.checkbox').checkbox('set checked')
|
modal.find(".ui.nf-ssl.checkbox").checkbox("set checked");
|
||||||
} else {
|
} else {
|
||||||
modal.find('.ui.nf-ssl.checkbox').checkbox('set unchecked')
|
modal.find(".ui.nf-ssl.checkbox").checkbox("set unchecked");
|
||||||
}
|
}
|
||||||
showFormModal('.notification.modal', '#notificationForm', '/api/notification')
|
showFormModal(
|
||||||
|
".notification.modal",
|
||||||
|
"#notificationForm",
|
||||||
|
"/api/notification"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addOrEditServer(server) {
|
function addOrEditServer(server) {
|
||||||
const modal = $('.server.modal')
|
const modal = $(".server.modal");
|
||||||
modal.children('.header').text((server ? '修改' : '添加') + '服务器')
|
modal.children(".header").text((server ? "修改" : "添加") + "服务器");
|
||||||
modal.find('.positive.button').html(server ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>')
|
modal
|
||||||
modal.find('input[name=id]').val(server ? server.ID : null)
|
.find(".positive.button")
|
||||||
modal.find('input[name=name]').val(server ? server.Name : null)
|
.html(
|
||||||
modal.find('input[name=Tag]').val(server ? server.Tag : null)
|
server ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>'
|
||||||
modal.find('input[name=DisplayIndex]').val(server ? server.DisplayIndex : null)
|
);
|
||||||
modal.find('textarea[name=Note]').val(server ? server.Note : null)
|
modal.find("input[name=id]").val(server ? server.ID : null);
|
||||||
|
modal.find("input[name=name]").val(server ? server.Name : null);
|
||||||
|
modal.find("input[name=Tag]").val(server ? server.Tag : null);
|
||||||
|
modal
|
||||||
|
.find("input[name=DisplayIndex]")
|
||||||
|
.val(server ? server.DisplayIndex : null);
|
||||||
|
modal.find("textarea[name=Note]").val(server ? server.Note : null);
|
||||||
if (server) {
|
if (server) {
|
||||||
modal.find('.secret.field').attr('style', '')
|
modal.find(".secret.field").attr("style", "");
|
||||||
modal.find('input[name=secret]').val(server.Secret)
|
modal.find("input[name=secret]").val(server.Secret);
|
||||||
} else {
|
} else {
|
||||||
modal.find('.secret.field').attr('style', 'display:none')
|
modal.find(".secret.field").attr("style", "display:none");
|
||||||
modal.find('input[name=secret]').val('')
|
modal.find("input[name=secret]").val("");
|
||||||
}
|
}
|
||||||
showFormModal('.server.modal', '#serverForm', '/api/server')
|
showFormModal(".server.modal", "#serverForm", "/api/server");
|
||||||
}
|
}
|
||||||
|
|
||||||
function addOrEditMonitor(monitor) {
|
function addOrEditMonitor(monitor) {
|
||||||
const modal = $('.monitor.modal')
|
const modal = $(".monitor.modal");
|
||||||
modal.children('.header').text((monitor ? '修改' : '添加') + '监控')
|
modal.children(".header").text((monitor ? "修改" : "添加") + "监控");
|
||||||
modal.find('.positive.button').html(monitor ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>')
|
modal
|
||||||
modal.find('input[name=ID]').val(monitor ? monitor.ID : null)
|
.find(".positive.button")
|
||||||
modal.find('input[name=Name]').val(monitor ? monitor.Name : null)
|
.html(
|
||||||
modal.find('input[name=Target]').val(monitor ? monitor.Target : null)
|
monitor ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>'
|
||||||
modal.find('select[name=Type]').val(monitor ? monitor.Type : 1)
|
);
|
||||||
showFormModal('.monitor.modal', '#monitorForm', '/api/monitor')
|
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);
|
||||||
|
if (monitor && monitor.Notify) {
|
||||||
|
modal.find(".ui.nb-notify.checkbox").checkbox("set checked");
|
||||||
|
} else {
|
||||||
|
modal.find(".ui.nb-notify.checkbox").checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
var servers;
|
||||||
|
if (monitor) {
|
||||||
|
servers = monitor.SkipServersRaw;
|
||||||
|
const serverList = JSON.parse(servers || "[]");
|
||||||
|
const node = modal.find("i.dropdown.icon");
|
||||||
|
for (let i = 0; i < serverList.length; i++) {
|
||||||
|
node.after(
|
||||||
|
'<a class="ui label transition visible" data-value="' +
|
||||||
|
serverList[i] +
|
||||||
|
'" style="display: inline-block !important;">ID:' +
|
||||||
|
serverList[i] +
|
||||||
|
'<i class="delete icon"></i></a>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modal
|
||||||
|
.find("input[name=SkipServersRaw]")
|
||||||
|
.val(monitor ? "[]," + servers.substr(1, servers.length - 2) : "[]");
|
||||||
|
showFormModal(".monitor.modal", "#monitorForm", "/api/monitor");
|
||||||
}
|
}
|
||||||
|
|
||||||
function addOrEditCron(cron) {
|
function addOrEditCron(cron) {
|
||||||
const modal = $('.cron.modal')
|
const modal = $(".cron.modal");
|
||||||
modal.children('.header').text((cron ? '修改' : '添加') + '计划任务')
|
modal.children(".header").text((cron ? "修改" : "添加") + "计划任务");
|
||||||
modal.find('.positive.button').html(cron ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>')
|
modal
|
||||||
modal.find('input[name=ID]').val(cron ? cron.ID : null)
|
.find(".positive.button")
|
||||||
modal.find('input[name=Name]').val(cron ? cron.Name : null)
|
.html(
|
||||||
modal.find('input[name=Scheduler]').val(cron ? cron.Scheduler : null)
|
cron ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>'
|
||||||
modal.find('a.ui.label.visible').each((i, el) => {
|
);
|
||||||
el.remove()
|
modal.find("input[name=ID]").val(cron ? cron.ID : null);
|
||||||
})
|
modal.find("input[name=Name]").val(cron ? cron.Name : null);
|
||||||
var servers
|
modal.find("input[name=Scheduler]").val(cron ? cron.Scheduler : null);
|
||||||
|
modal.find("a.ui.label.visible").each((i, el) => {
|
||||||
|
el.remove();
|
||||||
|
});
|
||||||
|
var servers;
|
||||||
if (cron) {
|
if (cron) {
|
||||||
servers = cron.ServersRaw
|
servers = cron.ServersRaw;
|
||||||
serverList = JSON.parse(servers)
|
const serverList = JSON.parse(servers || "[]");
|
||||||
const node = modal.find('i.dropdown.icon')
|
const node = modal.find("i.dropdown.icon");
|
||||||
for (let i = 0; i < serverList.length; i++) {
|
for (let i = 0; i < serverList.length; i++) {
|
||||||
node.after('<a class="ui label transition visible" data-value="' + serverList[i] + '" style="display: inline-block !important;">ID:' + serverList[i] + '<i class="delete icon"></i></a>')
|
node.after(
|
||||||
|
'<a class="ui label transition visible" data-value="' +
|
||||||
|
serverList[i] +
|
||||||
|
'" style="display: inline-block !important;">ID:' +
|
||||||
|
serverList[i] +
|
||||||
|
'<i class="delete icon"></i></a>'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modal.find('input[name=ServersRaw]').val(cron ? '[],' + servers.substr(1, servers.length - 2) : '[]')
|
modal
|
||||||
modal.find('textarea[name=Command]').val(cron ? cron.Command : null)
|
.find("input[name=ServersRaw]")
|
||||||
|
.val(cron ? "[]," + servers.substr(1, servers.length - 2) : "[]");
|
||||||
|
modal.find("textarea[name=Command]").val(cron ? cron.Command : null);
|
||||||
if (cron && cron.PushSuccessful) {
|
if (cron && cron.PushSuccessful) {
|
||||||
modal.find('.ui.push-successful.checkbox').checkbox('set checked')
|
modal.find(".ui.push-successful.checkbox").checkbox("set checked");
|
||||||
} else {
|
} else {
|
||||||
modal.find('.ui.push-successful.checkbox').checkbox('set unchecked')
|
modal.find(".ui.push-successful.checkbox").checkbox("set unchecked");
|
||||||
}
|
}
|
||||||
showFormModal('.cron.modal', '#cronForm', '/api/cron')
|
showFormModal(".cron.modal", "#cronForm", "/api/cron");
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteRequest(api) {
|
function deleteRequest(api) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: api,
|
url: api,
|
||||||
type: 'DELETE',
|
type: "DELETE",
|
||||||
}).done(resp => {
|
})
|
||||||
|
.done((resp) => {
|
||||||
if (resp.code == 200) {
|
if (resp.code == 200) {
|
||||||
if (resp.message) {
|
if (resp.message) {
|
||||||
alert(resp.message)
|
alert(resp.message);
|
||||||
} else {
|
} else {
|
||||||
alert('删除成功')
|
alert("删除成功");
|
||||||
}
|
}
|
||||||
window.location.reload()
|
window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert('删除失败 ' + resp.code + ':' + resp.message)
|
alert("删除失败 " + resp.code + ":" + resp.message);
|
||||||
confirmBtn.toggleClass('loading')
|
confirmBtn.toggleClass("loading");
|
||||||
}
|
}
|
||||||
}).fail(err => {
|
})
|
||||||
alert('网络错误:' + err.responseText)
|
.fail((err) => {
|
||||||
|
alert("网络错误:" + err.responseText);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function manualTrigger(btn, cronId) {
|
function manualTrigger(btn, cronId) {
|
||||||
$(btn).toggleClass('loading')
|
$(btn).toggleClass("loading");
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/api/cron/' + cronId + '/manual',
|
url: "/api/cron/" + cronId + "/manual",
|
||||||
type: 'GET',
|
type: "GET",
|
||||||
}).done(resp => {
|
})
|
||||||
$(btn).toggleClass('loading')
|
.done((resp) => {
|
||||||
|
$(btn).toggleClass("loading");
|
||||||
if (resp.code == 200) {
|
if (resp.code == 200) {
|
||||||
$.suiAlert({
|
$.suiAlert({
|
||||||
title: '触发成功,等待执行结果',
|
title: "触发成功,等待执行结果",
|
||||||
type: 'success',
|
type: "success",
|
||||||
description: resp.message,
|
description: resp.message,
|
||||||
time: '3',
|
time: "3",
|
||||||
position: 'top-center',
|
position: "top-center",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$.suiAlert({
|
$.suiAlert({
|
||||||
title: '触发失败 ',
|
title: "触发失败 ",
|
||||||
type: 'error',
|
type: "error",
|
||||||
description: resp.code + ':' + resp.message,
|
description: resp.code + ":" + resp.message,
|
||||||
time: '3',
|
time: "3",
|
||||||
position: 'top-center',
|
position: "top-center",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).fail(err => {
|
})
|
||||||
$(btn).toggleClass('loading')
|
.fail((err) => {
|
||||||
|
$(btn).toggleClass("loading");
|
||||||
$.suiAlert({
|
$.suiAlert({
|
||||||
title: '触发失败 ',
|
title: "触发失败 ",
|
||||||
type: 'error',
|
type: "error",
|
||||||
description: '网络错误:' + err.responseText,
|
description: "网络错误:" + err.responseText,
|
||||||
time: '3',
|
time: "3",
|
||||||
position: 'top-center',
|
position: "top-center",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function logout(id) {
|
function logout(id) {
|
||||||
$.post('/api/logout', JSON.stringify({ id: id })).done(function (resp) {
|
$.post("/api/logout", JSON.stringify({ id: id }))
|
||||||
|
.done(function (resp) {
|
||||||
if (resp.code == 200) {
|
if (resp.code == 200) {
|
||||||
$.suiAlert({
|
$.suiAlert({
|
||||||
title: '注销成功',
|
title: "注销成功",
|
||||||
type: 'success',
|
type: "success",
|
||||||
description: '如需继续访问请使用 GitHub 再次登录',
|
description: "如需继续访问请使用 GitHub 再次登录",
|
||||||
time: '3',
|
time: "3",
|
||||||
position: 'top-center',
|
position: "top-center",
|
||||||
});
|
});
|
||||||
window.location.reload()
|
window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
$.suiAlert({
|
$.suiAlert({
|
||||||
title: '注销失败',
|
title: "注销失败",
|
||||||
description: resp.code + ':' + resp.message,
|
description: resp.code + ":" + resp.message,
|
||||||
type: 'error',
|
type: "error",
|
||||||
time: '3',
|
time: "3",
|
||||||
position: 'top-center',
|
position: "top-center",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).fail(function (err) {
|
|
||||||
$.suiAlert({
|
|
||||||
title: '网络错误',
|
|
||||||
description: err.responseText,
|
|
||||||
type: 'error',
|
|
||||||
time: '3',
|
|
||||||
position: 'top-center',
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
|
.fail(function (err) {
|
||||||
|
$.suiAlert({
|
||||||
|
title: "网络错误",
|
||||||
|
description: err.responseText,
|
||||||
|
type: "error",
|
||||||
|
time: "3",
|
||||||
|
position: "top-center",
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
try {
|
try {
|
||||||
$('.ui.servers.search.dropdown').dropdown({
|
$(".ui.servers.search.dropdown").dropdown({
|
||||||
clearable: true,
|
clearable: true,
|
||||||
apiSettings: {
|
apiSettings: {
|
||||||
url: '/api/search-server?word={query}',
|
url: "/api/search-server?word={query}",
|
||||||
cache: false,
|
cache: false,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
}
|
});
|
||||||
})
|
|
||||||
|
2
resource/template/common/footer.html
vendored
2
resource/template/common/footer.html
vendored
@ -9,7 +9,7 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.1/dist/semantic.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.1/dist/semantic.min.js"></script>
|
||||||
<script src="/static/semantic-ui-alerts.min.js"></script>
|
<script src="/static/semantic-ui-alerts.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js"></script>
|
||||||
<script src="/static/main.js?v202101240939"></script>
|
<script src="/static/main.js?v202104232106"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
2
resource/template/common/header.html
vendored
2
resource/template/common/header.html
vendored
@ -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?v202102051040">
|
<link rel="stylesheet" type="text/css" href="/static/main.css?v202104192145">
|
||||||
<link rel="shortcut icon" type="image/png" href="/static/logo.png?v20210320" />
|
<link rel="shortcut icon" type="image/png" href="/static/logo.png?v20210320" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
2
resource/template/common/menu.html
vendored
2
resource/template/common/menu.html
vendored
@ -14,7 +14,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a class='item{{if eq .MatchedPath "/"}} active{{end}}' href="/"><i class="home icon"></i>首页</a>
|
<a class='item{{if eq .MatchedPath "/"}} active{{end}}' href="/"><i class="home icon"></i>首页</a>
|
||||||
<a class='item{{if eq .MatchedPath "/service"}} active{{end}}' href="/service"><i class="rss icon"></i>服务状态</a>
|
<a class='item{{if eq .MatchedPath "/service"}} active{{end}}' href="/service"><i class="rss icon"></i>服务</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="right menu">
|
<div class="right menu">
|
||||||
<div class="item">
|
<div class="item">
|
||||||
|
35
resource/template/component/monitor.html
vendored
35
resource/template/component/monitor.html
vendored
@ -3,14 +3,18 @@
|
|||||||
<div class="header">添加监控</div>
|
<div class="header">添加监控</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<form id="monitorForm" class="ui form">
|
<form id="monitorForm" class="ui form">
|
||||||
<input type="hidden" name="ID">
|
<input type="hidden" name="ID" />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>名称</label>
|
<label>名称</label>
|
||||||
<input type="text" name="Name" placeholder="博客">
|
<input type="text" name="Name" placeholder="博客" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>目标</label>
|
<label>目标</label>
|
||||||
<input type="text" name="Target" placeholder="HTTP(https://t.tt)|Ping(t.tt)|TCP(t.tt:80)">
|
<input
|
||||||
|
type="text"
|
||||||
|
name="Target"
|
||||||
|
placeholder="HTTP(https://t.tt)|Ping(t.tt)|TCP(t.tt:80)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>类型</label>
|
<label>类型</label>
|
||||||
@ -20,18 +24,35 @@
|
|||||||
<option value="3">TCP-Ping</option>
|
<option value="3">TCP-Ping</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>不通过下列服务器请求</label>
|
||||||
|
<div class="ui fluid multiple servers search selection dropdown">
|
||||||
|
<input type="hidden" name="SkipServersRaw" />
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
<div class="default text">输入ID/名称以搜索</div>
|
||||||
|
<div class="menu"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui nb-notify checkbox">
|
||||||
|
<input name="Notify" type="checkbox" tabindex="0" class="hidden" />
|
||||||
|
<label>启用故障通知</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="ui warning message">
|
<div class="ui warning message">
|
||||||
<p>
|
<p>
|
||||||
类型为 <b>HTTP-GET</b> 时输入URL(带 http/https, HTTPS 协议的会顺带监控SSL证书);<br>
|
类型为 <b>HTTP-GET</b> 时输入URL(带 http/https, HTTPS
|
||||||
类型为 <b>ICMP-Ping</b> 时输入主机名/IP,不带端口;<br>
|
协议的会顺带监控SSL证书);<br />
|
||||||
|
类型为 <b>ICMP-Ping</b> 时输入主机名/IP,不带端口;<br />
|
||||||
类型为 <b>TCP-Ping</b> 时输入主机名/IP + 端口号:example.com:22
|
类型为 <b>TCP-Ping</b> 时输入主机名/IP + 端口号:example.com:22
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class=" actions">
|
<div class="actions">
|
||||||
<div class="ui negative button">取消</div>
|
<div class="ui negative button">取消</div>
|
||||||
<button class="ui positive right labeled icon button">确认<i class="checkmark icon"></i>
|
<button class="ui positive right labeled icon button">
|
||||||
|
确认<i class="checkmark icon"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
<h2 class="ui teal image header">
|
<h2 class="ui teal image header">
|
||||||
<img src="static/logo.png?v20210320" class="image">
|
<img src="static/logo.png?v20210320" class="image">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
使用 GitHub 账号登录
|
使用 {{.LoginType}} 账号登录
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
<a href="/oauth2/login" class="ui fluid large teal submit button">登录</a>
|
<a href="/oauth2/login" class="ui fluid large teal submit button">登录</a>
|
||||||
<div class="ui message">
|
<div class="ui message">
|
||||||
没有账号? <a href="https://github.com/join" target="_blank">注册</a>
|
没有账号? <a href="{{.RegistrationLink}}" target="_blank">注册</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
{{define "dashboard/monitor"}}
|
{{define "dashboard/monitor"}} {{template "common/header" .}} {{template
|
||||||
{{template "common/header" .}}
|
"common/menu" .}}
|
||||||
{{template "common/menu" .}}
|
|
||||||
<div class="nb-container">
|
<div class="nb-container">
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
<div class="right floated right aligned twelve wide column">
|
<div class="right floated right aligned twelve wide column">
|
||||||
<button class="ui right labeled positive icon button" onclick="addOrEditMonitor()"><i
|
<button
|
||||||
class="add icon"></i> 添加监控
|
class="ui right labeled positive icon button"
|
||||||
|
onclick="addOrEditMonitor()"
|
||||||
|
>
|
||||||
|
<i class="add icon"></i> 添加监控
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -16,7 +18,9 @@
|
|||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>名称</th>
|
<th>名称</th>
|
||||||
<th>目标</th>
|
<th>目标</th>
|
||||||
|
<th>跳过的服务器</th>
|
||||||
<th>类型</th>
|
<th>类型</th>
|
||||||
|
<th>通知</th>
|
||||||
<th>管理</th>
|
<th>管理</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -26,21 +30,24 @@
|
|||||||
<td>{{$monitor.ID}}</td>
|
<td>{{$monitor.ID}}</td>
|
||||||
<td>{{$monitor.Name}}</td>
|
<td>{{$monitor.Name}}</td>
|
||||||
<td>{{$monitor.Target}}</td>
|
<td>{{$monitor.Target}}</td>
|
||||||
|
<td>{{$monitor.SkipServersRaw}}</td>
|
||||||
<td>
|
<td>
|
||||||
{{if eq $monitor.Type 1}}HTTP(S)/SSL证书
|
{{if eq $monitor.Type 1}}HTTP(S)/SSL证书 {{else if eq $monitor.Type
|
||||||
{{else if eq $monitor.Type 2}}
|
2}} ICMP Ping {{else}} TCP 端口 {{end}}
|
||||||
ICMP Ping
|
|
||||||
{{else}}
|
|
||||||
TCP 端口
|
|
||||||
{{end}}
|
|
||||||
</td>
|
</td>
|
||||||
|
<td>{{$monitor.Notify}}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui mini icon buttons">
|
<div class="ui mini icon buttons">
|
||||||
<button class="ui button" onclick="addOrEditMonitor({{$monitor}})">
|
<button
|
||||||
|
class="ui button"
|
||||||
|
onclick="addOrEditMonitor({{$monitor}})"
|
||||||
|
>
|
||||||
<i class="edit icon"></i>
|
<i class="edit icon"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="ui button"
|
<button
|
||||||
onclick="showConfirm('删除监控','确认删除此监控?',deleteRequest,'/api/monitor/'+{{$monitor.ID}})">
|
class="ui button"
|
||||||
|
onclick="showConfirm('删除监控','确认删除此监控?',deleteRequest,'/api/monitor/'+{{$monitor.ID}})"
|
||||||
|
>
|
||||||
<i class="trash alternate outline icon"></i>
|
<i class="trash alternate outline icon"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -51,6 +58,8 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{template "component/monitor"}}
|
{{template "component/monitor"}} {{template "common/footer" .}}
|
||||||
{{template "common/footer" .}}
|
<script>
|
||||||
|
$(".checkbox").checkbox();
|
||||||
|
</script>
|
||||||
{{end}}
|
{{end}}
|
@ -8,11 +8,19 @@
|
|||||||
<label>站点标题</label>
|
<label>站点标题</label>
|
||||||
<input type="text" name="Title" placeholder="哪吒监控" value="{{.Conf.Site.Brand}}">
|
<input type="text" name="Title" placeholder="哪吒监控" value="{{.Conf.Site.Brand}}">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>登录类型</label>
|
||||||
|
<select name="Oauth2Type">
|
||||||
|
<option value="github"{{if eq .Conf.Oauth2.Type "github"}} selected="selected"{{end}}>GitHub</option>
|
||||||
|
<option value="gitee"{{if eq .Conf.Oauth2.Type "gitee"}} selected="selected"{{end}}>Gitee</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>管理员列表</label>
|
<label>管理员列表</label>
|
||||||
<input type="text" name="Admin" placeholder="1010,2020" value="{{.Conf.Oauth2.Admin}}">
|
<input type="text" name="Admin" placeholder="1010,2020" value="{{.Conf.Oauth2.Admin}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
<label>前台主题</label>
|
||||||
<select name="Theme">
|
<select name="Theme">
|
||||||
<option value="default"{{if eq .Conf.Site.Theme "default"}} selected="selected"{{end}}>默认主题</option>
|
<option value="default"{{if eq .Conf.Site.Theme "default"}} selected="selected"{{end}}>默认主题</option>
|
||||||
<option value="daynight"{{if eq .Conf.Site.Theme "daynight"}} selected="selected"{{end}}>JackieSung DayNight</option>
|
<option value="daynight"{{if eq .Conf.Site.Theme "daynight"}} selected="selected"{{end}}>JackieSung DayNight</option>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
debug: true
|
debug: false
|
||||||
httpport: 80
|
httpport: 80
|
||||||
oauth2:
|
oauth2:
|
||||||
type: "nz_oauth2_type" #Oauth2 登录接入类型,gitee/github
|
type: "nz_oauth2_type" #Oauth2 登录接入类型,gitee/github
|
||||||
|
@ -2,7 +2,7 @@ version: "3.3"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
dashboard:
|
dashboard:
|
||||||
image: image_url
|
image: nz_image_url
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/dashboard/data
|
- ./data:/dashboard/data
|
||||||
|
@ -11,7 +11,7 @@ NZ_BASE_PATH="/opt/nezha"
|
|||||||
NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard"
|
NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard"
|
||||||
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
||||||
NZ_AGENT_SERVICE="/etc/systemd/system/nezha-agent.service"
|
NZ_AGENT_SERVICE="/etc/systemd/system/nezha-agent.service"
|
||||||
NZ_VERSION="v0.4.10"
|
NZ_VERSION="v0.5.0"
|
||||||
|
|
||||||
red='\033[0;31m'
|
red='\033[0;31m'
|
||||||
green='\033[0;32m'
|
green='\033[0;32m'
|
||||||
@ -276,7 +276,7 @@ modify_dashboard_config() {
|
|||||||
sed -i "s/nz_site_title/${nz_site_title}/" ${NZ_DASHBOARD_PATH}/data/config.yaml
|
sed -i "s/nz_site_title/${nz_site_title}/" ${NZ_DASHBOARD_PATH}/data/config.yaml
|
||||||
sed -i "s/nz_site_port/${nz_site_port}/" ${NZ_DASHBOARD_PATH}/docker-compose.yaml
|
sed -i "s/nz_site_port/${nz_site_port}/" ${NZ_DASHBOARD_PATH}/docker-compose.yaml
|
||||||
sed -i "s/nz_grpc_port/${nz_grpc_port}/" ${NZ_DASHBOARD_PATH}/docker-compose.yaml
|
sed -i "s/nz_grpc_port/${nz_grpc_port}/" ${NZ_DASHBOARD_PATH}/docker-compose.yaml
|
||||||
sed -i "s/image_url/${Docker_IMG}/" ${NZ_DASHBOARD_PATH}/docker-compose.yaml
|
sed -i "s/nz_image_url/${Docker_IMG}/" ${NZ_DASHBOARD_PATH}/docker-compose.yaml
|
||||||
|
|
||||||
echo -e "面板配置 ${green}修改成功,请稍等重启生效${plain}"
|
echo -e "面板配置 ${green}修改成功,请稍等重启生效${plain}"
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ Type=simple
|
|||||||
User=root
|
User=root
|
||||||
Group=root
|
Group=root
|
||||||
WorkingDirectory=/opt/nezha/agent/
|
WorkingDirectory=/opt/nezha/agent/
|
||||||
ExecStart=/opt/nezha/agent/nezha-agent -d -s nz_grpc_host:nz_grpc_port -p nz_client_secret
|
ExecStart=/opt/nezha/agent/nezha-agent -s nz_grpc_host:nz_grpc_port -p nz_client_secret
|
||||||
Restart=always
|
Restart=always
|
||||||
#Environment=DEBUG=true
|
#Environment=DEBUG=true
|
||||||
|
|
||||||
|
@ -43,7 +43,9 @@ func AlertSentinelStart() {
|
|||||||
checkStatus()
|
checkStatus()
|
||||||
checkCount++
|
checkCount++
|
||||||
if lastPrint.Before(startedAt.Add(-1 * time.Hour)) {
|
if lastPrint.Before(startedAt.Add(-1 * time.Hour)) {
|
||||||
|
if Conf.Debug {
|
||||||
log.Println("报警规则检测每小时", checkCount, "次", startedAt, time.Now())
|
log.Println("报警规则检测每小时", checkCount, "次", startedAt, time.Now())
|
||||||
|
}
|
||||||
checkCount = 0
|
checkCount = 0
|
||||||
lastPrint = startedAt
|
lastPrint = startedAt
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
pb "github.com/naiba/nezha/proto"
|
pb "github.com/naiba/nezha/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "v0.4.15" // !!记得修改 README 重的 badge 版本!!
|
var Version = "v0.7.1" // !!记得修改 README 中的 badge 版本!!
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SnapshotDelay = 3
|
SnapshotDelay = 3
|
||||||
@ -46,7 +46,7 @@ func ReSortServer() {
|
|||||||
|
|
||||||
sort.SliceStable(SortedServerList, func(i, j int) bool {
|
sort.SliceStable(SortedServerList, func(i, j int) bool {
|
||||||
if SortedServerList[i].DisplayIndex == SortedServerList[j].DisplayIndex {
|
if SortedServerList[i].DisplayIndex == SortedServerList[j].DisplayIndex {
|
||||||
return SortedServerList[i].ID < SortedServerList[i].ID
|
return SortedServerList[i].ID < SortedServerList[j].ID
|
||||||
}
|
}
|
||||||
return SortedServerList[i].DisplayIndex > SortedServerList[j].DisplayIndex
|
return SortedServerList[i].DisplayIndex > SortedServerList[j].DisplayIndex
|
||||||
})
|
})
|
||||||
|
333
service/dao/servicesentinel.go
Normal file
333
service/dao/servicesentinel.go
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
package dao
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/naiba/nezha/model"
|
||||||
|
pb "github.com/naiba/nezha/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const _CurrentStatusSize = 30 // 统计 5 分钟内的数据为当前状态
|
||||||
|
|
||||||
|
var ServiceSentinelShared *ServiceSentinel
|
||||||
|
|
||||||
|
type ReportData struct {
|
||||||
|
Data *pb.TaskResult
|
||||||
|
Reporter uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type _TodayStatsOfMonitor struct {
|
||||||
|
Up int
|
||||||
|
Down int
|
||||||
|
Delay float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServiceSentinel() {
|
||||||
|
ServiceSentinelShared = &ServiceSentinel{
|
||||||
|
serviceReportChannel: make(chan ReportData, 200),
|
||||||
|
serviceStatusToday: make(map[uint64]*_TodayStatsOfMonitor),
|
||||||
|
serviceCurrentStatusIndex: make(map[uint64]int),
|
||||||
|
serviceCurrentStatusData: make(map[uint64][]model.MonitorHistory),
|
||||||
|
latestDate: make(map[uint64]string),
|
||||||
|
lastStatus: make(map[uint64]string),
|
||||||
|
serviceResponseDataStoreCurrentUp: make(map[uint64]uint64),
|
||||||
|
serviceResponseDataStoreCurrentDown: make(map[uint64]uint64),
|
||||||
|
monitors: make(map[uint64]model.Monitor),
|
||||||
|
sslCertCache: make(map[uint64]string),
|
||||||
|
}
|
||||||
|
ServiceSentinelShared.OnMonitorUpdate()
|
||||||
|
|
||||||
|
year, month, day := time.Now().Date()
|
||||||
|
today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
|
||||||
|
var mhs []model.MonitorHistory
|
||||||
|
DB.Where("created_at >= ?", today).Find(&mhs)
|
||||||
|
|
||||||
|
// 加载当日记录
|
||||||
|
totalDelay := make(map[uint64]float32)
|
||||||
|
for i := 0; i < len(mhs); i++ {
|
||||||
|
if mhs[i].Successful {
|
||||||
|
ServiceSentinelShared.serviceStatusToday[mhs[i].MonitorID].Up++
|
||||||
|
totalDelay[mhs[i].MonitorID] += mhs[i].Delay
|
||||||
|
} else {
|
||||||
|
ServiceSentinelShared.serviceStatusToday[mhs[i].MonitorID].Down++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for id, delay := range totalDelay {
|
||||||
|
ServiceSentinelShared.serviceStatusToday[id].Delay = delay / float32(ServiceSentinelShared.serviceStatusToday[id].Up)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新入库时间及当日数据入库游标
|
||||||
|
for k := range ServiceSentinelShared.monitors {
|
||||||
|
ServiceSentinelShared.latestDate[k] = time.Now().Format("02-Jan-06")
|
||||||
|
}
|
||||||
|
|
||||||
|
go ServiceSentinelShared.worker()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
使用缓存 channel,处理上报的 Service 请求结果,然后判断是否需要报警
|
||||||
|
需要记录上一次的状态信息
|
||||||
|
*/
|
||||||
|
type ServiceSentinel struct {
|
||||||
|
serviceResponseDataStoreLock sync.RWMutex
|
||||||
|
monitorsLock sync.RWMutex
|
||||||
|
serviceReportChannel chan ReportData
|
||||||
|
serviceStatusToday map[uint64]*_TodayStatsOfMonitor
|
||||||
|
serviceCurrentStatusIndex map[uint64]int
|
||||||
|
serviceCurrentStatusData map[uint64][]model.MonitorHistory
|
||||||
|
latestDate map[uint64]string
|
||||||
|
lastStatus map[uint64]string
|
||||||
|
serviceResponseDataStoreCurrentUp map[uint64]uint64
|
||||||
|
serviceResponseDataStoreCurrentDown map[uint64]uint64
|
||||||
|
monitors map[uint64]model.Monitor
|
||||||
|
sslCertCache map[uint64]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *ServiceSentinel) Dispatch(r ReportData) {
|
||||||
|
ss.serviceReportChannel <- r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *ServiceSentinel) Monitors() []model.Monitor {
|
||||||
|
ss.monitorsLock.RLock()
|
||||||
|
defer ss.monitorsLock.RUnlock()
|
||||||
|
var monitors []model.Monitor
|
||||||
|
for _, v := range ss.monitors {
|
||||||
|
monitors = append(monitors, v)
|
||||||
|
}
|
||||||
|
sort.SliceStable(monitors, func(i, j int) bool {
|
||||||
|
return monitors[i].ID < monitors[j].ID
|
||||||
|
})
|
||||||
|
return monitors
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *ServiceSentinel) OnMonitorUpdate() {
|
||||||
|
var monitors []model.Monitor
|
||||||
|
DB.Find(&monitors)
|
||||||
|
ss.monitorsLock.Lock()
|
||||||
|
defer ss.monitorsLock.Unlock()
|
||||||
|
ss.monitors = make(map[uint64]model.Monitor)
|
||||||
|
for i := 0; i < len(monitors); i++ {
|
||||||
|
ss.monitors[monitors[i].ID] = monitors[i]
|
||||||
|
if len(ss.serviceCurrentStatusData[monitors[i].ID]) == 0 {
|
||||||
|
ss.serviceCurrentStatusData[monitors[i].ID] = make([]model.MonitorHistory, _CurrentStatusSize)
|
||||||
|
}
|
||||||
|
if ss.serviceStatusToday[monitors[i].ID] == nil {
|
||||||
|
ss.serviceStatusToday[monitors[i].ID] = &_TodayStatsOfMonitor{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *ServiceSentinel) OnMonitorDelete(id uint64) {
|
||||||
|
ss.serviceResponseDataStoreLock.Lock()
|
||||||
|
defer ss.serviceResponseDataStoreLock.Unlock()
|
||||||
|
delete(ss.serviceCurrentStatusIndex, id)
|
||||||
|
delete(ss.serviceCurrentStatusData, id)
|
||||||
|
delete(ss.latestDate, id)
|
||||||
|
delete(ss.lastStatus, id)
|
||||||
|
delete(ss.serviceResponseDataStoreCurrentUp, id)
|
||||||
|
delete(ss.serviceResponseDataStoreCurrentDown, id)
|
||||||
|
delete(ss.sslCertCache, id)
|
||||||
|
ss.monitorsLock.Lock()
|
||||||
|
defer ss.monitorsLock.Unlock()
|
||||||
|
delete(ss.monitors, id)
|
||||||
|
Cache.Delete(model.CacheKeyServicePage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *ServiceSentinel) LoadStats() map[uint64]*model.ServiceItemResponse {
|
||||||
|
var cached bool
|
||||||
|
var msm map[uint64]*model.ServiceItemResponse
|
||||||
|
data, has := Cache.Get(model.CacheKeyServicePage)
|
||||||
|
if has {
|
||||||
|
msm = data.(map[uint64]*model.ServiceItemResponse)
|
||||||
|
cached = true
|
||||||
|
}
|
||||||
|
if !cached {
|
||||||
|
msm = make(map[uint64]*model.ServiceItemResponse)
|
||||||
|
var ms []model.Monitor
|
||||||
|
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
|
||||||
|
DB.Where("created_at >= ? AND created_at < ?", today.AddDate(0, 0, -29), today).Find(&mhs)
|
||||||
|
for i := 0; i < len(ms); i++ {
|
||||||
|
msm[ms[i].ID] = &model.ServiceItemResponse{
|
||||||
|
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 := 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]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 缓存一天
|
||||||
|
Cache.Set(model.CacheKeyServicePage, msm, time.Until(time.Date(year, month, day, 23, 59, 59, 999, today.Location())))
|
||||||
|
}
|
||||||
|
// 最新一天的数据
|
||||||
|
ss.serviceResponseDataStoreLock.RLock()
|
||||||
|
defer ss.serviceResponseDataStoreLock.RUnlock()
|
||||||
|
for k := range ss.monitors {
|
||||||
|
if msm[k] == nil {
|
||||||
|
msm[k] = &model.ServiceItemResponse{
|
||||||
|
Up: new([30]int),
|
||||||
|
Down: new([30]int),
|
||||||
|
Delay: new([30]float32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msm[k].Monitor = ss.monitors[k]
|
||||||
|
v := ss.serviceStatusToday[k]
|
||||||
|
msm[k].Up[29] = v.Up
|
||||||
|
msm[k].Down[29] = v.Down
|
||||||
|
msm[k].TotalUp += uint64(v.Up)
|
||||||
|
msm[k].TotalDown += uint64(v.Down)
|
||||||
|
msm[k].Delay[29] = v.Delay
|
||||||
|
}
|
||||||
|
// 最后 5 分钟的状态 与 monitor 对象填充
|
||||||
|
for k, v := range ss.serviceResponseDataStoreCurrentDown {
|
||||||
|
msm[k].CurrentDown = v
|
||||||
|
}
|
||||||
|
for k, v := range ss.serviceResponseDataStoreCurrentUp {
|
||||||
|
msm[k].CurrentUp = v
|
||||||
|
}
|
||||||
|
return msm
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStateStr(percent uint64) string {
|
||||||
|
if percent == 0 {
|
||||||
|
return "无数据"
|
||||||
|
}
|
||||||
|
if percent > 95 {
|
||||||
|
return "良好"
|
||||||
|
}
|
||||||
|
if percent > 80 {
|
||||||
|
return "低可用"
|
||||||
|
}
|
||||||
|
return "故障"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *ServiceSentinel) worker() {
|
||||||
|
for r := range ss.serviceReportChannel {
|
||||||
|
if ss.monitors[r.Data.GetId()].ID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mh := model.PB2MonitorHistory(r.Data)
|
||||||
|
ss.serviceResponseDataStoreLock.Lock()
|
||||||
|
// 先查看是否到下一天
|
||||||
|
nowDate := time.Now().Format("02-Jan-06")
|
||||||
|
if nowDate != ss.latestDate[mh.MonitorID] {
|
||||||
|
// 清理前一天数据
|
||||||
|
ss.latestDate[mh.MonitorID] = nowDate
|
||||||
|
ss.serviceResponseDataStoreCurrentUp[mh.MonitorID] = 0
|
||||||
|
ss.serviceResponseDataStoreCurrentDown[mh.MonitorID] = 0
|
||||||
|
ss.serviceStatusToday[mh.MonitorID].Delay = 0
|
||||||
|
ss.serviceStatusToday[mh.MonitorID].Up = 0
|
||||||
|
ss.serviceStatusToday[mh.MonitorID].Down = 0
|
||||||
|
}
|
||||||
|
// 写入当天状态
|
||||||
|
if mh.Successful {
|
||||||
|
ss.serviceStatusToday[mh.MonitorID].Delay = (ss.serviceStatusToday[mh.
|
||||||
|
MonitorID].Delay*float32(ss.serviceStatusToday[mh.MonitorID].Up) +
|
||||||
|
mh.Delay) / float32(ss.serviceStatusToday[mh.MonitorID].Up+1)
|
||||||
|
ss.serviceStatusToday[mh.MonitorID].Up++
|
||||||
|
} else {
|
||||||
|
ss.serviceStatusToday[mh.MonitorID].Down++
|
||||||
|
}
|
||||||
|
// 写入当前数据
|
||||||
|
ss.serviceCurrentStatusData[mh.MonitorID][ss.serviceCurrentStatusIndex[mh.MonitorID]] = mh
|
||||||
|
ss.serviceCurrentStatusIndex[mh.MonitorID]++
|
||||||
|
// 数据持久化
|
||||||
|
if ss.serviceCurrentStatusIndex[mh.MonitorID] == _CurrentStatusSize {
|
||||||
|
ss.serviceCurrentStatusIndex[mh.MonitorID] = 0
|
||||||
|
dataToSave := ss.serviceCurrentStatusData[mh.MonitorID]
|
||||||
|
if err := DB.Create(&dataToSave).Error; err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 更新当前状态
|
||||||
|
ss.serviceResponseDataStoreCurrentUp[mh.MonitorID] = 0
|
||||||
|
ss.serviceResponseDataStoreCurrentDown[mh.MonitorID] = 0
|
||||||
|
for i := 0; i < len(ss.serviceCurrentStatusData[mh.MonitorID]); i++ {
|
||||||
|
if ss.serviceCurrentStatusData[mh.MonitorID][i].MonitorID > 0 {
|
||||||
|
if ss.serviceCurrentStatusData[mh.MonitorID][i].Successful {
|
||||||
|
ss.serviceResponseDataStoreCurrentUp[mh.MonitorID]++
|
||||||
|
} else {
|
||||||
|
ss.serviceResponseDataStoreCurrentDown[mh.MonitorID]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var upPercent uint64 = 0
|
||||||
|
if ss.serviceResponseDataStoreCurrentDown[mh.MonitorID]+ss.serviceResponseDataStoreCurrentUp[mh.MonitorID] > 0 {
|
||||||
|
upPercent = ss.serviceResponseDataStoreCurrentUp[mh.MonitorID] * 100 / (ss.serviceResponseDataStoreCurrentDown[mh.MonitorID] + ss.serviceResponseDataStoreCurrentUp[mh.MonitorID])
|
||||||
|
}
|
||||||
|
stateStr := getStateStr(upPercent)
|
||||||
|
if Conf.Debug {
|
||||||
|
log.Println(ss.monitors[mh.MonitorID].Target, stateStr, "Agent:", r.Reporter, "Successful:", mh.Successful, "Response:", mh.Data)
|
||||||
|
}
|
||||||
|
if stateStr == "故障" || stateStr != ss.lastStatus[mh.MonitorID] {
|
||||||
|
ss.monitorsLock.RLock()
|
||||||
|
isNeedSendNotification := (ss.lastStatus[mh.MonitorID] != "" || stateStr == "故障") && ss.monitors[mh.MonitorID].Notify
|
||||||
|
ss.lastStatus[mh.MonitorID] = stateStr
|
||||||
|
if isNeedSendNotification {
|
||||||
|
go SendNotification(fmt.Sprintf("服务监控:%s 服务状态:%s", ss.monitors[mh.MonitorID].Name, stateStr), true)
|
||||||
|
}
|
||||||
|
ss.monitorsLock.RUnlock()
|
||||||
|
}
|
||||||
|
ss.serviceResponseDataStoreLock.Unlock()
|
||||||
|
// SSL 证书报警
|
||||||
|
var errMsg string
|
||||||
|
if strings.HasPrefix(mh.Data, "SSL证书错误:") {
|
||||||
|
// 排除 i/o timeont、connection timeout、EOF 错误
|
||||||
|
if !strings.HasSuffix(mh.Data, "timeout") &&
|
||||||
|
!strings.HasSuffix(mh.Data, "EOF") &&
|
||||||
|
!strings.HasSuffix(mh.Data, "timed out") {
|
||||||
|
errMsg = mh.Data
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var newCert = strings.Split(mh.Data, "|")
|
||||||
|
if len(newCert) > 1 {
|
||||||
|
if ss.sslCertCache[mh.MonitorID] == "" {
|
||||||
|
ss.sslCertCache[mh.MonitorID] = mh.Data
|
||||||
|
}
|
||||||
|
expiresNew, _ := time.Parse("2006-01-02 15:04:05 -0700 MST", newCert[1])
|
||||||
|
// 证书过期提醒
|
||||||
|
if expiresNew.Before(time.Now().AddDate(0, 0, 7)) {
|
||||||
|
errMsg = fmt.Sprintf(
|
||||||
|
"SSL证书将在七天内过期,过期时间:%s。",
|
||||||
|
expiresNew.Format("2006-01-02 15:04:05"))
|
||||||
|
}
|
||||||
|
// 证书变更提醒
|
||||||
|
var oldCert = strings.Split(ss.sslCertCache[mh.MonitorID], "|")
|
||||||
|
var expiresOld time.Time
|
||||||
|
if len(oldCert) > 1 {
|
||||||
|
expiresOld, _ = time.Parse("2006-01-02 15:04:05 -0700 MST", oldCert[1])
|
||||||
|
}
|
||||||
|
if oldCert[0] != newCert[0] && !expiresNew.Equal(expiresOld) {
|
||||||
|
errMsg = fmt.Sprintf(
|
||||||
|
"SSL证书变更,旧:%s, %s 过期;新:%s, %s 过期。",
|
||||||
|
oldCert[0], expiresOld.Format("2006-01-02 15:04:05"), newCert[0], expiresNew.Format("2006-01-02 15:04:05"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errMsg != "" {
|
||||||
|
ss.monitorsLock.RLock()
|
||||||
|
if ss.monitors[mh.MonitorID].Notify {
|
||||||
|
go SendNotification(fmt.Sprintf("服务监控:%s %s", ss.monitors[mh.MonitorID].Name, errMsg), true)
|
||||||
|
}
|
||||||
|
ss.monitorsLock.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ func (a *AuthHandler) GetRequestMetadata(ctx context.Context, uri ...string) (ma
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuthHandler) RequireTransportSecurity() bool {
|
func (a *AuthHandler) RequireTransportSecurity() bool {
|
||||||
return !dao.Conf.Debug
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuthHandler) Check(ctx context.Context) (uint64, error) {
|
func (a *AuthHandler) Check(ctx context.Context) (uint64, error) {
|
||||||
|
@ -3,8 +3,6 @@ package rpc
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/naiba/nezha/model"
|
"github.com/naiba/nezha/model"
|
||||||
@ -22,49 +20,12 @@ func (s *NezhaHandler) ReportTask(c context.Context, r *pb.TaskResult) (*pb.Rece
|
|||||||
if clientID, err = s.Auth.Check(c); err != nil {
|
if clientID, err = s.Auth.Check(c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if r.GetType() == model.TaskTypeHTTPGET {
|
if r.GetType() != model.TaskTypeCommand {
|
||||||
// SSL 证书报警
|
dao.ServiceSentinelShared.Dispatch(dao.ReportData{
|
||||||
var errMsg string
|
Data: r,
|
||||||
if strings.HasPrefix(r.GetData(), "SSL证书错误:") {
|
Reporter: clientID,
|
||||||
// 排除 i/o timeont、connection timeout、EOF 错误
|
})
|
||||||
if !strings.HasSuffix(r.GetData(), "timeout") &&
|
|
||||||
!strings.HasSuffix(r.GetData(), "EOF") &&
|
|
||||||
!strings.HasSuffix(r.GetData(), "timed out") {
|
|
||||||
errMsg = r.GetData()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
var last model.MonitorHistory
|
|
||||||
var newCert = strings.Split(r.GetData(), "|")
|
|
||||||
if len(newCert) > 1 {
|
|
||||||
expiresNew, _ := time.Parse("2006-01-02 15:04:05 -0700 MST", newCert[1])
|
|
||||||
// 证书过期提醒
|
|
||||||
if expiresNew.Before(time.Now().AddDate(0, 0, 7)) {
|
|
||||||
errMsg = fmt.Sprintf(
|
|
||||||
"SSL证书将在七天内过期,过期时间:%s。",
|
|
||||||
expiresNew.Format("2006-01-02 15:04:05"))
|
|
||||||
}
|
|
||||||
// 证书变更提醒
|
|
||||||
if err := dao.DB.Where("monitor_id = ? AND data LIKE ?", r.GetId(), "%|%").Order("id DESC").First(&last).Error; err == nil {
|
|
||||||
var oldCert = strings.Split(last.Data, "|")
|
|
||||||
var expiresOld time.Time
|
|
||||||
if len(oldCert) > 1 {
|
|
||||||
expiresOld, _ = time.Parse("2006-01-02 15:04:05 -0700 MST", oldCert[1])
|
|
||||||
}
|
|
||||||
if last.Data != "" && oldCert[0] != newCert[0] && !expiresNew.Equal(expiresOld) {
|
|
||||||
errMsg = fmt.Sprintf(
|
|
||||||
"SSL证书变更,旧:%s, %s 过期;新:%s, %s 过期。",
|
|
||||||
oldCert[0], expiresOld.Format("2006-01-02 15:04:05"), newCert[0], expiresNew.Format("2006-01-02 15:04:05"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if errMsg != "" {
|
|
||||||
var monitor model.Monitor
|
|
||||||
dao.DB.First(&monitor, "id = ?", r.GetId())
|
|
||||||
dao.SendNotification(fmt.Sprintf("服务监控:%s %s", monitor.Name, errMsg), true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.GetType() == model.TaskTypeCommand {
|
|
||||||
// 处理上报的计划任务
|
// 处理上报的计划任务
|
||||||
dao.CronLock.RLock()
|
dao.CronLock.RLock()
|
||||||
defer dao.CronLock.RUnlock()
|
defer dao.CronLock.RUnlock()
|
||||||
@ -81,12 +42,6 @@ func (s *NezhaHandler) ReportTask(c context.Context, r *pb.TaskResult) (*pb.Rece
|
|||||||
LastResult: r.GetSuccessful(),
|
LastResult: r.GetSuccessful(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// 存入历史记录
|
|
||||||
mh := model.PB2MonitorHistory(r)
|
|
||||||
if err := dao.DB.Create(&mh).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return &pb.Receipt{Proced: true}, nil
|
return &pb.Receipt{Proced: true}, nil
|
||||||
}
|
}
|
||||||
@ -102,10 +57,7 @@ func (s *NezhaHandler) RequestTask(h *pb.Host, stream pb.NezhaService_RequestTas
|
|||||||
dao.ServerList[clientID].TaskStream = stream
|
dao.ServerList[clientID].TaskStream = stream
|
||||||
dao.ServerList[clientID].TaskClose = closeCh
|
dao.ServerList[clientID].TaskClose = closeCh
|
||||||
dao.ServerLock.RUnlock()
|
dao.ServerLock.RUnlock()
|
||||||
select {
|
return <-closeCh
|
||||||
case err = <-closeCh:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NezhaHandler) ReportSystemState(c context.Context, r *pb.State) (*pb.Receipt, error) {
|
func (s *NezhaHandler) ReportSystemState(c context.Context, r *pb.State) (*pb.Receipt, error) {
|
||||||
@ -131,7 +83,6 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
|
|||||||
host := model.PB2Host(r)
|
host := model.PB2Host(r)
|
||||||
dao.ServerLock.RLock()
|
dao.ServerLock.RLock()
|
||||||
defer dao.ServerLock.RUnlock()
|
defer dao.ServerLock.RUnlock()
|
||||||
log.Println(dao.Conf.IgnoredIPNotificationServerIDs)
|
|
||||||
if dao.Conf.EnableIPChangeNotification &&
|
if dao.Conf.EnableIPChangeNotification &&
|
||||||
dao.Conf.IgnoredIPNotificationServerIDs[clientID] != struct{}{} &&
|
dao.Conf.IgnoredIPNotificationServerIDs[clientID] != struct{}{} &&
|
||||||
dao.ServerList[clientID].Host != nil &&
|
dao.ServerList[clientID].Host != nil &&
|
||||||
|
Loading…
x
Reference in New Issue
Block a user