|
|
@ -72,23 +72,12 @@ class Monitor extends BeanModel {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Return an object that ready to parse to JSON
|
|
|
|
* Return an object that ready to parse to JSON
|
|
|
|
|
|
|
|
* @param {object} preloadData to prevent n+1 problems, we query the data in a batch outside of this function
|
|
|
|
* @param {boolean} includeSensitiveData Include sensitive data in
|
|
|
|
* @param {boolean} includeSensitiveData Include sensitive data in
|
|
|
|
* JSON
|
|
|
|
* JSON
|
|
|
|
* @returns {Promise<object>} Object ready to parse
|
|
|
|
* @returns {object} Object ready to parse
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
async toJSON(includeSensitiveData = true) {
|
|
|
|
toJSON(preloadData = {}, includeSensitiveData = true) {
|
|
|
|
|
|
|
|
|
|
|
|
let notificationIDList = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let list = await R.find("monitor_notification", " monitor_id = ? ", [
|
|
|
|
|
|
|
|
this.id,
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (let bean of list) {
|
|
|
|
|
|
|
|
notificationIDList[bean.notification_id] = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const tags = await this.getTags();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let screenshot = null;
|
|
|
|
let screenshot = null;
|
|
|
|
|
|
|
|
|
|
|
@ -96,7 +85,7 @@ class Monitor extends BeanModel {
|
|
|
|
screenshot = "/screenshots/" + jwt.sign(this.id, UptimeKumaServer.getInstance().jwtSecret) + ".png";
|
|
|
|
screenshot = "/screenshots/" + jwt.sign(this.id, UptimeKumaServer.getInstance().jwtSecret) + ".png";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const path = await this.getPath();
|
|
|
|
const path = preloadData.paths.get(this.id) || [];
|
|
|
|
const pathName = path.join(" / ");
|
|
|
|
const pathName = path.join(" / ");
|
|
|
|
|
|
|
|
|
|
|
|
let data = {
|
|
|
|
let data = {
|
|
|
@ -106,15 +95,15 @@ class Monitor extends BeanModel {
|
|
|
|
path,
|
|
|
|
path,
|
|
|
|
pathName,
|
|
|
|
pathName,
|
|
|
|
parent: this.parent,
|
|
|
|
parent: this.parent,
|
|
|
|
childrenIDs: await Monitor.getAllChildrenIDs(this.id),
|
|
|
|
childrenIDs: preloadData.childrenIDs.get(this.id) || [],
|
|
|
|
url: this.url,
|
|
|
|
url: this.url,
|
|
|
|
method: this.method,
|
|
|
|
method: this.method,
|
|
|
|
hostname: this.hostname,
|
|
|
|
hostname: this.hostname,
|
|
|
|
port: this.port,
|
|
|
|
port: this.port,
|
|
|
|
maxretries: this.maxretries,
|
|
|
|
maxretries: this.maxretries,
|
|
|
|
weight: this.weight,
|
|
|
|
weight: this.weight,
|
|
|
|
active: await this.isActive(),
|
|
|
|
active: preloadData.activeStatus.get(this.id),
|
|
|
|
forceInactive: !await Monitor.isParentActive(this.id),
|
|
|
|
forceInactive: preloadData.forceInactive.get(this.id),
|
|
|
|
type: this.type,
|
|
|
|
type: this.type,
|
|
|
|
timeout: this.timeout,
|
|
|
|
timeout: this.timeout,
|
|
|
|
interval: this.interval,
|
|
|
|
interval: this.interval,
|
|
|
@ -134,9 +123,9 @@ class Monitor extends BeanModel {
|
|
|
|
docker_container: this.docker_container,
|
|
|
|
docker_container: this.docker_container,
|
|
|
|
docker_host: this.docker_host,
|
|
|
|
docker_host: this.docker_host,
|
|
|
|
proxyId: this.proxy_id,
|
|
|
|
proxyId: this.proxy_id,
|
|
|
|
notificationIDList,
|
|
|
|
notificationIDList: preloadData.notifications.get(this.id) || {},
|
|
|
|
tags: tags,
|
|
|
|
tags: preloadData.tags.get(this.id) || [],
|
|
|
|
maintenance: await Monitor.isUnderMaintenance(this.id),
|
|
|
|
maintenance: preloadData.maintenanceStatus.get(this.id),
|
|
|
|
mqttTopic: this.mqttTopic,
|
|
|
|
mqttTopic: this.mqttTopic,
|
|
|
|
mqttSuccessMessage: this.mqttSuccessMessage,
|
|
|
|
mqttSuccessMessage: this.mqttSuccessMessage,
|
|
|
|
mqttCheckType: this.mqttCheckType,
|
|
|
|
mqttCheckType: this.mqttCheckType,
|
|
|
@ -202,16 +191,6 @@ class Monitor extends BeanModel {
|
|
|
|
return data;
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Checks if the monitor is active based on itself and its parents
|
|
|
|
|
|
|
|
* @returns {Promise<boolean>} Is the monitor active?
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
async isActive() {
|
|
|
|
|
|
|
|
const parentActive = await Monitor.isParentActive(this.id);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (this.active === 1) && parentActive;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Get all tags applied to this monitor
|
|
|
|
* Get all tags applied to this monitor
|
|
|
|
* @returns {Promise<LooseObject<any>[]>} List of tags on the
|
|
|
|
* @returns {Promise<LooseObject<any>[]>} List of tags on the
|
|
|
@ -1197,6 +1176,18 @@ class Monitor extends BeanModel {
|
|
|
|
return checkCertificateResult;
|
|
|
|
return checkCertificateResult;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Checks if the monitor is active based on itself and its parents
|
|
|
|
|
|
|
|
* @param {number} monitorID ID of monitor to send
|
|
|
|
|
|
|
|
* @param {boolean} active is active
|
|
|
|
|
|
|
|
* @returns {Promise<boolean>} Is the monitor active?
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
static async isActive(monitorID, active) {
|
|
|
|
|
|
|
|
const parentActive = await Monitor.isParentActive(monitorID);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (active === 1) && parentActive;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Send statistics to clients
|
|
|
|
* Send statistics to clients
|
|
|
|
* @param {Server} io Socket server instance
|
|
|
|
* @param {Server} io Socket server instance
|
|
|
@ -1333,7 +1324,10 @@ class Monitor extends BeanModel {
|
|
|
|
for (let notification of notificationList) {
|
|
|
|
for (let notification of notificationList) {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const heartbeatJSON = bean.toJSON();
|
|
|
|
const heartbeatJSON = bean.toJSON();
|
|
|
|
|
|
|
|
const monitorData = [{ id: monitor.id,
|
|
|
|
|
|
|
|
active: monitor.active
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
const preloadData = await Monitor.preparePreloadData(monitorData);
|
|
|
|
// Prevent if the msg is undefined, notifications such as Discord cannot send out.
|
|
|
|
// Prevent if the msg is undefined, notifications such as Discord cannot send out.
|
|
|
|
if (!heartbeatJSON["msg"]) {
|
|
|
|
if (!heartbeatJSON["msg"]) {
|
|
|
|
heartbeatJSON["msg"] = "N/A";
|
|
|
|
heartbeatJSON["msg"] = "N/A";
|
|
|
@ -1344,7 +1338,7 @@ class Monitor extends BeanModel {
|
|
|
|
heartbeatJSON["timezoneOffset"] = UptimeKumaServer.getInstance().getTimezoneOffset();
|
|
|
|
heartbeatJSON["timezoneOffset"] = UptimeKumaServer.getInstance().getTimezoneOffset();
|
|
|
|
heartbeatJSON["localDateTime"] = dayjs.utc(heartbeatJSON["time"]).tz(heartbeatJSON["timezone"]).format(SQL_DATETIME_FORMAT);
|
|
|
|
heartbeatJSON["localDateTime"] = dayjs.utc(heartbeatJSON["time"]).tz(heartbeatJSON["timezone"]).format(SQL_DATETIME_FORMAT);
|
|
|
|
|
|
|
|
|
|
|
|
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(false), heartbeatJSON);
|
|
|
|
await Notification.send(JSON.parse(notification.config), msg, monitor.toJSON(preloadData, false), heartbeatJSON);
|
|
|
|
} catch (e) {
|
|
|
|
} catch (e) {
|
|
|
|
log.error("monitor", "Cannot send notification to " + notification.name);
|
|
|
|
log.error("monitor", "Cannot send notification to " + notification.name);
|
|
|
|
log.error("monitor", e);
|
|
|
|
log.error("monitor", e);
|
|
|
@ -1507,6 +1501,111 @@ class Monitor extends BeanModel {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Gets monitor notification of multiple monitor
|
|
|
|
|
|
|
|
* @param {Array} monitorIDs IDs of monitor to get
|
|
|
|
|
|
|
|
* @returns {Promise<LooseObject<any>>} object
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
static async getMonitorNotification(monitorIDs) {
|
|
|
|
|
|
|
|
return await R.getAll(`
|
|
|
|
|
|
|
|
SELECT monitor_notification.monitor_id, monitor_notification.notification_id
|
|
|
|
|
|
|
|
FROM monitor_notification
|
|
|
|
|
|
|
|
WHERE monitor_notification.monitor_id IN (?)
|
|
|
|
|
|
|
|
`, [
|
|
|
|
|
|
|
|
monitorIDs,
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Gets monitor tags of multiple monitor
|
|
|
|
|
|
|
|
* @param {Array} monitorIDs IDs of monitor to get
|
|
|
|
|
|
|
|
* @returns {Promise<LooseObject<any>>} object
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
static async getMonitorTag(monitorIDs) {
|
|
|
|
|
|
|
|
return await R.getAll(`
|
|
|
|
|
|
|
|
SELECT monitor_tag.monitor_id, tag.name, tag.color
|
|
|
|
|
|
|
|
FROM monitor_tag
|
|
|
|
|
|
|
|
JOIN tag ON monitor_tag.tag_id = tag.id
|
|
|
|
|
|
|
|
WHERE monitor_tag.monitor_id IN (?)
|
|
|
|
|
|
|
|
`, [
|
|
|
|
|
|
|
|
monitorIDs,
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* prepare preloaded data for efficient access
|
|
|
|
|
|
|
|
* @param {Array} monitorData IDs & active field of monitor to get
|
|
|
|
|
|
|
|
* @returns {Promise<LooseObject<any>>} object
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
static async preparePreloadData(monitorData) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const notificationsMap = new Map();
|
|
|
|
|
|
|
|
const tagsMap = new Map();
|
|
|
|
|
|
|
|
const maintenanceStatusMap = new Map();
|
|
|
|
|
|
|
|
const childrenIDsMap = new Map();
|
|
|
|
|
|
|
|
const activeStatusMap = new Map();
|
|
|
|
|
|
|
|
const forceInactiveMap = new Map();
|
|
|
|
|
|
|
|
const pathsMap = new Map();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (monitorData.length > 0) {
|
|
|
|
|
|
|
|
const monitorIDs = monitorData.map(monitor => monitor.id);
|
|
|
|
|
|
|
|
const notifications = await Monitor.getMonitorNotification(monitorIDs);
|
|
|
|
|
|
|
|
const tags = await Monitor.getMonitorTag(monitorIDs);
|
|
|
|
|
|
|
|
const maintenanceStatuses = await Promise.all(monitorData.map(monitor => Monitor.isUnderMaintenance(monitor.id)));
|
|
|
|
|
|
|
|
const childrenIDs = await Promise.all(monitorData.map(monitor => Monitor.getAllChildrenIDs(monitor.id)));
|
|
|
|
|
|
|
|
const activeStatuses = await Promise.all(monitorData.map(monitor => Monitor.isActive(monitor.id, monitor.active)));
|
|
|
|
|
|
|
|
const forceInactiveStatuses = await Promise.all(monitorData.map(monitor => Monitor.isParentActive(monitor.id)));
|
|
|
|
|
|
|
|
const paths = await Promise.all(monitorData.map(monitor => Monitor.getAllPath(monitor.id, monitor.name)));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
notifications.forEach(row => {
|
|
|
|
|
|
|
|
if (!notificationsMap.has(row.monitor_id)) {
|
|
|
|
|
|
|
|
notificationsMap.set(row.monitor_id, {});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
notificationsMap.get(row.monitor_id)[row.notification_id] = true;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tags.forEach(row => {
|
|
|
|
|
|
|
|
if (!tagsMap.has(row.monitor_id)) {
|
|
|
|
|
|
|
|
tagsMap.set(row.monitor_id, []);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
tagsMap.get(row.monitor_id).push({
|
|
|
|
|
|
|
|
name: row.name,
|
|
|
|
|
|
|
|
color: row.color
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
monitorData.forEach((monitor, index) => {
|
|
|
|
|
|
|
|
maintenanceStatusMap.set(monitor.id, maintenanceStatuses[index]);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
monitorData.forEach((monitor, index) => {
|
|
|
|
|
|
|
|
childrenIDsMap.set(monitor.id, childrenIDs[index]);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
monitorData.forEach((monitor, index) => {
|
|
|
|
|
|
|
|
activeStatusMap.set(monitor.id, activeStatuses[index]);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
monitorData.forEach((monitor, index) => {
|
|
|
|
|
|
|
|
forceInactiveMap.set(monitor.id, !forceInactiveStatuses[index]);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
monitorData.forEach((monitor, index) => {
|
|
|
|
|
|
|
|
pathsMap.set(monitor.id, paths[index]);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
notifications: notificationsMap,
|
|
|
|
|
|
|
|
tags: tagsMap,
|
|
|
|
|
|
|
|
maintenanceStatus: maintenanceStatusMap,
|
|
|
|
|
|
|
|
childrenIDs: childrenIDsMap,
|
|
|
|
|
|
|
|
activeStatus: activeStatusMap,
|
|
|
|
|
|
|
|
forceInactive: forceInactiveMap,
|
|
|
|
|
|
|
|
paths: pathsMap,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Gets Parent of the monitor
|
|
|
|
* Gets Parent of the monitor
|
|
|
|
* @param {number} monitorID ID of monitor to get
|
|
|
|
* @param {number} monitorID ID of monitor to get
|
|
|
@ -1539,16 +1638,18 @@ class Monitor extends BeanModel {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Gets the full path
|
|
|
|
* Gets the full path
|
|
|
|
|
|
|
|
* @param {number} monitorID ID of the monitor to get
|
|
|
|
|
|
|
|
* @param {string} name of the monitor to get
|
|
|
|
* @returns {Promise<string[]>} Full path (includes groups and the name) of the monitor
|
|
|
|
* @returns {Promise<string[]>} Full path (includes groups and the name) of the monitor
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
async getPath() {
|
|
|
|
static async getAllPath(monitorID, name) {
|
|
|
|
const path = [ this.name ];
|
|
|
|
const path = [ name ];
|
|
|
|
|
|
|
|
|
|
|
|
if (this.parent === null) {
|
|
|
|
if (this.parent === null) {
|
|
|
|
return path;
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let parent = await Monitor.getParent(this.id);
|
|
|
|
let parent = await Monitor.getParent(monitorID);
|
|
|
|
while (parent !== null) {
|
|
|
|
while (parent !== null) {
|
|
|
|
path.unshift(parent.name);
|
|
|
|
path.unshift(parent.name);
|
|
|
|
parent = await Monitor.getParent(parent.id);
|
|
|
|
parent = await Monitor.getParent(parent.id);
|
|
|
|