mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-28 01:04:05 +00:00
Merge branch 'master' of https://github.com/louislam/uptime-kuma
This commit is contained in:
commit
34ca5c5c15
6 changed files with 171 additions and 31 deletions
|
@ -4,7 +4,6 @@
|
||||||
"indentation": 4,
|
"indentation": 4,
|
||||||
"no-descending-specificity": null,
|
"no-descending-specificity": null,
|
||||||
"selector-list-comma-newline-after": null,
|
"selector-list-comma-newline-after": null,
|
||||||
"declaration-empty-line-before": null,
|
"declaration-empty-line-before": null
|
||||||
"no-duplicate-selectors": null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,6 +279,116 @@ class Notification {
|
||||||
throwGeneralAxiosError(error)
|
throwGeneralAxiosError(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (notification.type === "mattermost") {
|
||||||
|
try {
|
||||||
|
const mattermostUserName = notification.mattermostusername || "Uptime Kuma";
|
||||||
|
// If heartbeatJSON is null, assume we're testing.
|
||||||
|
if (heartbeatJSON == null) {
|
||||||
|
let mattermostTestData = {
|
||||||
|
username: mattermostUserName,
|
||||||
|
text: msg,
|
||||||
|
}
|
||||||
|
await axios.post(notification.mattermostWebhookUrl, mattermostTestData)
|
||||||
|
return okMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mattermostChannel = notification.mattermostchannel;
|
||||||
|
const mattermostIconEmoji = notification.mattermosticonemo;
|
||||||
|
const mattermostIconUrl = notification.mattermosticonurl;
|
||||||
|
|
||||||
|
if (heartbeatJSON["status"] == 0) {
|
||||||
|
let mattermostdowndata = {
|
||||||
|
username: mattermostUserName,
|
||||||
|
text: "Uptime Kuma Alert",
|
||||||
|
channel: mattermostChannel,
|
||||||
|
icon_emoji: mattermostIconEmoji,
|
||||||
|
icon_url: mattermostIconUrl,
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
fallback:
|
||||||
|
"Your " +
|
||||||
|
monitorJSON["name"] +
|
||||||
|
" service went down.",
|
||||||
|
color: "#FF0000",
|
||||||
|
title:
|
||||||
|
"❌ " +
|
||||||
|
monitorJSON["name"] +
|
||||||
|
" service went down. ❌",
|
||||||
|
title_link: monitorJSON["url"],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
short: true,
|
||||||
|
title: "Service Name",
|
||||||
|
value: monitorJSON["name"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
short: true,
|
||||||
|
title: "Time (UTC)",
|
||||||
|
value: heartbeatJSON["time"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
short: false,
|
||||||
|
title: "Error",
|
||||||
|
value: heartbeatJSON["msg"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
await axios.post(
|
||||||
|
notification.mattermostWebhookUrl,
|
||||||
|
mattermostdowndata
|
||||||
|
);
|
||||||
|
return okMsg;
|
||||||
|
} else if (heartbeatJSON["status"] == 1) {
|
||||||
|
let mattermostupdata = {
|
||||||
|
username: mattermostUserName,
|
||||||
|
text: "Uptime Kuma Alert",
|
||||||
|
channel: mattermostChannel,
|
||||||
|
icon_emoji: mattermostIconEmoji,
|
||||||
|
icon_url: mattermostIconUrl,
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
fallback:
|
||||||
|
"Your " +
|
||||||
|
monitorJSON["name"] +
|
||||||
|
" service went up!",
|
||||||
|
color: "#32CD32",
|
||||||
|
title:
|
||||||
|
"✅ " +
|
||||||
|
monitorJSON["name"] +
|
||||||
|
" service went up! ✅",
|
||||||
|
title_link: monitorJSON["url"],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
short: true,
|
||||||
|
title: "Service Name",
|
||||||
|
value: monitorJSON["name"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
short: true,
|
||||||
|
title: "Time (UTC)",
|
||||||
|
value: heartbeatJSON["time"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
short: false,
|
||||||
|
title: "Ping",
|
||||||
|
value: heartbeatJSON["ping"] + "ms",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
await axios.post(
|
||||||
|
notification.mattermostWebhookUrl,
|
||||||
|
mattermostupdata
|
||||||
|
);
|
||||||
|
return okMsg;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throwGeneralAxiosError(error);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (notification.type === "pushover") {
|
} else if (notification.type === "pushover") {
|
||||||
let pushoverlink = "https://api.pushover.net/1/messages.json"
|
let pushoverlink = "https://api.pushover.net/1/messages.json"
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -163,10 +163,6 @@ export default {
|
||||||
|
|
||||||
&.empty {
|
&.empty {
|
||||||
background-color: aliceblue;
|
background-color: aliceblue;
|
||||||
|
|
||||||
.dark & {
|
|
||||||
background-color: #d0d3d5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.down {
|
&.down {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
<option value="apprise">Apprise (Support 50+ Notification services)</option>
|
<option value="apprise">Apprise (Support 50+ Notification services)</option>
|
||||||
<option value="pushbullet">Pushbullet</option>
|
<option value="pushbullet">Pushbullet</option>
|
||||||
<option value="line">Line Messenger</option>
|
<option value="line">Line Messenger</option>
|
||||||
|
<option value="mattermost">Mattermost</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -238,6 +239,39 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="notification.type === 'mattermost'">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="mattermost-webhook-url" class="form-label">Webhook URL<span style="color:red;"><sup>*</sup></span></label>
|
||||||
|
<input id="mattermost-webhook-url" v-model="notification.mattermostWebhookUrl" type="text" class="form-control" required>
|
||||||
|
<label for="mattermost-username" class="form-label">Username</label>
|
||||||
|
<input id="mattermost-username" v-model="notification.mattermostusername" type="text" class="form-control">
|
||||||
|
<label for="mattermost-iconurl" class="form-label">Icon URL</label>
|
||||||
|
<input id="mattermost-iconurl" v-model="notification.mattermosticonurl" type="text" class="form-control">
|
||||||
|
<label for="mattermost-iconemo" class="form-label">Icon Emoji</label>
|
||||||
|
<input id="mattermost-iconemo" v-model="notification.mattermosticonemo" type="text" class="form-control">
|
||||||
|
<label for="mattermost-channel" class="form-label">Channel Name</label>
|
||||||
|
<input id="mattermost-channel-name" v-model="notification.mattermostchannel" type="text" class="form-control">
|
||||||
|
<div class="form-text">
|
||||||
|
<span style="color:red;"><sup>*</sup></span>Required
|
||||||
|
<p style="margin-top: 8px;">
|
||||||
|
More info about webhooks on: <a href="https://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">https://docs.mattermost.com/developer/webhooks-incoming.html</a>
|
||||||
|
</p>
|
||||||
|
<p style="margin-top: 8px;">
|
||||||
|
You can override the default channel that webhook posts to by entering the channel name into "Channel Name" field. This needs to be enabled in Mattermost webhook settings. Ex: #other-channel
|
||||||
|
</p>
|
||||||
|
<p style="margin-top: 8px;">
|
||||||
|
If you leave the Uptime Kuma URL field blank, it will default to the Project Github page.
|
||||||
|
</p>
|
||||||
|
<p style="margin-top: 8px;">
|
||||||
|
You can provide a link to a picture in "Icon URL" to override the default profile picture. Will not be used if Icon Emoji is set.
|
||||||
|
</p>
|
||||||
|
<p style="margin-top: 8px;">
|
||||||
|
Emoji cheat sheet: <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a> Note: emoji takes preference over Icon URL.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type === 'pushy'">
|
<template v-if="notification.type === 'pushy'">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="pushy-app-token" class="form-label">API_KEY</label>
|
<label for="pushy-app-token" class="form-label">API_KEY</label>
|
||||||
|
|
|
@ -24,6 +24,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
// Configurable filtering on top of the returned data
|
||||||
chartPeriodHrs: 6,
|
chartPeriodHrs: 6,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -55,11 +56,10 @@ export default {
|
||||||
|
|
||||||
elements: {
|
elements: {
|
||||||
point: {
|
point: {
|
||||||
|
// Hide points on chart unless mouse-over
|
||||||
radius: 0,
|
radius: 0,
|
||||||
|
hitRadius: 100,
|
||||||
},
|
},
|
||||||
bar: {
|
|
||||||
barThickness: "flex",
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
|
@ -77,9 +77,9 @@ export default {
|
||||||
maxRotation: 0,
|
maxRotation: 0,
|
||||||
autoSkipPadding: 30,
|
autoSkipPadding: 30,
|
||||||
},
|
},
|
||||||
bounds: "ticks",
|
|
||||||
grid: {
|
grid: {
|
||||||
color: this.$root.theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)",
|
color: this.$root.theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)",
|
||||||
|
offset: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
|
@ -109,8 +109,11 @@ export default {
|
||||||
mode: "nearest",
|
mode: "nearest",
|
||||||
intersect: false,
|
intersect: false,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
|
backgroundColor: this.$root.theme === "light" ? "rgba(212,232,222,1.0)" : "rgba(32,42,38,1.0)",
|
||||||
|
bodyColor: this.$root.theme === "light" ? "rgba(12,12,18,1.0)" : "rgba(220,220,220,1.0)",
|
||||||
|
titleColor: this.$root.theme === "light" ? "rgba(12,12,18,1.0)" : "rgba(220,220,220,1.0)",
|
||||||
filter: function (tooltipItem) {
|
filter: function (tooltipItem) {
|
||||||
return tooltipItem.datasetIndex === 0;
|
return tooltipItem.datasetIndex === 0; // Hide tooltip on Bar Chart
|
||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (context) => {
|
label: (context) => {
|
||||||
|
@ -125,32 +128,29 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
chartData() {
|
chartData() {
|
||||||
let ping_data = [];
|
let pingData = []; // Ping Data for Line Chart, y-axis contains ping time
|
||||||
let down_data = [];
|
let downData = []; // Down Data for Bar Chart, y-axis is 1 if target is down, 0 if target is up
|
||||||
if (this.monitorId in this.$root.heartbeatList) {
|
if (this.monitorId in this.$root.heartbeatList) {
|
||||||
ping_data = this.$root.heartbeatList[this.monitorId]
|
this.$root.heartbeatList[this.monitorId]
|
||||||
.filter(
|
.filter(
|
||||||
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours")))
|
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours")))
|
||||||
.map((beat) => {
|
.map((beat) => {
|
||||||
return {
|
const x = this.$root.datetime(beat.time);
|
||||||
x: dayjs.utc(beat.time).tz(this.$root.timezone).format("YYYY-MM-DD HH:mm:ss"),
|
pingData.push({
|
||||||
|
x,
|
||||||
y: beat.ping,
|
y: beat.ping,
|
||||||
};
|
});
|
||||||
});
|
downData.push({
|
||||||
down_data = this.$root.heartbeatList[this.monitorId]
|
x,
|
||||||
.filter(
|
|
||||||
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours")))
|
|
||||||
.map((beat) => {
|
|
||||||
return {
|
|
||||||
x: dayjs.utc(beat.time).tz(this.$root.timezone).format("YYYY-MM-DD HH:mm:ss"),
|
|
||||||
y: beat.status === 0 ? 1 : 0,
|
y: beat.status === 0 ? 1 : 0,
|
||||||
};
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
data: ping_data,
|
// Line Chart
|
||||||
|
data: pingData,
|
||||||
fill: "origin",
|
fill: "origin",
|
||||||
tension: 0.2,
|
tension: 0.2,
|
||||||
borderColor: "#5CDD8B",
|
borderColor: "#5CDD8B",
|
||||||
|
@ -158,11 +158,15 @@ export default {
|
||||||
yAxisID: "y",
|
yAxisID: "y",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Bar Chart
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: down_data,
|
data: downData,
|
||||||
borderColor: "#00000000",
|
borderColor: "#00000000",
|
||||||
backgroundColor: "#DC354568",
|
backgroundColor: "#DC354568",
|
||||||
yAxisID: "y1",
|
yAxisID: "y1",
|
||||||
|
barThickness: "flex",
|
||||||
|
barPercentage: 1,
|
||||||
|
categoryPercentage: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -191,6 +191,7 @@ main {
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
|
color: #aaa;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding-bottom: 30px;
|
padding-bottom: 30px;
|
||||||
|
@ -198,10 +199,6 @@ footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
header {
|
header {
|
||||||
background-color: #161b22;
|
background-color: #161b22;
|
||||||
|
|
Loading…
Reference in a new issue