mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-30 18:24:03 +00:00
Merge branch 'master' into bugfix/1451_blank_page_on_unkown_resource
This commit is contained in:
commit
73b965c867
29 changed files with 304 additions and 109 deletions
|
@ -8,7 +8,7 @@ services:
|
||||||
image: louislam/uptime-kuma:1
|
image: louislam/uptime-kuma:1
|
||||||
container_name: uptime-kuma
|
container_name: uptime-kuma
|
||||||
volumes:
|
volumes:
|
||||||
- ./uptime-kuma:/app/data
|
- ./uptime-kuma-data:/app/data
|
||||||
ports:
|
ports:
|
||||||
- 3001:3001
|
- 3001:3001 # <Host Port>:<Container Port>
|
||||||
restart: always
|
restart: always
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "uptime-kuma",
|
"name": "uptime-kuma",
|
||||||
"version": "1.15.1",
|
"version": "1.16.0-beta.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -182,7 +182,7 @@ class Monitor extends BeanModel {
|
||||||
// undefined if not https
|
// undefined if not https
|
||||||
let tlsInfo = undefined;
|
let tlsInfo = undefined;
|
||||||
|
|
||||||
if (!previousBeat) {
|
if (!previousBeat || this.type === "push") {
|
||||||
previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
|
previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
|
||||||
this.id,
|
this.id,
|
||||||
]);
|
]);
|
||||||
|
@ -377,9 +377,6 @@ class Monitor extends BeanModel {
|
||||||
log.debug("monitor", "heartbeatCount" + heartbeatCount + " " + time);
|
log.debug("monitor", "heartbeatCount" + heartbeatCount + " " + time);
|
||||||
|
|
||||||
if (heartbeatCount <= 0) {
|
if (heartbeatCount <= 0) {
|
||||||
// Fix #922, since previous heartbeat could be inserted by api, it should get from database
|
|
||||||
previousBeat = await Monitor.getPreviousHeartbeat(this.id);
|
|
||||||
|
|
||||||
throw new Error("No heartbeat in the time window");
|
throw new Error("No heartbeat in the time window");
|
||||||
} else {
|
} else {
|
||||||
// No need to insert successful heartbeat for push type, so end here
|
// No need to insert successful heartbeat for push type, so end here
|
||||||
|
|
|
@ -6,9 +6,14 @@ class Apprise extends NotificationProvider {
|
||||||
name = "apprise";
|
name = "apprise";
|
||||||
|
|
||||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
let s = childProcess.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL ]);
|
const args = [ "-vv", "-b", msg, notification.appriseURL ];
|
||||||
|
if (notification.title) {
|
||||||
|
args.push("-t");
|
||||||
|
args.push(notification.title);
|
||||||
|
}
|
||||||
|
const s = childProcess.spawnSync("apprise", args);
|
||||||
|
|
||||||
let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
|
const output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
|
||||||
|
|
||||||
if (output) {
|
if (output) {
|
||||||
|
|
||||||
|
|
|
@ -22,16 +22,23 @@ class Discord extends NotificationProvider {
|
||||||
return okMsg;
|
return okMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
let url;
|
let address;
|
||||||
|
|
||||||
if (monitorJSON["type"] === "port") {
|
switch (monitorJSON["type"]) {
|
||||||
url = monitorJSON["hostname"];
|
case "ping":
|
||||||
|
address = monitorJSON["hostname"];
|
||||||
|
break;
|
||||||
|
case "port":
|
||||||
|
case "dns":
|
||||||
|
case "steam":
|
||||||
|
address = monitorJSON["hostname"];
|
||||||
if (monitorJSON["port"]) {
|
if (monitorJSON["port"]) {
|
||||||
url += ":" + monitorJSON["port"];
|
address += ":" + monitorJSON["port"];
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
} else {
|
default:
|
||||||
url = monitorJSON["url"];
|
address = monitorJSON["url"];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If heartbeatJSON is not null, we go into the normal alerting loop.
|
// If heartbeatJSON is not null, we go into the normal alerting loop.
|
||||||
|
@ -48,8 +55,8 @@ class Discord extends NotificationProvider {
|
||||||
value: monitorJSON["name"],
|
value: monitorJSON["name"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Service URL",
|
name: "Service URL / Address",
|
||||||
value: url,
|
value: address,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Time (UTC)",
|
name: "Time (UTC)",
|
||||||
|
@ -84,7 +91,7 @@ class Discord extends NotificationProvider {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Service URL",
|
name: "Service URL",
|
||||||
value: url.startsWith("http") ? "[Visit Service](" + url + ")" : url,
|
value: address.startsWith("http") ? "[Visit Service](" + address + ")" : address,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Time (UTC)",
|
name: "Time (UTC)",
|
||||||
|
|
|
@ -10,7 +10,10 @@ import { sleep } from "../util.ts";
|
||||||
export default {
|
export default {
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: [ String, Number ],
|
value: {
|
||||||
|
type: [ String, Number ],
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
time: {
|
time: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0.3,
|
default: 0.3,
|
||||||
|
|
|
@ -13,7 +13,10 @@ dayjs.extend(relativeTime);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: String,
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
dateOnly: {
|
dateOnly: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
@ -168,7 +168,8 @@ export default {
|
||||||
|
|
||||||
getBeatTitle(beat) {
|
getBeatTitle(beat) {
|
||||||
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
|
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
|
||||||
}
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
<Uptime :monitor="monitor.element" type="24" :pill="true" />
|
<Uptime :monitor="monitor.element" type="24" :pill="true" />
|
||||||
{{ monitor.element.name }}
|
{{ monitor.element.name }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showTags" class="tags">
|
<div v-if="showTag" class="tags">
|
||||||
<Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
|
<Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,10 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
status: Number,
|
status: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -5,8 +5,14 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
monitor: Object,
|
monitor: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
pill: {
|
pill: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
<a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a>
|
<a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<label for="title" class="form-label">{{ $t("Title") }}</label>
|
||||||
|
<input id="title" v-model="$parent.notification.title" type="text" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<i18n-t tag="p" keypath="Status:">
|
<i18n-t tag="p" keypath="Status:">
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="clicksendsms-login" class="form-label">API Username</label>
|
<label for="clicksendsms-login" class="form-label">{{ $t("API Username") }}</label>
|
||||||
<div class="form-text">
|
<i18n-t tag="div" class="form-text" keypath="wayToGetClickSendSMSToken">
|
||||||
{{ $t("apiCredentials") }}
|
|
||||||
<a href="http://dashboard.clicksend.com/account/subaccounts" target="_blank">{{ $t("here") }}</a>
|
<a href="http://dashboard.clicksend.com/account/subaccounts" target="_blank">{{ $t("here") }}</a>
|
||||||
</div>
|
</i18n-t>
|
||||||
<input id="clicksendsms-login" v-model="$parent.notification.clicksendsmsLogin" type="text" class="form-control" required>
|
<input id="clicksendsms-login" v-model="$parent.notification.clicksendsmsLogin" type="text" class="form-control" required>
|
||||||
<label for="clicksendsms-key" class="form-label">API Key</label>
|
<label for="clicksendsms-key" class="form-label">{{ $t("API Key") }}</label>
|
||||||
<HiddenInput id="clicksendsms-key" v-model="$parent.notification.clicksendsmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
|
<HiddenInput id="clicksendsms-key" v-model="$parent.notification.clicksendsmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
@ -16,15 +15,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="clicksendsms-to-number" class="form-label">Recipient Number</label>
|
<label for="clicksendsms-to-number" class="form-label">{{ $t("Recipient Number") }}</label>
|
||||||
<input id="clicksendsms-to-number" v-model="$parent.notification.clicksendsmsToNumber" type="text" minlength="8" maxlength="14" class="form-control" required>
|
<input id="clicksendsms-to-number" v-model="$parent.notification.clicksendsmsToNumber" type="text" minlength="8" maxlength="14" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="clicksendsms-sender-name" class="form-label">From Name/Number -
|
<label for="clicksendsms-sender-name" class="form-label">{{ $t("From Name/Number") }} -
|
||||||
<a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">More Info</a>
|
<a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">{{ $t("Read more") }}</a>
|
||||||
</label>
|
</label>
|
||||||
<input id="clicksendsms-sender-name" v-model="$parent.notification.clicksendsmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
<input id="clicksendsms-sender-name" v-model="$parent.notification.clicksendsmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
||||||
<div class="form-text">Leave blank to use a shared sender number.</div>
|
<div class="form-text">{{ $t("Leave blank to use a shared sender number.") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<b>{{ $t("Basic Settings") }}</b>
|
<b>{{ $t("Basic Settings") }}</b>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
<div class="mb-3" style="margin-top: 12px;">
|
<div class="mb-3" style="margin-top: 12px;">
|
||||||
<label for="line-user-id" class="form-label">User ID</label>
|
<label for="line-user-id" class="form-label">{{ $t("User ID") }}</label>
|
||||||
<input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required>
|
<input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
<i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text">
|
<i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text">
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="octopush-version" class="form-label">Octopush API Version</label>
|
<label for="octopush-version" class="form-label">{{ $t("Octopush API Version") }}</label>
|
||||||
<select id="octopush-version" v-model="$parent.notification.octopushVersion" class="form-select">
|
<select id="octopush-version" v-model="$parent.notification.octopushVersion" class="form-select">
|
||||||
<option value="2">Octopush (endpoint: api.octopush.com)</option>
|
<option value="2">{{ $t("octopush") }} ({{ $t("endpoint") }}: api.octopush.com)</option>
|
||||||
<option value="1">Legacy Octopush-DM (endpoint: www.octopush-dm.com)</option>
|
<option value="1">{{ $t("Legacy Octopush-DM") }} ({{ $t("endpoint") }}: www.octopush-dm.com)</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
{{ $t("octopushLegacyHint") }}
|
{{ $t("octopushLegacyHint") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="octopush-key" class="form-label">API KEY</label>
|
<label for="octopush-key" class="form-label">{{ $t("octopushAPIKey") }}</label>
|
||||||
<HiddenInput id="octopush-key" v-model="$parent.notification.octopushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
|
<HiddenInput id="octopush-key" v-model="$parent.notification.octopushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||||
<label for="octopush-login" class="form-label">API LOGIN</label>
|
<label for="octopush-login" class="form-label">{{ $t("octopushLogin") }}</label>
|
||||||
<input id="octopush-login" v-model="$parent.notification.octopushLogin" type="text" class="form-control" required>
|
<input id="octopush-login" v-model="$parent.notification.octopushLogin" type="text" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="promosms-login" class="form-label">API LOGIN</label>
|
<label for="promosms-login" class="form-label">{{ $("promosmsLogin") }}</label>
|
||||||
<input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required>
|
<input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required>
|
||||||
<label for="promosms-key" class="form-label">API PASSWORD</label>
|
<label for="promosms-key" class="form-label">{{ $("promosmsPassword") }}</label>
|
||||||
<HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
|
<HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
|
|
@ -18,28 +18,29 @@
|
||||||
</select>
|
</select>
|
||||||
<label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label>
|
<label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label>
|
||||||
<select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select">
|
<select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select">
|
||||||
<option>pushover</option>
|
<option value="pushover">{{ $t("pushoversounds pushover") }}</option>
|
||||||
<option>bike</option>
|
<option value="bike">{{ $t("pushoversounds bike") }}</option>
|
||||||
<option>bugle</option>
|
<option value="bugle">{{ $t("pushoversounds bugle") }}</option>
|
||||||
<option>cashregister</option>
|
<option value="cashregister">{{ $t("pushoversounds cashregister") }}</option>
|
||||||
<option>classical</option>
|
<option value="classical">{{ $t("pushoversounds classical") }}</option>
|
||||||
<option>cosmic</option>
|
<option value="cosmic">{{ $t("pushoversounds cosmic") }}</option>
|
||||||
<option>falling</option>
|
<option value="falling">{{ $t("pushoversounds falling") }}</option>
|
||||||
<option>gamelan</option>
|
<option value="gamelan">{{ $t("pushoversounds gamelan") }}</option>
|
||||||
<option>incoming</option>
|
<option value="incoming">{{ $t("pushoversounds incoming") }}</option>
|
||||||
<option>intermission</option>
|
<option value="intermission">{{ $t("pushoversounds intermission") }}</option>
|
||||||
<option>mechanical</option>
|
<option value="magic">{{ $t("pushoversounds magic") }}</option>
|
||||||
<option>pianobar</option>
|
<option value="mechanical">{{ $t("pushoversounds mechanical") }}</option>
|
||||||
<option>siren</option>
|
<option value="pianobar">{{ $t("pushoversounds pianobar") }}</option>
|
||||||
<option>spacealarm</option>
|
<option value="siren">{{ $t("pushoversounds siren") }}</option>
|
||||||
<option>tugboat</option>
|
<option value="spacealarm">{{ $t("pushoversounds spacealarm") }}</option>
|
||||||
<option>alien</option>
|
<option value="tugboat">{{ $t("pushoversounds tugboat") }}</option>
|
||||||
<option>climb</option>
|
<option value="alien">{{ $t("pushoversounds alien") }}</option>
|
||||||
<option>persistent</option>
|
<option value="climb">{{ $t("pushoversounds climb") }}</option>
|
||||||
<option>echo</option>
|
<option value="persistent">{{ $t("pushoversounds persistent") }}</option>
|
||||||
<option>updown</option>
|
<option value="echo">{{ $t("pushoversounds echo") }}</option>
|
||||||
<option>vibrate</option>
|
<option value="updown">{{ $t("pushoversounds updown") }}</option>
|
||||||
<option>none</option>
|
<option value="vibrate">{{ $t("pushoversounds vibrate") }}</option>
|
||||||
|
<option value="none">{{ $t("pushoversounds none") }}</option>
|
||||||
</select>
|
</select>
|
||||||
<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") }}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<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">{{ $t("pushyAPIKey") }}</label>
|
||||||
<HiddenInput id="pushy-app-token" v-model="$parent.notification.pushyAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
|
<HiddenInput id="pushy-app-token" v-model="$parent.notification.pushyAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="pushy-user-key" class="form-label">USER_TOKEN</label>
|
<label for="pushy-user-key" class="form-label">{{ $t("pushyToken") }}</label>
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput>
|
<HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="push-api-key" class="form-label">API_KEY</label>
|
<label for="push-api-key" class="form-label">{{ $t("API Key") }}</label>
|
||||||
<HiddenInput id="push-api-key" v-model="$parent.notification.pushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
|
<HiddenInput id="push-api-key" v-model="$parent.notification.pushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
|
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<label><input v-model="settings.checkUpdate" type="checkbox" @change="saveSettings()" /> Show update if available</label>
|
<label><input v-model="settings.checkUpdate" type="checkbox" @change="saveSettings()" /> {{ $t("Show update if available") }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> Also check beta release</label>
|
<label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> {{ $t("Also check beta release") }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -464,4 +464,55 @@ export default {
|
||||||
"Domain Names": "Domain Names",
|
"Domain Names": "Domain Names",
|
||||||
signedInDisp: "Signed in as {0}",
|
signedInDisp: "Signed in as {0}",
|
||||||
signedInDispDisabled: "Auth Disabled.",
|
signedInDispDisabled: "Auth Disabled.",
|
||||||
|
"Certificate Expiry Notification": "Certificate Expiry Notification",
|
||||||
|
"API Username": "API Username",
|
||||||
|
"API Key": "API Key",
|
||||||
|
"Recipient Number": "Recipient Number",
|
||||||
|
"From Name/Number": "From Name/Number",
|
||||||
|
"Leave blank to use a shared sender number.": "Leave blank to use a shared sender number.",
|
||||||
|
"Octopush API Version": "Octopush API Version",
|
||||||
|
"Legacy Octopush-DM": "Legacy Octopush-DM",
|
||||||
|
"endpoint": "endpoint",
|
||||||
|
octopushAPIKey: "\"API key\" from HTTP API credentials in control panel",
|
||||||
|
octopushLogin: "\"Login\" from HTTP API credentials in control panel",
|
||||||
|
promosmsLogin: "API Login Name",
|
||||||
|
promosmsPassword: "API Password",
|
||||||
|
"pushoversounds pushover": "Pushover (default)",
|
||||||
|
"pushoversounds bike": "Bike",
|
||||||
|
"pushoversounds bugle": "Bugle",
|
||||||
|
"pushoversounds cashregister": "Cash Register",
|
||||||
|
"pushoversounds classical": "Classical",
|
||||||
|
"pushoversounds cosmic": "Cosmic",
|
||||||
|
"pushoversounds falling": "Falling",
|
||||||
|
"pushoversounds gamelan": "Gamelan",
|
||||||
|
"pushoversounds incoming": "Incoming",
|
||||||
|
"pushoversounds intermission": "Intermission",
|
||||||
|
"pushoversounds magic": "Magic",
|
||||||
|
"pushoversounds mechanical": "Mechanical",
|
||||||
|
"pushoversounds pianobar": "Piano Bar",
|
||||||
|
"pushoversounds siren": "Siren",
|
||||||
|
"pushoversounds spacealarm": "Space Alarm",
|
||||||
|
"pushoversounds tugboat": "Tug Boat",
|
||||||
|
"pushoversounds alien": "Alien Alarm (long)",
|
||||||
|
"pushoversounds climb": "Climb (long)",
|
||||||
|
"pushoversounds persistent": "Persistent (long)",
|
||||||
|
"pushoversounds echo": "Pushover Echo (long)",
|
||||||
|
"pushoversounds updown": "Up Down (long)",
|
||||||
|
"pushoversounds vibrate": "Vibrate Only",
|
||||||
|
"pushoversounds none": "None (silent)",
|
||||||
|
pushyAPIKey: "Secret API Key",
|
||||||
|
pushyToken: "Device token",
|
||||||
|
"Show update if available": "Show update if available",
|
||||||
|
"Also check beta release": "Also check beta release",
|
||||||
|
"Using a Reverse Proxy?": "Using a Reverse Proxy?",
|
||||||
|
"Check how to config it for WebSocket": "Check how to config it for WebSocket",
|
||||||
|
"Steam Game Server": "Steam Game Server",
|
||||||
|
"Most likely causes:": "Most likely causes:",
|
||||||
|
"The resource is no longer available.": "The resource is no longer available.",
|
||||||
|
"There might be a typing error in the address.": "There might be a typing error in the address.",
|
||||||
|
"What you can try:": "What you can try:",
|
||||||
|
"Retype the address.": "Retype the address.",
|
||||||
|
"Go back to the previous page.": "Go back to the previous page.",
|
||||||
|
"Coming Soon": "Coming Soon",
|
||||||
|
wayToGetClickSendSMSToken: "You can get API Username and API Key from {0} .",
|
||||||
};
|
};
|
||||||
|
|
|
@ -88,7 +88,7 @@ export default {
|
||||||
Dark: "黑暗",
|
Dark: "黑暗",
|
||||||
Auto: "自动",
|
Auto: "自动",
|
||||||
"Theme - Heartbeat Bar": "主题 - 心跳栏",
|
"Theme - Heartbeat Bar": "主题 - 心跳栏",
|
||||||
Normal: "正常", // 此处还供 Gorush 的通知优先级功能使用,不应翻译为“正常显示”
|
Normal: "正常",
|
||||||
Bottom: "靠下",
|
Bottom: "靠下",
|
||||||
None: "不显示",
|
None: "不显示",
|
||||||
Timezone: "时区",
|
Timezone: "时区",
|
||||||
|
@ -398,11 +398,9 @@ export default {
|
||||||
Invalid: "无效",
|
Invalid: "无效",
|
||||||
AccessKeyId: "AccessKey ID",
|
AccessKeyId: "AccessKey ID",
|
||||||
SecretAccessKey: "AccessKey Secret",
|
SecretAccessKey: "AccessKey Secret",
|
||||||
/* 以下为阿里云短信服务 API Dysms#SendSms 的参数 */
|
|
||||||
PhoneNumbers: "PhoneNumbers",
|
PhoneNumbers: "PhoneNumbers",
|
||||||
TemplateCode: "TemplateCode",
|
TemplateCode: "TemplateCode",
|
||||||
SignName: "SignName",
|
SignName: "SignName",
|
||||||
/* 以上为阿里云短信服务 API Dysms#SendSms 的参数 */
|
|
||||||
"Bark Endpoint": "Bark 接入点",
|
"Bark Endpoint": "Bark 接入点",
|
||||||
"Device Token": "Apple Device Token",
|
"Device Token": "Apple Device Token",
|
||||||
Platform: "平台",
|
Platform: "平台",
|
||||||
|
@ -441,7 +439,7 @@ export default {
|
||||||
"No Proxy": "无代理",
|
"No Proxy": "无代理",
|
||||||
"HTTP Basic Auth": "HTTP 基础身份验证",
|
"HTTP Basic Auth": "HTTP 基础身份验证",
|
||||||
"New Status Page": "新的状态页",
|
"New Status Page": "新的状态页",
|
||||||
"Page Not Found": "状态页未找到",
|
"Page Not Found": "未找到该页面",
|
||||||
"Reverse Proxy": "反向代理",
|
"Reverse Proxy": "反向代理",
|
||||||
"Subject:": "颁发给:",
|
"Subject:": "颁发给:",
|
||||||
"Valid To:": "有效期至:",
|
"Valid To:": "有效期至:",
|
||||||
|
@ -469,4 +467,57 @@ export default {
|
||||||
"Footer Text": "底部自定义文本",
|
"Footer Text": "底部自定义文本",
|
||||||
"Show Powered By": "显示 Powered By",
|
"Show Powered By": "显示 Powered By",
|
||||||
"Domain Names": "域名",
|
"Domain Names": "域名",
|
||||||
|
"Certificate Expiry Notification": "证书到期时通知",
|
||||||
|
"API Username": "API 凭证 Username",
|
||||||
|
"API Key": "API 凭证 Key",
|
||||||
|
"Recipient Number": "收件人手机号码",
|
||||||
|
"From Name/Number": "发件人名称/手机号码",
|
||||||
|
"Leave blank to use a shared sender number.": "留空以使用平台共享的发件人手机号码",
|
||||||
|
"Octopush API Version": "Octopush API 版本",
|
||||||
|
"Legacy Octopush-DM": "旧版本 Octopush-DM",
|
||||||
|
endpoint: "接入点",
|
||||||
|
octopushAPIKey: "控制台 HTTP API credentials 里的 \"API key\"",
|
||||||
|
octopushLogin: "控制台 HTTP API credentials 里的 \"Login\"",
|
||||||
|
promosmsLogin: "API 登录名",
|
||||||
|
promosmsPassword: "API 密码",
|
||||||
|
"pushoversounds pushover": "Pushover(默认)",
|
||||||
|
"pushoversounds bike": "Bike",
|
||||||
|
"pushoversounds bugle": "Bugle",
|
||||||
|
"pushoversounds cashregister": "Cash Register",
|
||||||
|
"pushoversounds classical": "Classical",
|
||||||
|
"pushoversounds cosmic": "Cosmic",
|
||||||
|
"pushoversounds falling": "Falling",
|
||||||
|
"pushoversounds gamelan": "Gamelan",
|
||||||
|
"pushoversounds incoming": "Incoming",
|
||||||
|
"pushoversounds intermission": "Intermission",
|
||||||
|
"pushoversounds magic": "Magic",
|
||||||
|
"pushoversounds mechanical": "Mechanical",
|
||||||
|
"pushoversounds pianobar": "Piano Bar",
|
||||||
|
"pushoversounds siren": "Siren",
|
||||||
|
"pushoversounds spacealarm": "Space Alarm",
|
||||||
|
"pushoversounds tugboat": "Tug Boat",
|
||||||
|
"pushoversounds alien": "Alien Alarm(长铃声)",
|
||||||
|
"pushoversounds climb": "Climb(长铃声)",
|
||||||
|
"pushoversounds persistent": "Persistent(长铃声)",
|
||||||
|
"pushoversounds echo": "Pushover Echo(长铃声)",
|
||||||
|
"pushoversounds updown": "Up Down(长铃声)",
|
||||||
|
"pushoversounds vibrate": "仅震动",
|
||||||
|
"pushoversounds none": "无(禁音)",
|
||||||
|
pushyAPIKey: "API 密钥",
|
||||||
|
pushyToken: "设备 Token",
|
||||||
|
"Show update if available": "有更新时通知",
|
||||||
|
"Also check beta release": "一并检查 Beta 版更新",
|
||||||
|
"Using a Reverse Proxy?": "正在使用反向代理?",
|
||||||
|
"Check how to config it for WebSocket": "查看如何将反向代理与 WebSocket 一起使用",
|
||||||
|
"Steam Game Server": "Steam 游戏服务器",
|
||||||
|
"Most likely causes:": "最可能的原因:",
|
||||||
|
"The resource is no longer available.": "您所请求的资源已不再可用;",
|
||||||
|
"There might be a typing error in the address.": "您输入的地址可能有误。",
|
||||||
|
"What you can try:": "您可以尝试以下操作:",
|
||||||
|
"Retype the address.": "重新输入地址;",
|
||||||
|
"Go back to the previous page.": "返回到上一页面。",
|
||||||
|
"Coming Soon": "即将推出",
|
||||||
|
wayToGetClickSendSMSToken: "您可以从 {0} 获取 API 凭证 Username 和 凭证 Key。",
|
||||||
|
signedInDisp: "当前用户: {0}",
|
||||||
|
signedInDispDisabled: "已禁用身份验证",
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
{{ $root.connectionErrorMsg }}
|
{{ $root.connectionErrorMsg }}
|
||||||
<div v-if="$root.showReverseProxyGuide">
|
<div v-if="$root.showReverseProxyGuide">
|
||||||
Using a Reverse Proxy? <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">Check how to config it for WebSocket</a>
|
{{ $t("Using a Reverse Proxy?") }} <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">{{ $t("Check how to config it for WebSocket") }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
Push
|
Push
|
||||||
</option>
|
</option>
|
||||||
<option value="steam">
|
<option value="steam">
|
||||||
Steam Game Server
|
{{ $t("Steam Game Server") }}
|
||||||
</option>
|
</option>
|
||||||
<option value="mqtt">
|
<option value="mqtt">
|
||||||
MQTT
|
MQTT
|
||||||
|
|
|
@ -22,16 +22,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="guide">
|
<div class="guide">
|
||||||
Most likely causes:
|
{{ $t("Most likely causes:") }}
|
||||||
<ul>
|
<ul>
|
||||||
<li>The resource is no longer available.</li>
|
<li>{{ $t("The resource is no longer available.") }}</li>
|
||||||
<li>There might be a typing error in the address.</li>
|
<li>{{ $t("There might be a typing error in the address.") }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
What you can try:<br />
|
{{ $t("What you can try:") }}<br />
|
||||||
<ul>
|
<ul>
|
||||||
<li>Retype the address.</li>
|
<li>{{ $t("Retype the address.") }}</li>
|
||||||
<li><a href="#" class="go-back" @click="goBack()">Go back to the previous page.</a></li>
|
<li><a href="#" class="go-back" @click="goBack()">{{ $t("Go back to the previous page.") }}</a></li>
|
||||||
<li><a href="/" class="go-back">Go back to home page.</a></li>
|
<li><a href="/" class="go-back">Go back to home page.</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="false" class="my-3">
|
<div v-if="false" class="my-3">
|
||||||
<label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label>
|
<label for="password" class="form-label">{{ $t("Password") }} <sup>{{ $t("Coming Soon") }}</sup></label>
|
||||||
<input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
|
<input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -104,7 +104,8 @@
|
||||||
|
|
||||||
<!-- Uploader -->
|
<!-- Uploader -->
|
||||||
<!-- url="/api/status-page/upload-logo" -->
|
<!-- url="/api/status-page/upload-logo" -->
|
||||||
<ImageCropUpload v-model="showImageCropUpload"
|
<ImageCropUpload
|
||||||
|
v-model="showImageCropUpload"
|
||||||
field="img"
|
field="img"
|
||||||
:width="128"
|
:width="128"
|
||||||
:height="128"
|
:height="128"
|
||||||
|
@ -281,22 +282,21 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import PublicGroupList from "../components/PublicGroupList.vue";
|
|
||||||
import ImageCropUpload from "vue-image-crop-upload";
|
|
||||||
import { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_PARTIAL_DOWN, UP } from "../util.ts";
|
|
||||||
import { useToast } from "vue-toastification";
|
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import Favico from "favico.js";
|
import Favico from "favico.js";
|
||||||
import { getResBaseURL } from "../util-frontend";
|
|
||||||
import Confirm from "../components/Confirm.vue";
|
|
||||||
// import Prism Editor
|
|
||||||
import { PrismEditor } from "vue-prism-editor";
|
|
||||||
import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere
|
|
||||||
|
|
||||||
// import highlighting library (you can use any library you want just return html string)
|
// import highlighting library (you can use any library you want just return html string)
|
||||||
import { highlight, languages } from "prismjs/components/prism-core";
|
import { highlight, languages } from "prismjs/components/prism-core";
|
||||||
import "prismjs/components/prism-css";
|
import "prismjs/components/prism-css";
|
||||||
import "prismjs/themes/prism-tomorrow.css"; // import syntax highlighting styles
|
import "prismjs/themes/prism-tomorrow.css"; // import syntax highlighting styles
|
||||||
|
import ImageCropUpload from "vue-image-crop-upload";
|
||||||
|
// import Prism Editor
|
||||||
|
import { PrismEditor } from "vue-prism-editor";
|
||||||
|
import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere
|
||||||
|
import { useToast } from "vue-toastification";
|
||||||
|
import Confirm from "../components/Confirm.vue";
|
||||||
|
import PublicGroupList from "../components/PublicGroupList.vue";
|
||||||
|
import { getResBaseURL } from "../util-frontend";
|
||||||
|
import { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_PARTIAL_DOWN, UP } from "../util.ts";
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
|
@ -693,7 +693,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
statusPageLogoLoaded(eventPayload) {
|
statusPageLogoLoaded(eventPayload) {
|
||||||
// Remark: may not work in dev, due to cros
|
// Remark: may not work in dev, due to CORS
|
||||||
favicon.image(eventPayload.target);
|
favicon.image(eventPayload.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
const { genSecret } = require("../src/util");
|
const { genSecret, DOWN } = require("../src/util");
|
||||||
const utilServerRewire = require("../server/util-server");
|
const utilServerRewire = require("../server/util-server");
|
||||||
|
const Discord = require("../server/notification-providers/discord");
|
||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
jest.mock("axios");
|
||||||
|
|
||||||
describe("Test parseCertificateInfo", () => {
|
describe("Test parseCertificateInfo", () => {
|
||||||
it("should handle undefined", async () => {
|
it("should handle undefined", async () => {
|
||||||
|
@ -164,6 +168,68 @@ describe("Test reset-password", () => {
|
||||||
}, 120000);
|
}, 120000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Test Discord Notification Provider", () => {
|
||||||
|
const sendNotification = async (hostname, port, type) => {
|
||||||
|
const discordProvider = new Discord();
|
||||||
|
|
||||||
|
axios.post.mockResolvedValue({});
|
||||||
|
|
||||||
|
await discordProvider.send(
|
||||||
|
{
|
||||||
|
discordUsername: "Uptime Kuma",
|
||||||
|
discordWebhookUrl: "https://discord.com",
|
||||||
|
},
|
||||||
|
"test message",
|
||||||
|
{
|
||||||
|
type,
|
||||||
|
hostname,
|
||||||
|
port,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: DOWN,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
it("should send hostname for dns monitors", async () => {
|
||||||
|
const hostname = "discord.com";
|
||||||
|
await sendNotification(hostname, null, "dns");
|
||||||
|
|
||||||
|
expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
|
||||||
|
hostname
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send hostname for ping monitors", async () => {
|
||||||
|
const hostname = "discord.com";
|
||||||
|
await sendNotification(hostname, null, "ping");
|
||||||
|
|
||||||
|
expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
|
||||||
|
hostname
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send hostname for port monitors", async () => {
|
||||||
|
const hostname = "discord.com";
|
||||||
|
const port = 1337;
|
||||||
|
await sendNotification(hostname, port, "port");
|
||||||
|
|
||||||
|
expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
|
||||||
|
`${hostname}:${port}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should send hostname for steam monitors", async () => {
|
||||||
|
const hostname = "discord.com";
|
||||||
|
const port = 1337;
|
||||||
|
await sendNotification(hostname, port, "steam");
|
||||||
|
|
||||||
|
expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
|
||||||
|
`${hostname}:${port}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("The function filterAndJoin", () => {
|
describe("The function filterAndJoin", () => {
|
||||||
it("should join and array of strings to one string", () => {
|
it("should join and array of strings to one string", () => {
|
||||||
const result = utilServerRewire.filterAndJoin(["one", "two", "three"]);
|
const result = utilServerRewire.filterAndJoin(["one", "two", "three"]);
|
||||||
|
@ -185,4 +251,3 @@ describe("The function filterAndJoin", () => {
|
||||||
expect(result).toBe("");
|
expect(result).toBe("");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue