🚸 可以使指定服务器不参与服务监控
This commit is contained in:
parent
6f565ba022
commit
585cf538d0
@ -1,13 +1,13 @@
|
|||||||
<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.6.2&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.6.3&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">
|
||||||
<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>
|
\>> QQ 交流群: ~~955957790~~ 已解散,**自 2021 年 3 月 26 日起不再提供技术支持,接受 PR。**<br>
|
||||||
\>> 交流论坛:正在选型搭建中…… 欢迎想建设社区的铁子与奶爸取得联系。
|
\>> 交流论坛:~~正在选型搭建中……~~ NodeBB 是理想选择,目前没精力维护。
|
||||||
|
|
||||||
\>> [我们的用户](https://www.google.com/search?q="powered+by+哪吒监控%7C哪吒面板"&filter=0) (Google)
|
\>> [我们的用户](https://www.google.com/search?q="powered+by+哪吒监控%7C哪吒面板"&filter=0) (Google)
|
||||||
|
|
||||||
|
@ -191,11 +191,12 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type monitorForm struct {
|
type monitorForm struct {
|
||||||
ID uint64
|
ID uint64
|
||||||
Name string
|
Name string
|
||||||
Target string
|
Target string
|
||||||
Type uint8
|
Type uint8
|
||||||
Notify string
|
Notify string
|
||||||
|
SkipServersRaw string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
||||||
@ -207,6 +208,7 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
|||||||
m.Target = mf.Target
|
m.Target = 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"
|
m.Notify = mf.Notify == "on"
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -39,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
|
||||||
|
@ -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 (
|
||||||
@ -14,10 +17,13 @@ const (
|
|||||||
|
|
||||||
type Monitor struct {
|
type Monitor struct {
|
||||||
Common
|
Common
|
||||||
Name string
|
Name string
|
||||||
Type uint8
|
Type uint8
|
||||||
Target string
|
Target string
|
||||||
Notify bool
|
SkipServersRaw string
|
||||||
|
Notify bool
|
||||||
|
|
||||||
|
SkipServers map[uint64]bool `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Monitor) PB() *pb.Task {
|
func (m *Monitor) PB() *pb.Task {
|
||||||
@ -27,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
|
||||||
|
}
|
||||||
|
@ -1,278 +1,368 @@
|
|||||||
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
|
||||||
closable: true,
|
.modal({
|
||||||
onApprove: function () {
|
closable: true,
|
||||||
confirmBtn.toggleClass('loading')
|
onApprove: function () {
|
||||||
callFn(extData)
|
confirmBtn.toggleClass("loading");
|
||||||
return false
|
callFn(extData);
|
||||||
}
|
return false;
|
||||||
}).modal('show')
|
},
|
||||||
|
})
|
||||||
|
.modal("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
function showFormModal(modelSelector, formID, URL, getData) {
|
function showFormModal(modelSelector, formID, URL, getData) {
|
||||||
$(modelSelector).modal({
|
$(modelSelector)
|
||||||
closable: true,
|
.modal({
|
||||||
onApprove: function () {
|
closable: true,
|
||||||
let success = false
|
onApprove: function () {
|
||||||
const btn = $(modelSelector + ' .positive.button')
|
let success = false;
|
||||||
const form = $(modelSelector + ' form')
|
const btn = $(modelSelector + " .positive.button");
|
||||||
if (btn.hasClass('loading')) {
|
const form = $(modelSelector + " form");
|
||||||
return success
|
if (btn.hasClass("loading")) {
|
||||||
}
|
return success;
|
||||||
form.children('.message').remove()
|
}
|
||||||
btn.toggleClass('loading')
|
form.children(".message").remove();
|
||||||
const data = getData ? getData() : $(formID).serializeArray().reduce(function (obj, item) {
|
btn.toggleClass("loading");
|
||||||
|
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" ||
|
||||||
obj[item.name] = parseInt(item.value);
|
item.name === "RequestType" ||
|
||||||
|
item.name === "RequestMethod" ||
|
||||||
|
item.name === "DisplayIndex" ||
|
||||||
|
item.name === "Type"
|
||||||
|
) {
|
||||||
|
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] =
|
||||||
}
|
"[" + item.value.substr(3, item.value.length - 1) + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
$.post(URL, JSON.stringify(data)).done(function (resp) {
|
$.post(URL, JSON.stringify(data))
|
||||||
if (resp.code == 200) {
|
.done(function (resp) {
|
||||||
if (resp.message) {
|
if (resp.code == 200) {
|
||||||
$.suiAlert({
|
if (resp.message) {
|
||||||
title: '操作成功',
|
$.suiAlert({
|
||||||
type: 'success',
|
title: "操作成功",
|
||||||
description: resp.message,
|
type: "success",
|
||||||
time: '3',
|
description: resp.message,
|
||||||
position: 'top-center',
|
time: "3",
|
||||||
});
|
position: "top-center",
|
||||||
}
|
});
|
||||||
window.location.reload()
|
}
|
||||||
} else {
|
window.location.reload();
|
||||||
form.append(`<div class="ui negative message"><div class="header">操作失败</div><p>` + resp.message + `</p></div>`)
|
} else {
|
||||||
}
|
form.append(
|
||||||
}).fail(function (err) {
|
`<div class="ui negative message"><div class="header">操作失败</div><p>` +
|
||||||
form.append(`<div class="ui negative message"><div class="header">网络错误</div><p>` + err.responseText + `</p></div>`)
|
resp.message +
|
||||||
}).always(function () {
|
`</p></div>`
|
||||||
btn.toggleClass('loading')
|
);
|
||||||
});
|
}
|
||||||
return success
|
})
|
||||||
}
|
.fail(function (err) {
|
||||||
}).modal('show')
|
form.append(
|
||||||
|
`<div class="ui negative message"><div class="header">网络错误</div><p>` +
|
||||||
|
err.responseText +
|
||||||
|
`</p></div>`
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.always(function () {
|
||||||
|
btn.toggleClass("loading");
|
||||||
|
});
|
||||||
|
return success;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.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>'
|
||||||
if (rule && rule.Enable) {
|
);
|
||||||
modal.find('.ui.rule-enable.checkbox').checkbox('set checked')
|
modal.find("input[name=ID]").val(rule ? rule.ID : null);
|
||||||
} else {
|
modal.find("input[name=Name]").val(rule ? rule.Name : null);
|
||||||
modal.find('.ui.rule-enable.checkbox').checkbox('set unchecked')
|
modal.find("textarea[name=RulesRaw]").val(rule ? rule.RulesRaw : null);
|
||||||
}
|
if (rule && rule.Enable) {
|
||||||
showFormModal('.rule.modal', '#ruleForm', '/api/alert-rule')
|
modal.find(".ui.rule-enable.checkbox").checkbox("set checked");
|
||||||
|
} else {
|
||||||
|
modal.find(".ui.rule-enable.checkbox").checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
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)
|
);
|
||||||
if (notification && notification.VerifySSL) {
|
modal.find("input[name=ID]").val(notification ? notification.ID : null);
|
||||||
modal.find('.ui.nf-ssl.checkbox').checkbox('set checked')
|
modal.find("input[name=Name]").val(notification ? notification.Name : null);
|
||||||
} else {
|
modal.find("input[name=URL]").val(notification ? notification.URL : null);
|
||||||
modal.find('.ui.nf-ssl.checkbox').checkbox('set unchecked')
|
modal
|
||||||
}
|
.find("textarea[name=RequestBody]")
|
||||||
showFormModal('.notification.modal', '#notificationForm', '/api/notification')
|
.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) {
|
||||||
|
modal.find(".ui.nf-ssl.checkbox").checkbox("set checked");
|
||||||
|
} else {
|
||||||
|
modal.find(".ui.nf-ssl.checkbox").checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
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);
|
||||||
if (server) {
|
modal.find("input[name=name]").val(server ? server.Name : null);
|
||||||
modal.find('.secret.field').attr('style', '')
|
modal.find("input[name=Tag]").val(server ? server.Tag : null);
|
||||||
modal.find('input[name=secret]').val(server.Secret)
|
modal
|
||||||
} else {
|
.find("input[name=DisplayIndex]")
|
||||||
modal.find('.secret.field').attr('style', 'display:none')
|
.val(server ? server.DisplayIndex : null);
|
||||||
modal.find('input[name=secret]').val('')
|
modal.find("textarea[name=Note]").val(server ? server.Note : null);
|
||||||
}
|
if (server) {
|
||||||
showFormModal('.server.modal', '#serverForm', '/api/server')
|
modal.find(".secret.field").attr("style", "");
|
||||||
|
modal.find("input[name=secret]").val(server.Secret);
|
||||||
|
} else {
|
||||||
|
modal.find(".secret.field").attr("style", "display:none");
|
||||||
|
modal.find("input[name=secret]").val("");
|
||||||
|
}
|
||||||
|
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)
|
);
|
||||||
if (monitor && monitor.Notify) {
|
modal.find("input[name=ID]").val(monitor ? monitor.ID : null);
|
||||||
modal.find(".ui.nb-notify.checkbox").checkbox("set checked");
|
modal.find("input[name=Name]").val(monitor ? monitor.Name : null);
|
||||||
} else {
|
modal.find("input[name=Target]").val(monitor ? monitor.Target : null);
|
||||||
modal.find(".ui.nb-notify.checkbox").checkbox("set unchecked");
|
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>'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
showFormModal('.monitor.modal', '#monitorForm', '/api/monitor')
|
}
|
||||||
|
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);
|
||||||
if (cron) {
|
modal.find("a.ui.label.visible").each((i, el) => {
|
||||||
servers = cron.ServersRaw
|
el.remove();
|
||||||
serverList = JSON.parse(servers)
|
});
|
||||||
const node = modal.find('i.dropdown.icon')
|
var servers;
|
||||||
for (let i = 0; i < serverList.length; i++) {
|
if (cron) {
|
||||||
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>')
|
servers = cron.ServersRaw;
|
||||||
}
|
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=ServersRaw]').val(cron ? '[],' + servers.substr(1, servers.length - 2) : '[]')
|
}
|
||||||
modal.find('textarea[name=Command]').val(cron ? cron.Command : null)
|
modal
|
||||||
if (cron && cron.PushSuccessful) {
|
.find("input[name=ServersRaw]")
|
||||||
modal.find('.ui.push-successful.checkbox').checkbox('set checked')
|
.val(cron ? "[]," + servers.substr(1, servers.length - 2) : "[]");
|
||||||
} else {
|
modal.find("textarea[name=Command]").val(cron ? cron.Command : null);
|
||||||
modal.find('.ui.push-successful.checkbox').checkbox('set unchecked')
|
if (cron && cron.PushSuccessful) {
|
||||||
}
|
modal.find(".ui.push-successful.checkbox").checkbox("set checked");
|
||||||
showFormModal('.cron.modal', '#cronForm', '/api/cron')
|
} else {
|
||||||
|
modal.find(".ui.push-successful.checkbox").checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
showFormModal(".cron.modal", "#cronForm", "/api/cron");
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteRequest(api) {
|
function deleteRequest(api) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: api,
|
url: api,
|
||||||
type: 'DELETE',
|
type: "DELETE",
|
||||||
}).done(resp => {
|
})
|
||||||
if (resp.code == 200) {
|
.done((resp) => {
|
||||||
if (resp.message) {
|
if (resp.code == 200) {
|
||||||
alert(resp.message)
|
if (resp.message) {
|
||||||
} else {
|
alert(resp.message);
|
||||||
alert('删除成功')
|
|
||||||
}
|
|
||||||
window.location.reload()
|
|
||||||
} else {
|
} else {
|
||||||
alert('删除失败 ' + resp.code + ':' + resp.message)
|
alert("删除成功");
|
||||||
confirmBtn.toggleClass('loading')
|
|
||||||
}
|
}
|
||||||
}).fail(err => {
|
window.location.reload();
|
||||||
alert('网络错误:' + err.responseText)
|
} else {
|
||||||
|
alert("删除失败 " + resp.code + ":" + resp.message);
|
||||||
|
confirmBtn.toggleClass("loading");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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) => {
|
||||||
if (resp.code == 200) {
|
$(btn).toggleClass("loading");
|
||||||
$.suiAlert({
|
if (resp.code == 200) {
|
||||||
title: '触发成功,等待执行结果',
|
|
||||||
type: 'success',
|
|
||||||
description: resp.message,
|
|
||||||
time: '3',
|
|
||||||
position: 'top-center',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$.suiAlert({
|
|
||||||
title: '触发失败 ',
|
|
||||||
type: 'error',
|
|
||||||
description: resp.code + ':' + resp.message,
|
|
||||||
time: '3',
|
|
||||||
position: 'top-center',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).fail(err => {
|
|
||||||
$(btn).toggleClass('loading')
|
|
||||||
$.suiAlert({
|
$.suiAlert({
|
||||||
title: '触发失败 ',
|
title: "触发成功,等待执行结果",
|
||||||
type: 'error',
|
type: "success",
|
||||||
description: '网络错误:' + err.responseText,
|
description: resp.message,
|
||||||
time: '3',
|
time: "3",
|
||||||
position: 'top-center',
|
position: "top-center",
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
$.suiAlert({
|
||||||
|
title: "触发失败 ",
|
||||||
|
type: "error",
|
||||||
|
description: resp.code + ":" + resp.message,
|
||||||
|
time: "3",
|
||||||
|
position: "top-center",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
$(btn).toggleClass("loading");
|
||||||
|
$.suiAlert({
|
||||||
|
title: "触发失败 ",
|
||||||
|
type: "error",
|
||||||
|
description: "网络错误:" + err.responseText,
|
||||||
|
time: "3",
|
||||||
|
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 }))
|
||||||
if (resp.code == 200) {
|
.done(function (resp) {
|
||||||
$.suiAlert({
|
if (resp.code == 200) {
|
||||||
title: '注销成功',
|
|
||||||
type: 'success',
|
|
||||||
description: '如需继续访问请使用 GitHub 再次登录',
|
|
||||||
time: '3',
|
|
||||||
position: 'top-center',
|
|
||||||
});
|
|
||||||
window.location.reload()
|
|
||||||
} else {
|
|
||||||
$.suiAlert({
|
|
||||||
title: '注销失败',
|
|
||||||
description: resp.code + ':' + resp.message,
|
|
||||||
type: 'error',
|
|
||||||
time: '3',
|
|
||||||
position: 'top-center',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).fail(function (err) {
|
|
||||||
$.suiAlert({
|
$.suiAlert({
|
||||||
title: '网络错误',
|
title: "注销成功",
|
||||||
description: err.responseText,
|
type: "success",
|
||||||
type: 'error',
|
description: "如需继续访问请使用 GitHub 再次登录",
|
||||||
time: '3',
|
time: "3",
|
||||||
position: 'top-center',
|
position: "top-center",
|
||||||
});
|
});
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
$.suiAlert({
|
||||||
|
title: "注销失败",
|
||||||
|
description: resp.code + ":" + resp.message,
|
||||||
|
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?v202104172324"></script>
|
<script src="/static/main.js?v202104222126"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
9
resource/template/component/monitor.html
vendored
9
resource/template/component/monitor.html
vendored
@ -24,6 +24,15 @@
|
|||||||
<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="field">
|
||||||
<div class="ui nb-notify checkbox">
|
<div class="ui nb-notify checkbox">
|
||||||
<input name="Notify" type="checkbox" tabindex="0" class="hidden" />
|
<input name="Notify" type="checkbox" tabindex="0" class="hidden" />
|
||||||
|
@ -2,60 +2,66 @@
|
|||||||
{{template "common/header" .}}
|
{{template "common/header" .}}
|
||||||
{{template "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"
|
||||||
</button>
|
onclick="addOrEditMonitor()"
|
||||||
</div>
|
>
|
||||||
</div>
|
<i class="add icon"></i> 添加监控
|
||||||
<table class="ui very basic table">
|
</button>
|
||||||
<thead>
|
</div>
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>名称</th>
|
|
||||||
<th>通知</th>
|
|
||||||
<th>目标</th>
|
|
||||||
<th>类型</th>
|
|
||||||
<th>管理</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{range $monitor := .Monitors}}
|
|
||||||
<tr>
|
|
||||||
<td>{{$monitor.ID}}</td>
|
|
||||||
<td>{{$monitor.Name}}</td>
|
|
||||||
<td>{{$monitor.Notify}}</td>
|
|
||||||
<td>{{$monitor.Target}}</td>
|
|
||||||
<td>
|
|
||||||
{{if eq $monitor.Type 1}}HTTP(S)/SSL证书
|
|
||||||
{{else if eq $monitor.Type 2}}
|
|
||||||
ICMP Ping
|
|
||||||
{{else}}
|
|
||||||
TCP 端口
|
|
||||||
{{end}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui mini icon buttons">
|
|
||||||
<button class="ui button" onclick="addOrEditMonitor({{$monitor}})">
|
|
||||||
<i class="edit icon"></i>
|
|
||||||
</button>
|
|
||||||
<button class="ui button"
|
|
||||||
onclick="showConfirm('删除监控','确认删除此监控?',deleteRequest,'/api/monitor/'+{{$monitor.ID}})">
|
|
||||||
<i class="trash alternate outline icon"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{end}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
<table class="ui very basic table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>名称</th>
|
||||||
|
<th>通知</th>
|
||||||
|
<th>目标</th>
|
||||||
|
<th>类型</th>
|
||||||
|
<th>跳过的服务器</th>
|
||||||
|
<th>管理</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{range $monitor := .Monitors}}
|
||||||
|
<tr>
|
||||||
|
<td>{{$monitor.ID}}</td>
|
||||||
|
<td>{{$monitor.Name}}</td>
|
||||||
|
<td>{{$monitor.Notify}}</td>
|
||||||
|
<td>{{$monitor.Target}}</td>
|
||||||
|
<td>
|
||||||
|
{{if eq $monitor.Type 1}}HTTP(S)/SSL证书 {{else if eq $monitor.Type
|
||||||
|
2}} ICMP Ping {{else}} TCP 端口 {{end}}
|
||||||
|
</td>
|
||||||
|
<td>{{$monitor.SkipServersRaw}}</td>
|
||||||
|
<td>
|
||||||
|
<div class="ui mini icon buttons">
|
||||||
|
<button
|
||||||
|
class="ui button"
|
||||||
|
onclick="addOrEditMonitor({{$monitor}})"
|
||||||
|
>
|
||||||
|
<i class="edit icon"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="ui button"
|
||||||
|
onclick="showConfirm('删除监控','确认删除此监控?',deleteRequest,'/api/monitor/'+{{$monitor.ID}})"
|
||||||
|
>
|
||||||
|
<i class="trash alternate outline icon"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{template "component/monitor"}}
|
{{template "component/monitor"}}
|
||||||
{{template "common/footer" .}}
|
{{template "common/footer" .}}
|
||||||
<script>
|
<script>
|
||||||
$('.checkbox').checkbox()
|
$(".checkbox").checkbox();
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
pb "github.com/naiba/nezha/proto"
|
pb "github.com/naiba/nezha/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "v0.6.2" // !!记得修改 README 重的 badge 版本!!
|
var Version = "v0.6.3" // !!记得修改 README 中的 badge 版本!!
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SnapshotDelay = 3
|
SnapshotDelay = 3
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
pb "github.com/naiba/nezha/proto"
|
pb "github.com/naiba/nezha/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
const _CurrentStatusSize = 60 // 统计 10 分钟内的数据为当前状态
|
const _CurrentStatusSize = 30 // 统计 5 分钟内的数据为当前状态
|
||||||
|
|
||||||
var ServiceSentinelShared *ServiceSentinel
|
var ServiceSentinelShared *ServiceSentinel
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ func (ss *ServiceSentinel) LoadStats() map[uint64]*model.ServiceItemResponse {
|
|||||||
msm[k].Delay[29] = (msm[k].Delay[29]*float32(msm[k].Up[29]) + v[i].Delay) / float32(msm[k].Up[29]+1)
|
msm[k].Delay[29] = (msm[k].Delay[29]*float32(msm[k].Up[29]) + v[i].Delay) / float32(msm[k].Up[29]+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 最后 10 分钟的状态 与 monitor 对象填充
|
// 最后 5 分钟的状态 与 monitor 对象填充
|
||||||
for k, v := range ss.serviceResponseDataStoreCurrentDown {
|
for k, v := range ss.serviceResponseDataStoreCurrentDown {
|
||||||
msm[k].CurrentDown = v
|
msm[k].CurrentDown = v
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user