Merge branch 'master' into feature/add-channel-notification-for-slack
This commit is contained in:
commit
376d84c742
11
README.md
11
README.md
|
@ -148,17 +148,18 @@ Telegram Notification Sample:
|
||||||
|
|
||||||
If you love this project, please consider giving me a ⭐.
|
If you love this project, please consider giving me a ⭐.
|
||||||
|
|
||||||
## 🗣️ Discussion
|
## 🗣️ Discussion / Ask for Help
|
||||||
|
|
||||||
### Issues Page
|
⚠️ For any general or technical questions, please don't send me an email, as I am unable to provide support in that manner. I will not response if you asked such questions.
|
||||||
|
|
||||||
You can discuss or ask for help in [issues](https://github.com/louislam/uptime-kuma/issues).
|
I recommend using Google, GitHub Issues, or Uptime Kuma's Subreddit for finding answers to your question. If you cannot find the information you need, feel free to ask:
|
||||||
|
|
||||||
### Subreddit
|
- [GitHub Issues](https://github.com/louislam/uptime-kuma/issues)
|
||||||
|
- [Subreddit r/Uptime kuma](https://www.reddit.com/r/UptimeKuma/)
|
||||||
|
|
||||||
My Reddit account: [u/louislamlam](https://reddit.com/u/louislamlam).
|
My Reddit account: [u/louislamlam](https://reddit.com/u/louislamlam).
|
||||||
You can mention me if you ask a question on Reddit.
|
You can mention me if you ask a question on Reddit.
|
||||||
[r/Uptime kuma](https://www.reddit.com/r/UptimeKuma/)
|
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -185,6 +185,7 @@
|
||||||
"vue-router": "~4.0.14",
|
"vue-router": "~4.0.14",
|
||||||
"vue-toastification": "~2.0.0-rc.5",
|
"vue-toastification": "~2.0.0-rc.5",
|
||||||
"vuedraggable": "~4.1.0",
|
"vuedraggable": "~4.1.0",
|
||||||
"wait-on": "^6.0.1"
|
"wait-on": "^6.0.1",
|
||||||
|
"whatwg-url": "~12.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,8 +365,8 @@ class Monitor extends BeanModel {
|
||||||
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
|
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
|
||||||
|
|
||||||
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
|
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
|
||||||
log.debug("monitor", `[${this.name}] call sendCertNotification`);
|
log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`);
|
||||||
await this.sendCertNotification(tlsInfoObject);
|
await this.checkCertExpiryNotifications(tlsInfoObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1212,13 +1212,19 @@ class Monitor extends BeanModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send notification about a certificate
|
* checks certificate chain for expiring certificates
|
||||||
* @param {Object} tlsInfoObject Information about certificate
|
* @param {Object} tlsInfoObject Information about certificate
|
||||||
*/
|
*/
|
||||||
async sendCertNotification(tlsInfoObject) {
|
async checkCertExpiryNotifications(tlsInfoObject) {
|
||||||
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
|
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
|
||||||
const notificationList = await Monitor.getNotificationList(this);
|
const notificationList = await Monitor.getNotificationList(this);
|
||||||
|
|
||||||
|
if (! notificationList.length > 0) {
|
||||||
|
// fail fast. If no notification is set, all the following checks can be skipped.
|
||||||
|
log.debug("monitor", "No notification, no need to send cert notification");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let notifyDays = await setting("tlsExpiryNotifyDays");
|
let notifyDays = await setting("tlsExpiryNotifyDays");
|
||||||
if (notifyDays == null || !Array.isArray(notifyDays)) {
|
if (notifyDays == null || !Array.isArray(notifyDays)) {
|
||||||
// Reset Default
|
// Reset Default
|
||||||
|
@ -1226,10 +1232,19 @@ class Monitor extends BeanModel {
|
||||||
notifyDays = [ 7, 14, 21 ];
|
notifyDays = [ 7, 14, 21 ];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notifyDays != null && Array.isArray(notifyDays)) {
|
if (Array.isArray(notifyDays)) {
|
||||||
for (const day of notifyDays) {
|
for (const targetDays of notifyDays) {
|
||||||
log.debug("monitor", "call sendCertNotificationByTargetDays", day);
|
let certInfo = tlsInfoObject.certInfo;
|
||||||
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, day, notificationList);
|
while (certInfo) {
|
||||||
|
let subjectCN = certInfo.subject["CN"];
|
||||||
|
if (certInfo.daysRemaining > targetDays) {
|
||||||
|
log.debug("monitor", `No need to send cert notification for ${certInfo.certType} certificate "${subjectCN}" (${certInfo.daysRemaining} days valid) on ${targetDays} deadline.`);
|
||||||
|
} else {
|
||||||
|
log.debug("monitor", `call sendCertNotificationByTargetDays for ${targetDays} deadline on certificate ${subjectCN}.`);
|
||||||
|
await this.sendCertNotificationByTargetDays(subjectCN, certInfo.certType, certInfo.daysRemaining, targetDays, notificationList);
|
||||||
|
}
|
||||||
|
certInfo = certInfo.issuerCertificate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1238,55 +1253,47 @@ class Monitor extends BeanModel {
|
||||||
/**
|
/**
|
||||||
* Send a certificate notification when certificate expires in less
|
* Send a certificate notification when certificate expires in less
|
||||||
* than target days
|
* than target days
|
||||||
* @param {number} daysRemaining Number of days remaining on certifcate
|
* @param {string} certCN Common Name attribute from the certificate subject
|
||||||
|
* @param {string} certType certificate type
|
||||||
|
* @param {number} daysRemaining Number of days remaining on certificate
|
||||||
* @param {number} targetDays Number of days to alert after
|
* @param {number} targetDays Number of days to alert after
|
||||||
* @param {LooseObject<any>[]} notificationList List of notification providers
|
* @param {LooseObject<any>[]} notificationList List of notification providers
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) {
|
async sendCertNotificationByTargetDays(certCN, certType, daysRemaining, targetDays, notificationList) {
|
||||||
|
|
||||||
if (daysRemaining > targetDays) {
|
let row = await R.getRow("SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days <= ?", [
|
||||||
log.debug("monitor", `No need to send cert notification. ${daysRemaining} > ${targetDays}`);
|
"certificate",
|
||||||
|
this.id,
|
||||||
|
targetDays,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Sent already, no need to send again
|
||||||
|
if (row) {
|
||||||
|
log.debug("monitor", "Sent already, no need to send again");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notificationList.length > 0) {
|
let sent = false;
|
||||||
|
log.debug("monitor", "Send certificate notification");
|
||||||
|
|
||||||
let row = await R.getRow("SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days <= ?", [
|
for (let notification of notificationList) {
|
||||||
|
try {
|
||||||
|
log.debug("monitor", "Sending to " + notification.name);
|
||||||
|
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] ${certType} certificate ${certCN} will be expired in ${daysRemaining} days`);
|
||||||
|
sent = true;
|
||||||
|
} catch (e) {
|
||||||
|
log.error("monitor", "Cannot send cert notification to " + notification.name);
|
||||||
|
log.error("monitor", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sent) {
|
||||||
|
await R.exec("INSERT INTO notification_sent_history (type, monitor_id, days) VALUES(?, ?, ?)", [
|
||||||
"certificate",
|
"certificate",
|
||||||
this.id,
|
this.id,
|
||||||
targetDays,
|
targetDays,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Sent already, no need to send again
|
|
||||||
if (row) {
|
|
||||||
log.debug("monitor", "Sent already, no need to send again");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sent = false;
|
|
||||||
log.debug("monitor", "Send certificate notification");
|
|
||||||
|
|
||||||
for (let notification of notificationList) {
|
|
||||||
try {
|
|
||||||
log.debug("monitor", "Sending to " + notification.name);
|
|
||||||
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will expire in ${daysRemaining} days`);
|
|
||||||
sent = true;
|
|
||||||
} catch (e) {
|
|
||||||
log.error("monitor", "Cannot send cert notification to " + notification.name);
|
|
||||||
log.error("monitor", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sent) {
|
|
||||||
await R.exec("INSERT INTO notification_sent_history (type, monitor_id, days) VALUES(?, ?, ?)", [
|
|
||||||
"certificate",
|
|
||||||
this.id,
|
|
||||||
targetDays,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.debug("monitor", "No notification, no need to send cert notification");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,8 +59,8 @@ class Discord extends NotificationProvider {
|
||||||
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
|
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Time (UTC)",
|
name: `Time (${heartbeatJSON["timezone"]})`,
|
||||||
value: heartbeatJSON["time"],
|
value: heartbeatJSON["localDateTime"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Error",
|
name: "Error",
|
||||||
|
@ -94,8 +94,8 @@ class Discord extends NotificationProvider {
|
||||||
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
|
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Time (UTC)",
|
name: `Time (${heartbeatJSON["timezone"]})`,
|
||||||
value: heartbeatJSON["time"],
|
value: heartbeatJSON["localDateTime"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ping",
|
name: "Ping",
|
||||||
|
|
|
@ -35,8 +35,7 @@ class Feishu extends NotificationProvider {
|
||||||
text:
|
text:
|
||||||
"[Down] " +
|
"[Down] " +
|
||||||
heartbeatJSON["msg"] +
|
heartbeatJSON["msg"] +
|
||||||
"\nTime (UTC): " +
|
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
|
||||||
heartbeatJSON["time"],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -62,8 +61,7 @@ class Feishu extends NotificationProvider {
|
||||||
text:
|
text:
|
||||||
"[Up] " +
|
"[Up] " +
|
||||||
heartbeatJSON["msg"] +
|
heartbeatJSON["msg"] +
|
||||||
"\nTime (UTC): " +
|
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
|
||||||
heartbeatJSON["time"],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
|
@ -33,7 +33,10 @@ class Line extends NotificationProvider {
|
||||||
"messages": [
|
"messages": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"text": "UptimeKuma Alert: [🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
|
"text": "UptimeKuma Alert: [🔴 Down]\n" +
|
||||||
|
"Name: " + monitorJSON["name"] + " \n" +
|
||||||
|
heartbeatJSON["msg"] +
|
||||||
|
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -44,7 +47,10 @@ class Line extends NotificationProvider {
|
||||||
"messages": [
|
"messages": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"text": "UptimeKuma Alert: [✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
|
"text": "UptimeKuma Alert: [✅ Up]\n" +
|
||||||
|
"Name: " + monitorJSON["name"] + " \n" +
|
||||||
|
heartbeatJSON["msg"] +
|
||||||
|
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,12 +24,18 @@ class LineNotify extends NotificationProvider {
|
||||||
await axios.post(lineAPIUrl, qs.stringify(testMessage), config);
|
await axios.post(lineAPIUrl, qs.stringify(testMessage), config);
|
||||||
} else if (heartbeatJSON["status"] === DOWN) {
|
} else if (heartbeatJSON["status"] === DOWN) {
|
||||||
let downMessage = {
|
let downMessage = {
|
||||||
"message": "\n[🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
|
"message": "\n[🔴 Down]\n" +
|
||||||
|
"Name: " + monitorJSON["name"] + " \n" +
|
||||||
|
heartbeatJSON["msg"] + "\n" +
|
||||||
|
`Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
|
||||||
};
|
};
|
||||||
await axios.post(lineAPIUrl, qs.stringify(downMessage), config);
|
await axios.post(lineAPIUrl, qs.stringify(downMessage), config);
|
||||||
} else if (heartbeatJSON["status"] === UP) {
|
} else if (heartbeatJSON["status"] === UP) {
|
||||||
let upMessage = {
|
let upMessage = {
|
||||||
"message": "\n[✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
|
"message": "\n[✅ Up]\n" +
|
||||||
|
"Name: " + monitorJSON["name"] + " \n" +
|
||||||
|
heartbeatJSON["msg"] + "\n" +
|
||||||
|
`Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
|
||||||
};
|
};
|
||||||
await axios.post(lineAPIUrl, qs.stringify(upMessage), config);
|
await axios.post(lineAPIUrl, qs.stringify(upMessage), config);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,9 @@ class LunaSea extends NotificationProvider {
|
||||||
if (heartbeatJSON["status"] === DOWN) {
|
if (heartbeatJSON["status"] === DOWN) {
|
||||||
let downdata = {
|
let downdata = {
|
||||||
"title": "UptimeKuma Alert: " + monitorJSON["name"],
|
"title": "UptimeKuma Alert: " + monitorJSON["name"],
|
||||||
"body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
|
"body": "[🔴 Down] " +
|
||||||
|
heartbeatJSON["msg"] +
|
||||||
|
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
|
||||||
};
|
};
|
||||||
await axios.post(lunaseaurl, downdata);
|
await axios.post(lunaseaurl, downdata);
|
||||||
return okMsg;
|
return okMsg;
|
||||||
|
@ -37,7 +39,9 @@ class LunaSea extends NotificationProvider {
|
||||||
if (heartbeatJSON["status"] === UP) {
|
if (heartbeatJSON["status"] === UP) {
|
||||||
let updata = {
|
let updata = {
|
||||||
"title": "UptimeKuma Alert: " + monitorJSON["name"],
|
"title": "UptimeKuma Alert: " + monitorJSON["name"],
|
||||||
"body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
|
"body": "[✅ Up] " +
|
||||||
|
heartbeatJSON["msg"] +
|
||||||
|
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
|
||||||
};
|
};
|
||||||
await axios.post(lunaseaurl, updata);
|
await axios.post(lunaseaurl, updata);
|
||||||
return okMsg;
|
return okMsg;
|
||||||
|
|
|
@ -88,8 +88,8 @@ class Mattermost extends NotificationProvider {
|
||||||
statusField,
|
statusField,
|
||||||
{
|
{
|
||||||
short: true,
|
short: true,
|
||||||
title: "Time (UTC)",
|
title: `Time (${heartbeatJSON["timezone"]})`,
|
||||||
value: heartbeatJSON.time,
|
value: heartbeatJSON.localDateTime,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,14 +29,18 @@ class Pushbullet extends NotificationProvider {
|
||||||
let downData = {
|
let downData = {
|
||||||
"type": "note",
|
"type": "note",
|
||||||
"title": "UptimeKuma Alert: " + monitorJSON["name"],
|
"title": "UptimeKuma Alert: " + monitorJSON["name"],
|
||||||
"body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
|
"body": "[🔴 Down] " +
|
||||||
|
heartbeatJSON["msg"] +
|
||||||
|
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
|
||||||
};
|
};
|
||||||
await axios.post(pushbulletUrl, downData, config);
|
await axios.post(pushbulletUrl, downData, config);
|
||||||
} else if (heartbeatJSON["status"] === UP) {
|
} else if (heartbeatJSON["status"] === UP) {
|
||||||
let upData = {
|
let upData = {
|
||||||
"type": "note",
|
"type": "note",
|
||||||
"title": "UptimeKuma Alert: " + monitorJSON["name"],
|
"title": "UptimeKuma Alert: " + monitorJSON["name"],
|
||||||
"body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
|
"body": "[✅ Up] " +
|
||||||
|
heartbeatJSON["msg"] +
|
||||||
|
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
|
||||||
};
|
};
|
||||||
await axios.post(pushbulletUrl, upData, config);
|
await axios.post(pushbulletUrl, upData, config);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,16 @@ class Pushover extends NotificationProvider {
|
||||||
if (notification.pushoverdevice) {
|
if (notification.pushoverdevice) {
|
||||||
data.device = notification.pushoverdevice;
|
data.device = notification.pushoverdevice;
|
||||||
}
|
}
|
||||||
|
if (notification.pushoverttl) {
|
||||||
|
data.ttl = notification.pushoverttl;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (heartbeatJSON == null) {
|
if (heartbeatJSON == null) {
|
||||||
await axios.post(pushoverlink, data);
|
await axios.post(pushoverlink, data);
|
||||||
return okMsg;
|
return okMsg;
|
||||||
} else {
|
} else {
|
||||||
data.message += "\n<b>Time (UTC)</b>:" + heartbeatJSON["time"];
|
data.message += `\n<b>Time (${heartbeatJSON["timezone"]})</b>:${heartbeatJSON["localDateTime"]}`;
|
||||||
await axios.post(pushoverlink, data);
|
await axios.post(pushoverlink, data);
|
||||||
return okMsg;
|
return okMsg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,6 @@ class RocketChat extends NotificationProvider {
|
||||||
return okMsg;
|
return okMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
const time = heartbeatJSON["time"];
|
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
"text": "Uptime Kuma Alert",
|
"text": "Uptime Kuma Alert",
|
||||||
"channel": notification.rocketchannel,
|
"channel": notification.rocketchannel,
|
||||||
|
@ -31,7 +29,7 @@ class RocketChat extends NotificationProvider {
|
||||||
"icon_emoji": notification.rocketiconemo,
|
"icon_emoji": notification.rocketiconemo,
|
||||||
"attachments": [
|
"attachments": [
|
||||||
{
|
{
|
||||||
"title": "Uptime Kuma Alert *Time (UTC)*\n" + time,
|
"title": `Uptime Kuma Alert *Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`,
|
||||||
"text": "*Message*\n" + msg,
|
"text": "*Message*\n" + msg,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -44,7 +44,6 @@ class Slack extends NotificationProvider {
|
||||||
return okMsg;
|
return okMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
const time = heartbeatJSON["time"];
|
|
||||||
const textMsg = "Uptime Kuma Alert";
|
const textMsg = "Uptime Kuma Alert";
|
||||||
let data = {
|
let data = {
|
||||||
"text": `${textMsg}\n${msg}`,
|
"text": `${textMsg}\n${msg}`,
|
||||||
|
@ -70,7 +69,7 @@ class Slack extends NotificationProvider {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "mrkdwn",
|
"type": "mrkdwn",
|
||||||
"text": "*Time (UTC)*\n" + time,
|
"text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`,
|
||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -91,7 +91,7 @@ class SMTP extends NotificationProvider {
|
||||||
|
|
||||||
let bodyTextContent = msg;
|
let bodyTextContent = msg;
|
||||||
if (heartbeatJSON) {
|
if (heartbeatJSON) {
|
||||||
bodyTextContent = `${msg}\nTime (UTC): ${heartbeatJSON["time"]}`;
|
bodyTextContent = `${msg}\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// send mail with defined transport object
|
// send mail with defined transport object
|
||||||
|
|
|
@ -19,6 +19,11 @@ const nodeVersion = parseInt(process.versions.node.split(".")[0]);
|
||||||
const requiredVersion = 14;
|
const requiredVersion = 14;
|
||||||
console.log(`Your Node.js version: ${nodeVersion}`);
|
console.log(`Your Node.js version: ${nodeVersion}`);
|
||||||
|
|
||||||
|
// See more: https://github.com/louislam/uptime-kuma/issues/3138
|
||||||
|
if (nodeVersion >= 20) {
|
||||||
|
console.warn("\x1b[31m%s\x1b[0m", "Warning: Uptime Kuma is currently not stable on Node.js >= 20, please use Node.js 18.");
|
||||||
|
}
|
||||||
|
|
||||||
if (nodeVersion < requiredVersion) {
|
if (nodeVersion < requiredVersion) {
|
||||||
console.error(`Error: Your Node.js version is not supported, please upgrade to Node.js >= ${requiredVersion}.`);
|
console.error(`Error: Your Node.js version is not supported, please upgrade to Node.js >= ${requiredVersion}.`);
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
|
|
|
@ -519,12 +519,16 @@ const parseCertificateInfo = function (info) {
|
||||||
|
|
||||||
// Move up the chain until loop is encountered
|
// Move up the chain until loop is encountered
|
||||||
if (link.issuerCertificate == null) {
|
if (link.issuerCertificate == null) {
|
||||||
|
link.certType = (i === 0) ? "self-signed" : "root CA";
|
||||||
break;
|
break;
|
||||||
} else if (link.issuerCertificate.fingerprint in existingList) {
|
} else if (link.issuerCertificate.fingerprint in existingList) {
|
||||||
|
// a root CA certificate is typically "signed by itself" (=> "self signed certificate") and thus the "issuerCertificate" is a reference to itself.
|
||||||
log.debug("cert", `[Last] ${link.issuerCertificate.fingerprint}`);
|
log.debug("cert", `[Last] ${link.issuerCertificate.fingerprint}`);
|
||||||
|
link.certType = (i === 0) ? "self-signed" : "root CA";
|
||||||
link.issuerCertificate = null;
|
link.issuerCertificate = null;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
link.certType = (i === 0) ? "server" : "intermediate CA";
|
||||||
link = link.issuerCertificate;
|
link = link.issuerCertificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,15 +48,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button
|
<button
|
||||||
id="monitor-submit-btn" class="btn btn-primary" type="submit"
|
id="monitor-submit-btn" class="btn btn-primary" type="submit"
|
||||||
:disabled="processing"
|
:disabled="processing"
|
||||||
>
|
>
|
||||||
{{ $t("Generate") }}
|
{{ $t("Generate") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,299 @@
|
||||||
|
<template>
|
||||||
|
<div ref="BadgeGeneratorModal" class="modal fade" tabindex="-1" data-bs-backdrop="static">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">
|
||||||
|
{{ $t("Badge Generator", [monitor.name]) }}
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" />
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="type" class="form-label">{{ $t("Badge Type") }}</label>
|
||||||
|
<select id="type" v-model="badge.type" class="form-select">
|
||||||
|
<option value="status">status</option>
|
||||||
|
<option value="uptime">uptime</option>
|
||||||
|
<option value="ping">ping</option>
|
||||||
|
<option value="avg-response">avg-response</option>
|
||||||
|
<option value="cert-exp">cert-exp</option>
|
||||||
|
<option value="response">response</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('duration') " class="mb-3">
|
||||||
|
<label for="duration" class="form-label">{{ $t("Badge Duration") }}</label>
|
||||||
|
<input id="duration" v-model="badge.duration" type="number" min="0" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('label') " class="mb-3">
|
||||||
|
<label for="label" class="form-label">{{ $t("Badge Label") }}</label>
|
||||||
|
<input id="label" v-model="badge.label" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('prefix') " class="mb-3">
|
||||||
|
<label for="prefix" class="form-label">{{ $t("Badge Prefix") }}</label>
|
||||||
|
<input id="prefix" v-model="badge.prefix" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('suffix') " class="mb-3">
|
||||||
|
<label for="suffix" class="form-label">{{ $t("Badge Suffix") }}</label>
|
||||||
|
<input id="suffix" v-model="badge.suffix" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('labelColor') " class="mb-3">
|
||||||
|
<label for="labelColor" class="form-label">{{ $t("Badge Label Color") }}</label>
|
||||||
|
<input id="labelColor" v-model="badge.labelColor" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('color') " class="mb-3">
|
||||||
|
<label for="color" class="form-label">{{ $t("Badge Color") }}</label>
|
||||||
|
<input id="color" v-model="badge.color" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('labelPrefix') " class="mb-3">
|
||||||
|
<label for="labelPrefix" class="form-label">{{ $t("Badge Label Prefix") }}</label>
|
||||||
|
<input id="labelPrefix" v-model="badge.labelPrefix" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('labelSuffix') " class="mb-3">
|
||||||
|
<label for="labelSuffix" class="form-label">{{ $t("Badge Label Suffix") }}</label>
|
||||||
|
<input id="labelSuffix" v-model="badge.labelSuffix" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('upColor') " class="mb-3">
|
||||||
|
<label for="upColor" class="form-label">{{ $t("Badge Up Color") }}</label>
|
||||||
|
<input id="upColor" v-model="badge.upColor" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('downColor') " class="mb-3">
|
||||||
|
<label for="downColor" class="form-label">{{ $t("Badge Down Color") }}</label>
|
||||||
|
<input id="downColor" v-model="badge.downColor" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('pendingColor') " class="mb-3">
|
||||||
|
<label for="pendingColor" class="form-label">{{ $t("Badge Pending Color") }}</label>
|
||||||
|
<input id="pendingColor" v-model="badge.pendingColor" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('maintenanceColor') " class="mb-3">
|
||||||
|
<label for="maintenanceColor" class="form-label">{{ $t("Badge Maintenance Color") }}</label>
|
||||||
|
<input id="maintenanceColor" v-model="badge.maintenanceColor" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('warnColor') " class="mb-3">
|
||||||
|
<label for="warnColor" class="form-label">{{ $t("Badge Warn Color") }}</label>
|
||||||
|
<input id="warnColor" v-model="badge.warnColor" type="number" min="0" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('warnDays') " class="mb-3">
|
||||||
|
<label for="warnDays" class="form-label">{{ $t("Badge Warn Days") }}</label>
|
||||||
|
<input id="warnDays" v-model="badge.warnDays" type="number" min="0" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if=" (parameters[badge.type || 'null'] || [] ).includes('downDays') " class="mb-3">
|
||||||
|
<label for="downDays" class="form-label">{{ $t("Badge Down Days") }}</label>
|
||||||
|
<input id="downDays" v-model="badge.downDays" type="number" min="0" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="style" class="form-label">{{ $t("Badge Style") }}</label>
|
||||||
|
<select id="style" v-model="badge.style" class="form-select">
|
||||||
|
<option value="plastic">plastic</option>
|
||||||
|
<option value="flat">flat</option>
|
||||||
|
<option value="flat-square">flat-square</option>
|
||||||
|
<option value="for-the-badge">for-the-badge</option>
|
||||||
|
<option value="social">social</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="value" class="form-label">{{ $t("Badge value (For Testing only.)") }}</label>
|
||||||
|
<input id="value" v-model="badge.value" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="push-url" class="form-label">{{ $t("Badge URL") }}</label>
|
||||||
|
<CopyableInput id="push-url" v-model="badgeURL" type="url" disabled="disabled" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-danger" data-bs-dismiss="modal">
|
||||||
|
{{ $t("Close") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Modal } from "bootstrap";
|
||||||
|
import CopyableInput from "./CopyableInput.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CopyableInput
|
||||||
|
},
|
||||||
|
props: {},
|
||||||
|
emits: [],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
model: null,
|
||||||
|
processing: false,
|
||||||
|
monitor: {
|
||||||
|
id: null,
|
||||||
|
name: null,
|
||||||
|
},
|
||||||
|
badge: {
|
||||||
|
type: "status",
|
||||||
|
duration: null,
|
||||||
|
label: null,
|
||||||
|
prefix: null,
|
||||||
|
suffix: null,
|
||||||
|
labelColor: null,
|
||||||
|
color: null,
|
||||||
|
labelPrefix: null,
|
||||||
|
labelSuffix: null,
|
||||||
|
upColor: null,
|
||||||
|
downColor: null,
|
||||||
|
pendingColor: null,
|
||||||
|
maintenanceColor: null,
|
||||||
|
warnColor: null,
|
||||||
|
warnDays: null,
|
||||||
|
downDays: null,
|
||||||
|
style: "flat",
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
status: [
|
||||||
|
"upLabel",
|
||||||
|
"downLabel",
|
||||||
|
"pendingLabel",
|
||||||
|
"maintenanceLabel",
|
||||||
|
"upColor",
|
||||||
|
"downColor",
|
||||||
|
"pendingColor",
|
||||||
|
"maintenanceColor",
|
||||||
|
],
|
||||||
|
uptime: [
|
||||||
|
"duration",
|
||||||
|
"labelPrefix",
|
||||||
|
"labelSuffix",
|
||||||
|
"prefix",
|
||||||
|
"suffix",
|
||||||
|
"color",
|
||||||
|
"labelColor",
|
||||||
|
],
|
||||||
|
ping: [
|
||||||
|
"duration",
|
||||||
|
"labelPrefix",
|
||||||
|
"labelSuffix",
|
||||||
|
"prefix",
|
||||||
|
"suffix",
|
||||||
|
"color",
|
||||||
|
"labelColor",
|
||||||
|
],
|
||||||
|
"avg-response": [
|
||||||
|
"duration",
|
||||||
|
"labelPrefix",
|
||||||
|
"labelSuffix",
|
||||||
|
"prefix",
|
||||||
|
"suffix",
|
||||||
|
"color",
|
||||||
|
"labelColor",
|
||||||
|
],
|
||||||
|
"cert-exp": [
|
||||||
|
"labelPrefix",
|
||||||
|
"labelSuffix",
|
||||||
|
"prefix",
|
||||||
|
"suffix",
|
||||||
|
"upColor",
|
||||||
|
"warnColor",
|
||||||
|
"downColor",
|
||||||
|
"warnDays",
|
||||||
|
"downDays",
|
||||||
|
"labelColor",
|
||||||
|
],
|
||||||
|
response: [
|
||||||
|
"labelPrefix",
|
||||||
|
"labelSuffix",
|
||||||
|
"prefix",
|
||||||
|
"suffix",
|
||||||
|
"color",
|
||||||
|
"labelColor",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
badgeURL() {
|
||||||
|
if (!this.monitor.id || !this.badge.type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let badgeURL = this.$root.baseURL + "/api/badge/" + this.monitor.id + "/" + this.badge.type;
|
||||||
|
|
||||||
|
let parameterList = {};
|
||||||
|
|
||||||
|
for (let parameter of this.parameters[this.badge.type] || []) {
|
||||||
|
if (parameter === "duration" && this.badge.duration) {
|
||||||
|
badgeURL += "/" + this.badge.duration;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.badge[parameter]) {
|
||||||
|
parameterList[parameter] = this.badge[parameter];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let parameter of [ "label", "style", "value" ]) {
|
||||||
|
if (parameter === "style" && this.badge.style === "flat") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.badge[parameter]) {
|
||||||
|
parameterList[parameter] = this.badge[parameter];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(parameterList).length > 0) {
|
||||||
|
return badgeURL + "?" + new URLSearchParams(parameterList);
|
||||||
|
}
|
||||||
|
|
||||||
|
return badgeURL;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.BadgeGeneratorModal = new Modal(this.$refs.BadgeGeneratorModal);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Setting monitor
|
||||||
|
* @param {number} monitorId ID of monitor
|
||||||
|
* @param {string} monitorName Name of monitor
|
||||||
|
*/
|
||||||
|
show(monitorId, monitorName) {
|
||||||
|
this.monitor = {
|
||||||
|
id: monitorId,
|
||||||
|
name: monitorName,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.BadgeGeneratorModal.show();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "../assets/vars.scss";
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
.modal-dialog .form-text, .modal-dialog p {
|
||||||
|
color: $dark-font-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,123 @@
|
||||||
|
<template>
|
||||||
|
<div ref="MonitorSettingDialog" class="modal fade" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">
|
||||||
|
{{ $t("Monitor Setting", [monitor.name]) }}
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" />
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="my-3 form-check">
|
||||||
|
<input id="show-clickable-link" v-model="monitor.isClickAble" class="form-check-input" type="checkbox" @click="toggleLink(monitor.group_index, monitor.monitor_index)" />
|
||||||
|
<label class="form-check-label" for="show-clickable-link">
|
||||||
|
{{ $t("Show Clickable Link") }}
|
||||||
|
</label>
|
||||||
|
<div class="form-text">
|
||||||
|
{{ $t("Show Clickable Link Description") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-add-group me-2"
|
||||||
|
@click="$refs.badgeGeneratorDialog.show(monitor.id, monitor.name)"
|
||||||
|
>
|
||||||
|
<font-awesome-icon icon="certificate" />
|
||||||
|
{{ $t("Open Badge Generator") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-danger" data-bs-dismiss="modal">
|
||||||
|
{{ $t("Close") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<BadgeGeneratorDialog ref="badgeGeneratorDialog" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Modal } from "bootstrap";
|
||||||
|
import BadgeGeneratorDialog from "./BadgeGeneratorDialog.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BadgeGeneratorDialog
|
||||||
|
},
|
||||||
|
props: {},
|
||||||
|
emits: [],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
monitor: {
|
||||||
|
id: null,
|
||||||
|
name: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.MonitorSettingDialog = new Modal(this.$refs.MonitorSettingDialog);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Setting monitor
|
||||||
|
* @param {Object} group Data of monitor
|
||||||
|
* @param {Object} monitor Data of monitor
|
||||||
|
*/
|
||||||
|
show(group, monitor) {
|
||||||
|
this.monitor = {
|
||||||
|
id: monitor.element.id,
|
||||||
|
name: monitor.element.name,
|
||||||
|
monitor_index: monitor.index,
|
||||||
|
group_index: group.index,
|
||||||
|
isClickAble: this.showLink(monitor),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.MonitorSettingDialog.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the value of sendUrl
|
||||||
|
* @param {number} groupIndex Index of group monitor is member of
|
||||||
|
* @param {number} index Index of monitor within group
|
||||||
|
*/
|
||||||
|
toggleLink(groupIndex, index) {
|
||||||
|
this.$root.publicGroupList[groupIndex].monitorList[index].sendUrl = !this.$root.publicGroupList[groupIndex].monitorList[index].sendUrl;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should a link to the monitor be shown?
|
||||||
|
* Attempts to guess if a link should be shown based upon if
|
||||||
|
* sendUrl is set and if the URL is default or not.
|
||||||
|
* @param {Object} monitor Monitor to check
|
||||||
|
* @param {boolean} [ignoreSendUrl=false] Should the presence of the sendUrl
|
||||||
|
* property be ignored. This will only work in edit mode.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
showLink(monitor, ignoreSendUrl = false) {
|
||||||
|
// We must check if there are any elements in monitorList to
|
||||||
|
// prevent undefined errors if it hasn't been loaded yet
|
||||||
|
if (this.$parent.editMode && ignoreSendUrl && Object.keys(this.$root.monitorList).length) {
|
||||||
|
return this.$root.monitorList[monitor.element.id].type === "http" || this.$root.monitorList[monitor.element.id].type === "keyword";
|
||||||
|
}
|
||||||
|
return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://" && !this.editMode;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "../assets/vars.scss";
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
.modal-dialog .form-text, .modal-dialog p {
|
||||||
|
color: $dark-font-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -49,16 +49,15 @@
|
||||||
{{ monitor.element.name }}
|
{{ monitor.element.name }}
|
||||||
</a>
|
</a>
|
||||||
<p v-else class="item-name"> {{ monitor.element.name }} </p>
|
<p v-else class="item-name"> {{ monitor.element.name }} </p>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="showLink(monitor, true)"
|
title="Setting"
|
||||||
title="Toggle Clickable Link"
|
|
||||||
>
|
>
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
v-if="editMode"
|
v-if="editMode"
|
||||||
:class="{'link-active': monitor.element.sendUrl, 'btn-link': true}"
|
:class="{'link-active': true, 'btn-link': true}"
|
||||||
icon="link" class="action me-3"
|
icon="cog" class="action me-3"
|
||||||
|
@click="$refs.monitorSettingDialog.show(group, monitor)"
|
||||||
@click="toggleLink(group.index, monitor.index)"
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,9 +76,11 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
<MonitorSettingDialog ref="monitorSettingDialog" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import MonitorSettingDialog from "./MonitorSettingDialog.vue";
|
||||||
import Draggable from "vuedraggable";
|
import Draggable from "vuedraggable";
|
||||||
import HeartbeatBar from "./HeartbeatBar.vue";
|
import HeartbeatBar from "./HeartbeatBar.vue";
|
||||||
import Uptime from "./Uptime.vue";
|
import Uptime from "./Uptime.vue";
|
||||||
|
@ -87,6 +88,7 @@ import Tag from "./Tag.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
MonitorSettingDialog,
|
||||||
Draggable,
|
Draggable,
|
||||||
HeartbeatBar,
|
HeartbeatBar,
|
||||||
Uptime,
|
Uptime,
|
||||||
|
@ -135,15 +137,6 @@ export default {
|
||||||
this.$root.publicGroupList[groupIndex].monitorList.splice(index, 1);
|
this.$root.publicGroupList[groupIndex].monitorList.splice(index, 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle the value of sendUrl
|
|
||||||
* @param {number} groupIndex Index of group monitor is member of
|
|
||||||
* @param {number} index Index of monitor within group
|
|
||||||
*/
|
|
||||||
toggleLink(groupIndex, index) {
|
|
||||||
this.$root.publicGroupList[groupIndex].monitorList[index].sendUrl = !this.$root.publicGroupList[groupIndex].monitorList[index].sendUrl;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should a link to the monitor be shown?
|
* Should a link to the monitor be shown?
|
||||||
* Attempts to guess if a link should be shown based upon if
|
* Attempts to guess if a link should be shown based upon if
|
||||||
|
|
|
@ -42,6 +42,8 @@
|
||||||
<option value="vibrate">{{ $t("pushoversounds vibrate") }}</option>
|
<option value="vibrate">{{ $t("pushoversounds vibrate") }}</option>
|
||||||
<option value="none">{{ $t("pushoversounds none") }}</option>
|
<option value="none">{{ $t("pushoversounds none") }}</option>
|
||||||
</select>
|
</select>
|
||||||
|
<label for="pushover-ttl" class="form-label">{{ $t("pushoverMessageTtl") }}</label>
|
||||||
|
<input id="pushover-ttl" v-model="$parent.notification.pushoverttl" type="number" min="0" step="1" class="form-control">
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
|
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
|
||||||
<i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
|
<i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
|
||||||
|
|
|
@ -49,6 +49,7 @@ import {
|
||||||
faFilter,
|
faFilter,
|
||||||
faInfoCircle,
|
faInfoCircle,
|
||||||
faClone,
|
faClone,
|
||||||
|
faCertificate,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
|
@ -95,6 +96,7 @@ library.add(
|
||||||
faFilter,
|
faFilter,
|
||||||
faInfoCircle,
|
faInfoCircle,
|
||||||
faClone,
|
faClone,
|
||||||
|
faCertificate,
|
||||||
);
|
);
|
||||||
|
|
||||||
export { FontAwesomeIcon };
|
export { FontAwesomeIcon };
|
||||||
|
|
|
@ -259,6 +259,7 @@
|
||||||
"More info on:": "Mehr Infos auf: {0}",
|
"More info on:": "Mehr Infos auf: {0}",
|
||||||
"pushoverDesc1": "Notfallpriorität (2) hat standardmässig 30 Sekunden Auszeit zwischen den Versuchen und läuft nach 1 Stunde ab.",
|
"pushoverDesc1": "Notfallpriorität (2) hat standardmässig 30 Sekunden Auszeit zwischen den Versuchen und läuft nach 1 Stunde ab.",
|
||||||
"pushoverDesc2": "Fülle das Geräte Feld aus, wenn du Benachrichtigungen an verschiedene Geräte senden möchtest.",
|
"pushoverDesc2": "Fülle das Geräte Feld aus, wenn du Benachrichtigungen an verschiedene Geräte senden möchtest.",
|
||||||
|
"pushoverMessageTtl": "Message TTL (Sekunden)",
|
||||||
"SMS Type": "SMS Typ",
|
"SMS Type": "SMS Typ",
|
||||||
"octopushTypePremium": "Premium (Schnell - zur Benachrichtigung empfohlen)",
|
"octopushTypePremium": "Premium (Schnell - zur Benachrichtigung empfohlen)",
|
||||||
"octopushTypeLowCost": "Kostengünstig (Langsam - manchmal vom Betreiber gesperrt)",
|
"octopushTypeLowCost": "Kostengünstig (Langsam - manchmal vom Betreiber gesperrt)",
|
||||||
|
|
|
@ -259,6 +259,7 @@
|
||||||
"More info on:": "Mehr Infos auf: {0}",
|
"More info on:": "Mehr Infos auf: {0}",
|
||||||
"pushoverDesc1": "Notfallpriorität (2) hat standardmäßig 30 Sekunden Auszeit zwischen den Versuchen und läuft nach 1 Stunde ab.",
|
"pushoverDesc1": "Notfallpriorität (2) hat standardmäßig 30 Sekunden Auszeit zwischen den Versuchen und läuft nach 1 Stunde ab.",
|
||||||
"pushoverDesc2": "Fülle das Geräte Feld aus, wenn du Benachrichtigungen an verschiedene Geräte senden möchtest.",
|
"pushoverDesc2": "Fülle das Geräte Feld aus, wenn du Benachrichtigungen an verschiedene Geräte senden möchtest.",
|
||||||
|
"pushoverMessageTtl": "Message TTL (Sekunden)",
|
||||||
"SMS Type": "SMS Typ",
|
"SMS Type": "SMS Typ",
|
||||||
"octopushTypePremium": "Premium (Schnell - zur Benachrichtigung empfohlen)",
|
"octopushTypePremium": "Premium (Schnell - zur Benachrichtigung empfohlen)",
|
||||||
"octopushTypeLowCost": "Kostengünstig (Langsam - manchmal vom Betreiber gesperrt)",
|
"octopushTypeLowCost": "Kostengünstig (Langsam - manchmal vom Betreiber gesperrt)",
|
||||||
|
|
|
@ -556,6 +556,7 @@
|
||||||
"More info on:": "More info on: {0}",
|
"More info on:": "More info on: {0}",
|
||||||
"pushoverDesc1": "Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.",
|
"pushoverDesc1": "Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.",
|
||||||
"pushoverDesc2": "If you want to send notifications to different devices, fill out Device field.",
|
"pushoverDesc2": "If you want to send notifications to different devices, fill out Device field.",
|
||||||
|
"pushoverMessageTtl": "Message TTL (Seconds)",
|
||||||
"SMS Type": "SMS Type",
|
"SMS Type": "SMS Type",
|
||||||
"octopushTypePremium": "Premium (Fast - recommended for alerting)",
|
"octopushTypePremium": "Premium (Fast - recommended for alerting)",
|
||||||
"octopushTypeLowCost": "Low Cost (Slow - sometimes blocked by operator)",
|
"octopushTypeLowCost": "Low Cost (Slow - sometimes blocked by operator)",
|
||||||
|
@ -720,5 +721,29 @@
|
||||||
"twilioAccountSID": "Account SID",
|
"twilioAccountSID": "Account SID",
|
||||||
"twilioAuthToken": "Auth Token",
|
"twilioAuthToken": "Auth Token",
|
||||||
"twilioFromNumber": "From Number",
|
"twilioFromNumber": "From Number",
|
||||||
"twilioToNumber": "To Number"
|
"twilioToNumber": "To Number",
|
||||||
|
"Monitor Setting": "{0}'s Monitor Setting",
|
||||||
|
"Show Clickable Link": "Show Clickable Link",
|
||||||
|
"Show Clickable Link Description": "If checked everyone who have access to this status page can have access to monitor URL.",
|
||||||
|
"Open Badge Generator": "Open Badge Generator",
|
||||||
|
"Badge Generator": "{0}'s Badge Generator",
|
||||||
|
"Badge Type": "Badge Type",
|
||||||
|
"Badge Duration": "Badge Duration",
|
||||||
|
"Badge Label": "Badge Label",
|
||||||
|
"Badge Prefix": "Badge Prefix",
|
||||||
|
"Badge Suffix": "Badge Suffix",
|
||||||
|
"Badge Label Color": "Badge Label Color",
|
||||||
|
"Badge Color": "Badge Color",
|
||||||
|
"Badge Label Prefix": "Badge Label Prefix",
|
||||||
|
"Badge Label Suffix": "Badge Label Suffix",
|
||||||
|
"Badge Up Color": "Badge Up Color",
|
||||||
|
"Badge Down Color": "Badge Down Color",
|
||||||
|
"Badge Pending Color": "Badge Pending Color",
|
||||||
|
"Badge Maintenance Color": "Badge Maintenance Color",
|
||||||
|
"Badge Warn Color": "Badge Warn Color",
|
||||||
|
"Badge Warn Days": "Badge Warn Days",
|
||||||
|
"Badge Down Days": "Badge Down Days",
|
||||||
|
"Badge Style": "Badge Style",
|
||||||
|
"Badge value (For Testing only.)": "Badge value (For Testing only.)",
|
||||||
|
"Badge URL": "Badge URL"
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Mobile Only -->
|
<!-- Mobile Only -->
|
||||||
<div v-if="$root.isMobile" style="width: 100%; height: 60px;" />
|
<div v-if="$root.isMobile" style="width: 100%; height: calc(60px + env(safe-area-inset-bottom));" />
|
||||||
<nav v-if="$root.isMobile && $root.loggedIn" class="bottom-nav">
|
<nav v-if="$root.isMobile && $root.loggedIn" class="bottom-nav">
|
||||||
<router-link to="/dashboard" class="nav-link">
|
<router-link to="/dashboard" class="nav-link">
|
||||||
<div><font-awesome-icon icon="tachometer-alt" /></div>
|
<div><font-awesome-icon icon="tachometer-alt" /></div>
|
||||||
|
@ -182,14 +182,14 @@ export default {
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
height: 60px;
|
height: calc(60px + env(safe-area-inset-bottom));
|
||||||
width: 100%;
|
width: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-shadow: 0 15px 47px 0 rgba(0, 0, 0, 0.05), 0 5px 14px 0 rgba(0, 0, 0, 0.05);
|
box-shadow: 0 15px 47px 0 rgba(0, 0, 0, 0.05), 0 5px 14px 0 rgba(0, 0, 0, 0.05);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
padding: 0 10px;
|
padding: 0 10px env(safe-area-inset-bottom);
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<Tag v-for="tag in monitor.tags" :key="tag.id" :item="tag" :size="'sm'" />
|
<Tag v-for="tag in monitor.tags" :key="tag.id" :item="tag" :size="'sm'" />
|
||||||
</div>
|
</div>
|
||||||
<p class="url">
|
<p class="url">
|
||||||
<a v-if="monitor.type === 'http' || monitor.type === 'keyword' " :href="monitor.url" target="_blank" rel="noopener noreferrer">{{ monitor.url }}</a>
|
<a v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'mp-health' " :href="monitor.url" target="_blank" rel="noopener noreferrer">{{ filterPassword(monitor.url) }}</a>
|
||||||
<span v-if="monitor.type === 'port'">TCP Port {{ monitor.hostname }}:{{ monitor.port }}</span>
|
<span v-if="monitor.type === 'port'">TCP Port {{ monitor.hostname }}:{{ monitor.port }}</span>
|
||||||
<span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
|
<span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
|
||||||
<span v-if="monitor.type === 'keyword'">
|
<span v-if="monitor.type === 'keyword'">
|
||||||
|
@ -18,6 +18,21 @@
|
||||||
<br>
|
<br>
|
||||||
<span>{{ $t("Last Result") }}:</span> <span class="keyword">{{ monitor.dns_last_result }}</span>
|
<span>{{ $t("Last Result") }}:</span> <span class="keyword">{{ monitor.dns_last_result }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="monitor.type === 'docker'">Docker container: {{ monitor.docker_container }}</span>
|
||||||
|
<span v-if="monitor.type === 'gamedig'">Gamedig - {{ monitor.game }}: {{ monitor.hostname }}:{{ monitor.port }}</span>
|
||||||
|
<span v-if="monitor.type === 'grpc-keyword'">gRPC - {{ filterPassword(monitor.grpcUrl) }}
|
||||||
|
<br>
|
||||||
|
<span>{{ $t("Keyword") }}:</span> <span class="keyword">{{ monitor.keyword }}</span>
|
||||||
|
</span>
|
||||||
|
<span v-if="monitor.type === 'mongodb'">{{ filterPassword(monitor.databaseConnectionString) }}</span>
|
||||||
|
<span v-if="monitor.type === 'mqtt'">MQTT: {{ monitor.hostname }}:{{ monitor.port }}/{{ monitor.mqttTopic }}</span>
|
||||||
|
<span v-if="monitor.type === 'mysql'">{{ filterPassword(monitor.databaseConnectionString) }}</span>
|
||||||
|
<span v-if="monitor.type === 'postgres'">{{ filterPassword(monitor.databaseConnectionString) }}</span>
|
||||||
|
<span v-if="monitor.type === 'push'">Push: <a :href="pushURL" target="_blank" rel="noopener noreferrer">{{ pushURL }}</a></span>
|
||||||
|
<span v-if="monitor.type === 'radius'">Radius: {{ filterPassword(monitor.hostname) }}</span>
|
||||||
|
<span v-if="monitor.type === 'redis'">{{ filterPassword(monitor.databaseConnectionString) }}</span>
|
||||||
|
<span v-if="monitor.type === 'sqlserver'">SQL Server: {{ filterPassword(monitor.databaseConnectionString) }}</span>
|
||||||
|
<span v-if="monitor.type === 'steam'">Steam Game Server: {{ monitor.hostname }}:{{ monitor.port }}</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="functions">
|
<div class="functions">
|
||||||
|
@ -54,35 +69,41 @@
|
||||||
|
|
||||||
<div class="shadow-box big-padding text-center stats">
|
<div class="shadow-box big-padding text-center stats">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col-12 col-sm col row d-flex align-items-center d-sm-block">
|
||||||
<h4>{{ pingTitle() }}</h4>
|
<h4 class="col-4 col-sm-12">{{ pingTitle() }}</h4>
|
||||||
<p>({{ $t("Current") }})</p>
|
<p class="col-4 col-sm-12 mb-0 mb-sm-2">({{ $t("Current") }})</p>
|
||||||
<span class="num">
|
<span class="col-4 col-sm-12 num">
|
||||||
<a href="#" @click.prevent="showPingChartBox = !showPingChartBox">
|
<a href="#" @click.prevent="showPingChartBox = !showPingChartBox">
|
||||||
<CountUp :value="ping" />
|
<CountUp :value="ping" />
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col-12 col-sm col row d-flex align-items-center d-sm-block">
|
||||||
<h4>{{ pingTitle(true) }}</h4>
|
<h4 class="col-4 col-sm-12">{{ pingTitle(true) }}</h4>
|
||||||
<p>(24{{ $t("-hour") }})</p>
|
<p class="col-4 col-sm-12 mb-0 mb-sm-2">(24{{ $t("-hour") }})</p>
|
||||||
<span class="num"><CountUp :value="avgPing" /></span>
|
<span class="col-4 col-sm-12 num">
|
||||||
|
<CountUp :value="avgPing" />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col-12 col-sm col row d-flex align-items-center d-sm-block">
|
||||||
<h4>{{ $t("Uptime") }}</h4>
|
<h4 class="col-4 col-sm-12">{{ $t("Uptime") }}</h4>
|
||||||
<p>(24{{ $t("-hour") }})</p>
|
<p class="col-4 col-sm-12 mb-0 mb-sm-2">(24{{ $t("-hour") }})</p>
|
||||||
<span class="num"><Uptime :monitor="monitor" type="24" /></span>
|
<span class="col-4 col-sm-12 num">
|
||||||
|
<Uptime :monitor="monitor" type="24" />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col-12 col-sm col row d-flex align-items-center d-sm-block">
|
||||||
<h4>{{ $t("Uptime") }}</h4>
|
<h4 class="col-4 col-sm-12">{{ $t("Uptime") }}</h4>
|
||||||
<p>(30{{ $t("-day") }})</p>
|
<p class="col-4 col-sm-12 mb-0 mb-sm-2">(30{{ $t("-day") }})</p>
|
||||||
<span class="num"><Uptime :monitor="monitor" type="720" /></span>
|
<span class="col-4 col-sm-12 num">
|
||||||
|
<Uptime :monitor="monitor" type="720" />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="tlsInfo" class="col">
|
<div v-if="tlsInfo" class="col-12 col-sm col row d-flex align-items-center d-sm-block">
|
||||||
<h4>{{ $t("Cert Exp.") }}</h4>
|
<h4 class="col-4 col-sm-12">{{ $t("Cert Exp.") }}</h4>
|
||||||
<p>(<Datetime :value="tlsInfo.certInfo.validTo" date-only />)</p>
|
<p class="col-4 col-sm-12 mb-0 mb-sm-2">(<Datetime :value="tlsInfo.certInfo.validTo" date-only />)</p>
|
||||||
<span class="num">
|
<span class="col-4 col-sm-12 num">
|
||||||
<a href="#" @click.prevent="toggleCertInfoBox = !toggleCertInfoBox">{{ tlsInfo.certInfo.daysRemaining }} {{ $tc("day", tlsInfo.certInfo.daysRemaining) }}</a>
|
<a href="#" @click.prevent="toggleCertInfoBox = !toggleCertInfoBox">{{ tlsInfo.certInfo.daysRemaining }} {{ $tc("day", tlsInfo.certInfo.daysRemaining) }}</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -136,7 +157,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(beat, index) in displayedRecords" :key="index" :class="{ 'shadow-box': $root.windowWidth <= 550}" style="padding: 10px;">
|
<tr v-for="(beat, index) in displayedRecords" :key="index" style="padding: 10px;">
|
||||||
<td><Status :status="beat.status" /></td>
|
<td><Status :status="beat.status" /></td>
|
||||||
<td :class="{ 'border-0':! beat.msg}"><Datetime :value="beat.time" /></td>
|
<td :class="{ 'border-0':! beat.msg}"><Datetime :value="beat.time" /></td>
|
||||||
<td class="border-0">{{ beat.msg }}</td>
|
<td class="border-0">{{ beat.msg }}</td>
|
||||||
|
@ -193,6 +214,7 @@ import Pagination from "v-pagination-3";
|
||||||
const PingChart = defineAsyncComponent(() => import("../components/PingChart.vue"));
|
const PingChart = defineAsyncComponent(() => import("../components/PingChart.vue"));
|
||||||
import Tag from "../components/Tag.vue";
|
import Tag from "../components/Tag.vue";
|
||||||
import CertificateInfo from "../components/CertificateInfo.vue";
|
import CertificateInfo from "../components/CertificateInfo.vue";
|
||||||
|
import { URL } from "whatwg-url";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -290,6 +312,10 @@ export default {
|
||||||
const endIndex = startIndex + this.perPage;
|
const endIndex = startIndex + this.perPage;
|
||||||
return this.heartBeatList.slice(startIndex, endIndex);
|
return this.heartBeatList.slice(startIndex, endIndex);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
pushURL() {
|
||||||
|
return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?status=up&msg=OK&ping=";
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
||||||
|
@ -376,12 +402,26 @@ export default {
|
||||||
translationPrefix = "Avg. ";
|
translationPrefix = "Avg. ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.monitor.type === "http") {
|
if (this.monitor.type === "http" || this.monitor.type === "keyword") {
|
||||||
return this.$t(translationPrefix + "Response");
|
return this.$t(translationPrefix + "Response");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.$t(translationPrefix + "Ping");
|
return this.$t(translationPrefix + "Ping");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Filter and hide password in URL for display */
|
||||||
|
filterPassword(urlString) {
|
||||||
|
try {
|
||||||
|
let parsedUrl = new URL(urlString);
|
||||||
|
if (parsedUrl.password !== "") {
|
||||||
|
parsedUrl.password = "******";
|
||||||
|
}
|
||||||
|
return parsedUrl.toString();
|
||||||
|
} catch (e) {
|
||||||
|
// Handle SQL Server
|
||||||
|
return urlString.replaceAll(/Password=(.+);/ig, "Password=******;");
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -415,6 +455,7 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.btn {
|
a.btn {
|
||||||
|
@ -471,6 +512,18 @@ table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 550px) {
|
||||||
|
.stats {
|
||||||
|
.col {
|
||||||
|
margin: 10px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.keyword {
|
.keyword {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue