diff --git a/.gitignore b/.gitignore
index affef48..5f4404c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,8 +10,11 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
+*.pprof
.idea
/data
/dist
.DS_Store
/main
+/cmd/agent/main
+/cmd/dashboard/main
\ No newline at end of file
diff --git a/README.md b/README.md
index 246a83b..f821c2d 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
LOGO designed by 熊大 .
-
+
:trollface: 哪吒监控 一站式轻监控轻运维系统。支持系统状态、HTTP(SSL 证书变更、即将到期、到期)、TCP、Ping 监控报警,命令批量执行和计划任务。
@@ -14,9 +14,9 @@
\>> [我们的用户](https://www.google.com/search?q="powered+by+哪吒监控"&filter=0) (Google)
-| 默认主题 | DayNight [@JackieSung](https://github.com/JackieSung4ev) | hotaru |
-| ------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------- |
-| ![首页截图1](https://s3.ax1x.com/2020/12/07/DvTCwD.jpg) | | |
+| 默认主题 | DayNight [@JackieSung](https://github.com/JackieSung4ev) | hotaru |
+| ----------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------- |
+| ![默认主题](resource/template/theme-default/screenshot.png) | ![daynight](resource/template/theme-daynight/screenshot.png) | |
## 安装脚本
@@ -36,11 +36,12 @@ CN=true sudo ./nezha.sh
_\* 使用 WatchTower 可以自动更新面板,Windows 终端可以使用 nssm 配置自启动(见尾部教程)_
-### 特殊技能
+### 增强配置
通过执行 `./nezha-agent --help` 查看支持的参数,如果你使用一键脚本,可以编辑 `/etc/systemd/system/nezha-agent.service`,在 `ExecStart=` 这一行的末尾加上
- `--skip-conn` 不监控连接数,机场/连接密集型机器推荐设置,不然比较占 CPU([shirou/gopsutil/issues#220](https://github.com/shirou/gopsutil/issues/220))
+- `--skip-procs` 不监控进程数,也可以降低 agent 占用
- `--disable-auto-update` 禁止 Agent 自动更新
## 功能说明
@@ -230,7 +231,7 @@ URL 里面也可放置占位符,请求时会进行简单的字符串替换。
- Agent 不断重启/无法启动 ?
+ Agent不上线不启动自检流程
1. 直接执行 `/opt/nezha/agent/nezha-agent -s 面板IP或非CDN域名:面板RPC端口 -p Agent密钥 -d` 查看日志是否是 DNS 问题。
2. `nc -v 域名/IP 面板RPC端口` 或者 `telnet 域名/IP 面板RPC端口` 检验是否是网络问题,检查本机与面板服务器出入站防火墙,如果单机无法判断可借助 提供的端口检查工具进行检测。
@@ -243,7 +244,7 @@ URL 里面也可放置占位符,请求时会进行简单的字符串替换。
首先在 release 下载对应的二进制解压 tar.gz 包后放置到 `/root`,然后 `chmod +x /root/nezha-agent` 赋予执行权限,然后创建 `/etc/init.d/nezha-service`:
-```
+```shell
#!/bin/sh /etc/rc.common
START=99
@@ -272,8 +273,9 @@ restart() {
- 实时通道断开/终端连接失败
-使用反向代理时需要针对 `/ws` 路径的 WebSocket 进行特别配置以支持实时更新服务器状态。
+ 实时通道断开/网页终端连接失败
+
+使用反向代理时需要针对 `/ws`,`/terminal` 路径的 WebSocket 进行特别配置以支持实时更新服务器状态和 **WebSSH**。
- Nginx(宝塔):在你的 nginx 配置文件中加入以下代码
@@ -285,7 +287,6 @@ restart() {
location ~ ^/(ws|terminal/.+)$ {
proxy_pass http://ip:站点访问端口;
- proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
@@ -295,6 +296,15 @@ restart() {
}
```
+ 如果非宝塔,还要在 `server{}` 中添加上这一段
+
+ ```nginx
+ location / {
+ proxy_pass http://ip:站点访问端口;
+ proxy_set_header Host $host;
+ }
+ ```
+
- CaddyServer v1(v2 无需特别配置)
```Caddyfile
diff --git a/cmd/agent/main.go b/cmd/agent/main.go
index 17096c1..896531e 100644
--- a/cmd/agent/main.go
+++ b/cmd/agent/main.go
@@ -30,25 +30,21 @@ import (
type AgentConfig struct {
SkipConnectionCount bool
+ SkipProcsCount bool
DisableAutoUpdate bool
Debug bool
Server string
ClientSecret string
}
-func init() {
- http.DefaultClient.Timeout = time.Second * 5
- flag.CommandLine.ParseErrorsWhitelist.UnknownFlags = true
-}
-
var (
- version string
- agentConf AgentConfig
+ version string
+ client pb.NezhaServiceClient
+ inited bool
)
var (
- client pb.NezhaServiceClient
- inited bool
+ agentConf AgentConfig
updateCh = make(chan struct{}) // Agent 自动更新间隔
httpClient = &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
@@ -63,6 +59,11 @@ const (
networkTimeOut = time.Second * 5 // 普通网络超时
)
+func init() {
+ http.DefaultClient.Timeout = time.Second * 5
+ flag.CommandLine.ParseErrorsWhitelist.UnknownFlags = true
+}
+
func main() {
// 来自于 GoReleaser 的版本号
monitor.Version = version
@@ -71,6 +72,7 @@ func main() {
flag.StringVarP(&agentConf.Server, "server", "s", "localhost:5555", "管理面板RPC端口")
flag.StringVarP(&agentConf.ClientSecret, "password", "p", "", "Agent连接Secret")
flag.BoolVar(&agentConf.SkipConnectionCount, "skip-conn", false, "不监控连接数")
+ flag.BoolVar(&agentConf.SkipProcsCount, "skip-procs", false, "不监控进程数")
flag.BoolVar(&agentConf.DisableAutoUpdate, "disable-auto-update", false, "禁用自动升级")
flag.Parse()
@@ -88,7 +90,6 @@ func run() {
}
go pty.DownloadDependency()
-
// 上报服务器信息
go reportState()
// 更新IP信息
@@ -168,8 +169,8 @@ func receiveTasks(tasks pb.NezhaService_RequestTaskClient) error {
}
go func() {
defer func() {
- if recover() != nil {
- println("task panic", task)
+ if err := recover(); err != nil {
+ println("task panic", task, err)
}
}()
doTask(task)
@@ -209,7 +210,7 @@ func reportState() {
if client != nil && inited {
monitor.TrackNetworkSpeed()
timeOutCtx, cancel := context.WithTimeout(context.Background(), networkTimeOut)
- _, err = client.ReportSystemState(timeOutCtx, monitor.GetState(agentConf.SkipConnectionCount).PB())
+ _, err = client.ReportSystemState(timeOutCtx, monitor.GetState(agentConf.SkipConnectionCount, agentConf.SkipProcsCount).PB())
cancel()
if err != nil {
println("reportState error", err)
@@ -229,7 +230,7 @@ func doSelfUpdate() {
println("检查更新:", v)
latest, err := selfupdate.UpdateSelf(v, "naiba/nezha")
if err != nil {
- println("自动更新失败:", err)
+ println("更新失败:", err)
return
}
if !latest.Version.Equals(v) {
@@ -282,7 +283,7 @@ func handleHttpGetTask(task *pb.Task, result *pb.TaskResult) {
}
if err == nil {
// 检查 SSL 证书信息
- if len(resp.TLS.PeerCertificates) > 0 {
+ if resp.TLS != nil && len(resp.TLS.PeerCertificates) > 0 {
c := resp.TLS.PeerCertificates[0]
result.Data = c.Issuer.CommonName + "|" + c.NotAfter.In(time.Local).String()
}
diff --git a/cmd/agent/monitor/monitor.go b/cmd/agent/monitor/monitor.go
index d2febd5..40c76f7 100644
--- a/cmd/agent/monitor/monitor.go
+++ b/cmd/agent/monitor/monitor.go
@@ -5,7 +5,6 @@ import (
"regexp"
"runtime"
"strings"
- "sync/atomic"
"syscall"
"time"
@@ -15,20 +14,27 @@ import (
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
+ "github.com/shirou/gopsutil/v3/process"
"github.com/naiba/nezha/model"
)
-var Version string = "debug"
-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", "fuse.rclone",
-}
-var excludeNetInterfaces = []string{
- "lo", "tun", "docker", "veth", "br-", "vmbr", "vnet", "kube",
-}
-var getMacDiskNo = regexp.MustCompile(`\/dev\/disk(\d)s.*`)
+var (
+ Version string = "debug"
+ expectDiskFsTypes = []string{
+ "apfs", "ext4", "ext3", "ext2", "f2fs", "reiserfs", "jfs", "btrfs",
+ "fuseblk", "zfs", "simfs", "ntfs", "fat32", "exfat", "xfs", "fuse.rclone",
+ }
+ excludeNetInterfaces = []string{
+ "lo", "tun", "docker", "veth", "br-", "vmbr", "vnet", "kube",
+ }
+ getMacDiskNo = regexp.MustCompile(`\/dev\/disk(\d)s.*`)
+)
+
+var (
+ netInSpeed, netOutSpeed, netInTransfer, netOutTransfer, lastUpdateNetStats uint64
+ cachedBootTime time.Time
+)
func GetHost() *model.Host {
hi, _ := host.Info()
@@ -58,6 +64,8 @@ func GetHost() *model.Host {
swapMemTotal = mv.SwapTotal
}
+ cachedBootTime = time.Now().Add(time.Duration(-1 * int64(hi.BootTime*1000)))
+
return &model.Host{
Platform: hi.OS,
PlatformVersion: hi.PlatformVersion,
@@ -74,8 +82,12 @@ func GetHost() *model.Host {
}
}
-func GetState(skipConnectionCount bool) *model.HostState {
- hi, _ := host.Info()
+func GetState(skipConnectionCount bool, skipProcsCount bool) *model.HostState {
+ var procs []int32
+ if !skipProcsCount {
+ procs, _ = process.Pids()
+ }
+
mv, _ := mem.VirtualMemory()
var swapMemUsed uint64
@@ -92,11 +104,11 @@ func GetState(skipConnectionCount bool) *model.HostState {
if err == nil {
cpuPercent = cp[0]
}
+
_, diskUsed := getDiskTotalAndUsed()
loadStat, _ := load.Avg()
var tcpConnCount, udpConnCount uint64
-
if !skipConnectionCount {
conns, _ := net.Connections("all")
for i := 0; i < len(conns); i++ {
@@ -114,17 +126,17 @@ func GetState(skipConnectionCount bool) *model.HostState {
MemUsed: mv.Total - mv.Available,
SwapUsed: swapMemUsed,
DiskUsed: diskUsed,
- NetInTransfer: atomic.LoadUint64(&netInTransfer),
- NetOutTransfer: atomic.LoadUint64(&netOutTransfer),
- NetInSpeed: atomic.LoadUint64(&netInSpeed),
- NetOutSpeed: atomic.LoadUint64(&netOutSpeed),
- Uptime: hi.Uptime,
+ NetInTransfer: netInTransfer,
+ NetOutTransfer: netOutTransfer,
+ NetInSpeed: netInSpeed,
+ NetOutSpeed: netOutSpeed,
+ Uptime: uint64(time.Since(cachedBootTime).Seconds()),
Load1: loadStat.Load1,
Load5: loadStat.Load5,
Load15: loadStat.Load15,
TcpConnCount: tcpConnCount,
UdpConnCount: udpConnCount,
- ProcessCount: hi.Procs,
+ ProcessCount: uint64(len(procs)),
}
}
@@ -140,14 +152,14 @@ func TrackNetworkSpeed() {
innerNetOutTransfer += v.BytesSent
}
now := uint64(time.Now().Unix())
- diff := now - atomic.LoadUint64(&lastUpdate)
+ diff := now - lastUpdateNetStats
if diff > 0 {
- atomic.StoreUint64(&netInSpeed, (innerNetInTransfer-atomic.LoadUint64(&netInTransfer))/diff)
- atomic.StoreUint64(&netOutSpeed, (innerNetOutTransfer-atomic.LoadUint64(&netOutTransfer))/diff)
+ netInSpeed = (innerNetInTransfer - netInTransfer) / diff
+ netOutSpeed = (innerNetOutTransfer - netOutTransfer) / diff
}
- atomic.StoreUint64(&netInTransfer, innerNetInTransfer)
- atomic.StoreUint64(&netOutTransfer, innerNetOutTransfer)
- atomic.StoreUint64(&lastUpdate, now)
+ netInTransfer = innerNetInTransfer
+ netOutTransfer = innerNetOutTransfer
+ lastUpdateNetStats = now
}
}
diff --git a/cmd/agent/monitor/myip.go b/cmd/agent/monitor/myip.go
index a0a2449..9ede603 100644
--- a/cmd/agent/monitor/myip.go
+++ b/cmd/agent/monitor/myip.go
@@ -35,13 +35,21 @@ func UpdateIP() {
for {
ipv4 := fetchGeoIP(geoIPApiList, false)
ipv6 := fetchGeoIP(geoIPApiList, true)
- cachedIP = fmt.Sprintf("ip(v4:%s,v6:[%s])", ipv4.IP, ipv6.IP)
+ if ipv4.IP == "" && ipv6.IP == "" {
+ time.Sleep(time.Minute)
+ continue
+ }
+ if ipv4.IP == "" || ipv6.IP == "" {
+ cachedIP = fmt.Sprintf("%s%s", ipv4.IP, ipv6.IP)
+ } else {
+ cachedIP = fmt.Sprintf("%s/%s", ipv4.IP, ipv6.IP)
+ }
if ipv4.CountryCode != "" {
cachedCountry = ipv4.CountryCode
} else if ipv6.CountryCode != "" {
cachedCountry = ipv6.CountryCode
}
- time.Sleep(time.Minute * 10)
+ time.Sleep(time.Minute * 30)
}
}
diff --git a/cmd/dashboard/controller/controller.go b/cmd/dashboard/controller/controller.go
index b20cb06..da84af9 100644
--- a/cmd/dashboard/controller/controller.go
+++ b/cmd/dashboard/controller/controller.go
@@ -112,7 +112,7 @@ func ServeWeb(port uint) *http.Server {
},
})
r.Static("/static", "resource/static")
- r.LoadHTMLGlob("resource/template/**/*")
+ r.LoadHTMLGlob("resource/template/**/*.html")
routers(r)
page404 := func(c *gin.Context) {
diff --git a/cmd/dashboard/controller/member_api.go b/cmd/dashboard/controller/member_api.go
index 25d65e8..9c9b3bd 100644
--- a/cmd/dashboard/controller/member_api.go
+++ b/cmd/dashboard/controller/member_api.go
@@ -267,19 +267,22 @@ func (ma *memberAPI) addOrEditCron(c *gin.Context) {
cr.Cover = cf.Cover
err = json.Unmarshal([]byte(cf.ServersRaw), &cr.Servers)
}
- if err == nil {
- _, err = cron.ParseStandard(cr.Scheduler)
- }
+ tx := dao.DB.Begin()
if err == nil {
if cf.ID == 0 {
- err = dao.DB.Create(&cr).Error
+ err = tx.Create(&cr).Error
} else {
- err = dao.DB.Save(&cr).Error
+ err = tx.Save(&cr).Error
}
}
if err == nil {
cr.CronID, err = dao.Cron.AddFunc(cr.Scheduler, dao.CronTrigger(cr))
}
+ if err == nil {
+ err = tx.Commit().Error
+ } else {
+ tx.Rollback()
+ }
if err != nil {
c.JSON(http.StatusOK, model.Response{
Code: http.StatusBadRequest,
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go
index 071d942..1f1d030 100644
--- a/cmd/dashboard/main.go
+++ b/cmd/dashboard/main.go
@@ -25,7 +25,7 @@ func init() {
// 初始化 dao 包
dao.Conf = &model.Config{}
- dao.Cron = cron.New(cron.WithLocation(shanghai))
+ dao.Cron = cron.New(cron.WithSeconds(), cron.WithLocation(shanghai))
dao.Crons = make(map[uint64]*model.Cron)
dao.ServerList = make(map[uint64]*model.Server)
dao.SecretToID = make(map[string]uint64)
@@ -60,13 +60,13 @@ func initSystem() {
loadCrons() //加载计划任务
// 清理 服务请求记录 和 流量记录 的旧数据
- _, err := dao.Cron.AddFunc("30 3 * * *", cleanMonitorHistory)
+ _, err := dao.Cron.AddFunc("0 30 3 * * *", cleanMonitorHistory)
if err != nil {
panic(err)
}
// 流量记录打点
- _, err = dao.Cron.AddFunc("0 * * * *", recordTransferHourlyUsage)
+ _, err = dao.Cron.AddFunc("0 0 * * * *", recordTransferHourlyUsage)
if err != nil {
panic(err)
}
@@ -162,10 +162,11 @@ func loadCrons() {
}
cr.CronID, err = dao.Cron.AddFunc(cr.Scheduler, dao.CronTrigger(cr))
- if err != nil {
- panic(err)
+ if err == nil {
+ dao.Crons[cr.ID] = &cr
+ } else {
+ log.Println("NEZHA>> 计划任务调度失败", cr, err)
}
- dao.Crons[cr.ID] = &cr
}
dao.Cron.Start()
}
diff --git a/cmd/playground/main.go b/cmd/playground/main.go
index 57b18b9..b2217c0 100644
--- a/cmd/playground/main.go
+++ b/cmd/playground/main.go
@@ -1,7 +1,25 @@
package main
-import "github.com/naiba/nezha/cmd/agent/pty"
+import (
+ "log"
+
+ "github.com/robfig/cron/v3"
+)
func main() {
- pty.DownloadDependency()
+ c := cron.New(cron.WithSeconds())
+ _, err := c.AddFunc("* * * * * *", func() {
+ log.Println("bingo second")
+ })
+ if err != nil {
+ panic(err)
+ }
+ _, err = c.AddFunc("* * * * *", func() {
+ log.Println("bingo minute")
+ })
+ if err != nil {
+ panic(err)
+ }
+ c.Start()
+ select {}
}
diff --git a/go.mod b/go.mod
index 8654ef5..8cc6e39 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
github.com/artdarek/go-unzip v1.0.0
github.com/blang/semver v3.5.1+incompatible
github.com/creack/pty v1.1.14
- github.com/fsnotify/fsnotify v1.4.9
+ github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gin-contrib/pprof v1.3.0
github.com/gin-gonic/gin v1.7.0
github.com/go-ping/ping v0.0.0-20210407214646-e4e642a95741
@@ -23,13 +23,14 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1 // indirect
github.com/robfig/cron/v3 v3.0.1
- github.com/shirou/gopsutil/v3 v3.21.7
+ github.com/shirou/gopsutil/v3 v3.21.8
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.7.0
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/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
google.golang.org/grpc v1.33.1
google.golang.org/protobuf v1.25.0
gopkg.in/yaml.v2 v2.2.8
diff --git a/go.sum b/go.sum
index 5373241..b03b7c9 100644
--- a/go.sum
+++ b/go.sum
@@ -290,8 +290,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/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/shirou/gopsutil/v3 v3.21.7 h1:PnTqQamUjwEDSgn+nBGu0qSDV/CfvyiR/gwTH3i7HTU=
-github.com/shirou/gopsutil/v3 v3.21.7/go.mod h1:RGl11Y7XMTQPmHh8F0ayC6haKNBgH4PXMJuTAcMOlz4=
+github.com/shirou/gopsutil/v3 v3.21.8 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA=
+github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
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/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@@ -320,10 +320,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/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw=
github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE=
-github.com/tklauser/go-sysconf v0.3.7 h1:HT7h4+536gjqeq1ZIJPgOl1rg1XFatQGVZWp7Py53eg=
-github.com/tklauser/go-sysconf v0.3.7/go.mod h1:JZIdXh4RmBvZDBZ41ld2bGxRV3n4daiiqA3skYhAoQ4=
-github.com/tklauser/numcpus v0.2.3 h1:nQ0QYpiritP6ViFhrKYsiv6VVxOpum2Gks5GhnJbS/8=
-github.com/tklauser/numcpus v0.2.3/go.mod h1:vpEPS/JC+oZGGQ/My/vJnNsvMDQL6PwOqt8dsCw5j+E=
+github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
+github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
+github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
+github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
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/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@@ -469,8 +469,9 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo=
+golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/model/config.go b/model/config.go
index 0c32d2d..7224b89 100644
--- a/model/config.go
+++ b/model/config.go
@@ -1,13 +1,11 @@
package model
import (
- "fmt"
"io/ioutil"
"os"
"strconv"
"strings"
- "github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"gopkg.in/yaml.v2"
)
@@ -68,13 +66,6 @@ func (c *Config) Read(path string) error {
}
c.updateIgnoredIPNotificationID()
-
- c.v.OnConfigChange(func(in fsnotify.Event) {
- c.v.Unmarshal(c)
- fmt.Println("配置文件更新,重载配置", c)
- })
-
- go c.v.WatchConfig()
return nil
}
diff --git a/resource/template/common/menu.html b/resource/template/common/menu.html
index 8292a4f..bd5bb4f 100644
--- a/resource/template/common/menu.html
+++ b/resource/template/common/menu.html
@@ -5,7 +5,7 @@
{{if .IsAdminPage}}
- 资产
+ 主机
服务
任务
报警
diff --git a/resource/template/component/cron.html b/resource/template/component/cron.html
index 9b315f9..3ee3da8 100644
--- a/resource/template/component/cron.html
+++ b/resource/template/component/cron.html
@@ -10,7 +10,7 @@
计划
-
+
命令
@@ -41,7 +41,7 @@
- 计划的格式为:* * * * *
分 时 天 月 星期,详情见 * * * * * * 秒 分 时 天 月 星期,详情见 计划表达式格式
命令:就像写 shell/bat 脚本一样,但是不推荐换行,多个命令使用 &&
/&
连接,如果遇到 xxx 命令找不到,可能是
diff --git a/resource/template/dashboard/monitor.html b/resource/template/dashboard/monitor.html
index 52fe96c..bc69aaa 100644
--- a/resource/template/dashboard/monitor.html
+++ b/resource/template/dashboard/monitor.html
@@ -5,7 +5,7 @@
@@ -43,7 +43,7 @@
+ onclick="showConfirm('删除服务','确认删除此服务?',deleteRequest,'/api/monitor/'+{{$monitor.ID}})">
diff --git a/resource/template/dashboard/server.html b/resource/template/dashboard/server.html
index 364376f..1fc31e1 100644
--- a/resource/template/dashboard/server.html
+++ b/resource/template/dashboard/server.html
@@ -6,7 +6,7 @@
添加服务器
+ class="add icon"> 添加主机
@@ -56,7 +56,7 @@
+ onclick="showConfirm('删除主机','确认删除此主机?',deleteRequest,'/api/server/'+{{$server.ID}})">
diff --git a/resource/template/theme-daynight/screenshot.png b/resource/template/theme-daynight/screenshot.png
new file mode 100644
index 0000000..c3d6d7f
Binary files /dev/null and b/resource/template/theme-daynight/screenshot.png differ
diff --git a/resource/template/theme-default/README.md b/resource/template/theme-default/README.md
deleted file mode 100644
index 5df6215..0000000
--- a/resource/template/theme-default/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# 默认主题
\ No newline at end of file
diff --git a/resource/template/theme-default/screenshot.png b/resource/template/theme-default/screenshot.png
new file mode 100644
index 0000000..90d24be
Binary files /dev/null and b/resource/template/theme-default/screenshot.png differ
diff --git a/resource/template/theme-hotaru/screenshot.png b/resource/template/theme-hotaru/screenshot.png
new file mode 100644
index 0000000..74eab8a
Binary files /dev/null and b/resource/template/theme-hotaru/screenshot.png differ
diff --git a/service/dao/dao.go b/service/dao/dao.go
index e5fec17..b8d8a0c 100644
--- a/service/dao/dao.go
+++ b/service/dao/dao.go
@@ -13,7 +13,7 @@ import (
pb "github.com/naiba/nezha/proto"
)
-var Version = "v0.10.1" // !!记得修改 README 中的 badge 版本!!
+var Version = "v0.10.2" // !!记得修改 README 中的 badge 版本!!
var (
Conf *model.Config
diff --git a/service/dao/servicesentinel.go b/service/dao/servicesentinel.go
index 56f3db5..0cce017 100644
--- a/service/dao/servicesentinel.go
+++ b/service/dao/servicesentinel.go
@@ -10,7 +10,6 @@ import (
"github.com/naiba/nezha/model"
pb "github.com/naiba/nezha/proto"
- "github.com/robfig/cron/v3"
)
const _CurrentStatusSize = 30 // 统计 15 分钟内的数据为当前状态
@@ -42,11 +41,9 @@ func NewServiceSentinel(serviceSentinelDispatchBus chan<- model.Monitor) {
sslCertCache: make(map[uint64]string),
// 30天数据缓存
monthlyStatus: make(map[uint64]*model.ServiceItemResponse),
- dispatchCron: cron.New(cron.WithSeconds()),
dispatchBus: serviceSentinelDispatchBus,
}
ServiceSentinelShared.loadMonitorHistory()
- ServiceSentinelShared.dispatchCron.Start()
year, month, day := time.Now().Date()
today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
@@ -75,7 +72,7 @@ func NewServiceSentinel(serviceSentinelDispatchBus chan<- model.Monitor) {
go ServiceSentinelShared.worker()
// 每日将游标往后推一天
- _, err := Cron.AddFunc("0 0 * * *", ServiceSentinelShared.refreshMonthlyServiceStatus)
+ _, err := Cron.AddFunc("0 0 0 * * *", ServiceSentinelShared.refreshMonthlyServiceStatus)
if err != nil {
panic(err)
}
@@ -102,8 +99,7 @@ type ServiceSentinel struct {
monthlyStatusLock sync.Mutex
monthlyStatus map[uint64]*model.ServiceItemResponse
// 服务监控调度计划任务
- dispatchCron *cron.Cron
- dispatchBus chan<- model.Monitor
+ dispatchBus chan<- model.Monitor
}
func (ss *ServiceSentinel) refreshMonthlyServiceStatus() {
@@ -147,7 +143,7 @@ func (ss *ServiceSentinel) loadMonitorHistory() {
ss.monitors = make(map[uint64]*model.Monitor)
for i := 0; i < len(monitors); i++ {
task := *monitors[i]
- monitors[i].CronJobID, err = ss.dispatchCron.AddFunc(task.CronSpec(), func() {
+ monitors[i].CronJobID, err = Cron.AddFunc(task.CronSpec(), func() {
ss.dispatchBus <- task
})
log.Println("NEZHA>> 服务监控任务", monitors[i].ID, monitors[i].Name, monitors[i].CronJobID)
@@ -194,7 +190,7 @@ func (ss *ServiceSentinel) OnMonitorUpdate(m model.Monitor) error {
defer ss.monitorsLock.Unlock()
var err error
// 写入新任务
- m.CronJobID, err = ss.dispatchCron.AddFunc(m.CronSpec(), func() {
+ m.CronJobID, err = Cron.AddFunc(m.CronSpec(), func() {
ss.dispatchBus <- m
})
if err != nil {
@@ -202,7 +198,7 @@ func (ss *ServiceSentinel) OnMonitorUpdate(m model.Monitor) error {
}
if ss.monitors[m.ID] != nil {
// 停掉旧任务
- ss.dispatchCron.Remove(ss.monitors[m.ID].CronJobID)
+ Cron.Remove(ss.monitors[m.ID].CronJobID)
} else {
// 新任务初始化数据
ss.monthlyStatusLock.Lock()
@@ -236,7 +232,7 @@ func (ss *ServiceSentinel) OnMonitorDelete(id uint64) {
ss.monitorsLock.Lock()
defer ss.monitorsLock.Unlock()
// 停掉定时任务
- ss.dispatchCron.Remove(ss.monitors[id].CronJobID)
+ Cron.Remove(ss.monitors[id].CronJobID)
delete(ss.monitors, id)
ss.monthlyStatusLock.Lock()
defer ss.monthlyStatusLock.Unlock()