Merge remote-tracking branch 'louislam/master' into feature/add-support-for-method-body-and-headers

This commit is contained in:
Bert Verhelst 2021-10-09 11:13:16 +02:00
commit daab2a05f5
66 changed files with 2786 additions and 160 deletions

View file

@ -9,6 +9,10 @@ assignees: ''
**Is it a duplicate question?** **Is it a duplicate question?**
Please search in Issues without filters: https://github.com/louislam/uptime-kuma/issues?q= Please search in Issues without filters: https://github.com/louislam/uptime-kuma/issues?q=
**Describe your problem**
**Info** **Info**
Uptime Kuma Version: Uptime Kuma Version:
Using Docker?: Yes/No Using Docker?: Yes/No

View file

@ -16,7 +16,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [macos-latest, ubuntu-latest, windows-latest] os: [macos-latest, ubuntu-latest, windows-latest]
node-version: [14.x, 15.x, 16.x] node-version: [14.x, 16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps: steps:
@ -27,7 +27,7 @@ jobs:
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'npm' cache: 'npm'
- run: npm ci - run: npm run install-legacy
- run: npm run build - run: npm run build
- run: npm test - run: npm test
env: env:

View file

@ -92,7 +92,7 @@ Light Mode:
Status Page: Status Page:
<img src="https://user-images.githubusercontent.com/1336778/133384019-962e1120-6c3a-481f-9d07-d7df765e9ba4.png" width="512" alt="" /> <img src="https://user-images.githubusercontent.com/1336778/134628766-a3fe0981-0926-4285-ab46-891a21c3e4cb.png" width="512" alt="" />
Settings Page: Settings Page:

7
babel.config.js Normal file
View file

@ -0,0 +1,7 @@
const config = {};
if (process.env.TEST_FRONTEND) {
config.presets = ["@babel/preset-env"];
}
module.exports = config;

5
jest-frontend.config.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
"rootDir": ".",
"testRegex": "./test/frontend.spec.js",
};

11
jest.config.js Normal file
View file

@ -0,0 +1,11 @@
module.exports = {
"verbose": true,
"preset": "jest-puppeteer",
"globals": {
"__DEV__": true
},
"testRegex": "./test/e2e.spec.js",
"rootDir": ".",
"testTimeout": 30000,
};

2281
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,8 @@
"build": "vite build", "build": "vite build",
"test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test", "test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test",
"test-with-build": "npm run build && npm test", "test-with-build": "npm run build && npm test",
"jest": "node test/prepare-jest.js && jest", "jest": "node test/prepare-jest.js && npm run jest-frontend && jest ",
"jest-frontend": "cross-env TEST_FRONTEND=1 jest --config=./jest-frontend.config.js",
"tsc": "tsc", "tsc": "tsc",
"vite-preview-dist": "vite preview --host", "vite-preview-dist": "vite preview --host",
"build-docker": "npm run build-docker-debian && npm run build-docker-alpine", "build-docker": "npm run build-docker-debian && npm run build-docker-alpine",
@ -98,6 +99,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "~7.15.7", "@babel/eslint-parser": "~7.15.7",
"@babel/preset-env": "^7.15.8",
"@types/bootstrap": "~5.1.6", "@types/bootstrap": "~5.1.6",
"@vitejs/plugin-legacy": "~1.6.1", "@vitejs/plugin-legacy": "~1.6.1",
"@vitejs/plugin-vue": "~1.9.2", "@vitejs/plugin-vue": "~1.9.2",
@ -115,15 +117,5 @@
"stylelint-config-standard": "~22.0.0", "stylelint-config-standard": "~22.0.0",
"typescript": "~4.4.3", "typescript": "~4.4.3",
"vite": "~2.6.4" "vite": "~2.6.4"
},
"jest": {
"verbose": true,
"preset": "jest-puppeteer",
"globals": {
"__DEV__": true
},
"testRegex": "./test/*.spec.js",
"rootDir": ".",
"testTimeout": 30000
} }
} }

View file

@ -1,6 +1,5 @@
const { setSetting } = require("./util-server"); const { setSetting } = require("./util-server");
const axios = require("axios"); const axios = require("axios");
const { isDev } = require("../src/util");
exports.version = require("../package.json").version; exports.version = require("../package.json").version;
exports.latestVersion = null; exports.latestVersion = null;

View file

@ -4,6 +4,8 @@
const { TimeLogger } = require("../src/util"); const { TimeLogger } = require("../src/util");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { io } = require("./server"); const { io } = require("./server");
const { setting } = require("./util-server");
const checkVersion = require("./check-version");
async function sendNotificationList(socket) { async function sendNotificationList(socket) {
const timeLogger = new TimeLogger(); const timeLogger = new TimeLogger();
@ -14,10 +16,10 @@ async function sendNotificationList(socket) {
]); ]);
for (let bean of list) { for (let bean of list) {
result.push(bean.export()) result.push(bean.export());
} }
io.to(socket.userID).emit("notificationList", result) io.to(socket.userID).emit("notificationList", result);
timeLogger.print("Send Notification List"); timeLogger.print("Send Notification List");
@ -39,7 +41,7 @@ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite =
LIMIT 100 LIMIT 100
`, [ `, [
monitorID, monitorID,
]) ]);
let result = list.reverse(); let result = list.reverse();
@ -69,7 +71,7 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove
LIMIT 500 LIMIT 500
`, [ `, [
monitorID, monitorID,
]) ]);
timeLogger.print(`[Monitor: ${monitorID}] sendImportantHeartbeatList`); timeLogger.print(`[Monitor: ${monitorID}] sendImportantHeartbeatList`);
@ -81,8 +83,18 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove
} }
async function sendInfo(socket) {
socket.emit("info", {
version: checkVersion.version,
latestVersion: checkVersion.latestVersion,
primaryBaseURL: await setting("primaryBaseURL")
});
}
module.exports = { module.exports = {
sendNotificationList, sendNotificationList,
sendImportantHeartbeatList, sendImportantHeartbeatList,
sendHeartbeatList, sendHeartbeatList,
} sendInfo
};

View file

@ -7,7 +7,7 @@ class Discord extends NotificationProvider {
name = "discord"; name = "discord";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
const discordDisplayName = notification.discordUsername || "Uptime Kuma"; const discordDisplayName = notification.discordUsername || "Uptime Kuma";

View file

@ -6,7 +6,7 @@ class Gotify extends NotificationProvider {
name = "gotify"; name = "gotify";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
if (notification.gotifyserverurl && notification.gotifyserverurl.endsWith("/")) { if (notification.gotifyserverurl && notification.gotifyserverurl.endsWith("/")) {
notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1); notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1);

View file

@ -7,7 +7,7 @@ class Line extends NotificationProvider {
name = "line"; name = "line";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
let lineAPIUrl = "https://api.line.me/v2/bot/message/push"; let lineAPIUrl = "https://api.line.me/v2/bot/message/push";
let config = { let config = {

View file

@ -7,7 +7,7 @@ class LunaSea extends NotificationProvider {
name = "lunasea"; name = "lunasea";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice
try { try {

View file

@ -0,0 +1,45 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
const Crypto = require("crypto");
const { debug } = require("../../src/util");
class Matrix extends NotificationProvider {
name = "matrix";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
const size = 20;
const randomString = encodeURIComponent(
Crypto
.randomBytes(size)
.toString("base64")
.slice(0, size)
);
debug("Random String: " + randomString);
const roomId = encodeURIComponent(notification.internalRoomId);
debug("Matrix Room ID: " + roomId);
try {
let config = {
headers: {
"Authorization": `Bearer ${notification.accessToken}`,
}
};
let data = {
"msgtype": "m.text",
"body": msg
};
await axios.put(`${notification.homeserverUrl}/_matrix/client/r0/rooms/${roomId}/send/m.room.message/${randomString}`, data, config);
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = Matrix;

View file

@ -7,7 +7,7 @@ class Mattermost extends NotificationProvider {
name = "mattermost"; name = "mattermost";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
const mattermostUserName = notification.mattermostusername || "Uptime Kuma"; const mattermostUserName = notification.mattermostusername || "Uptime Kuma";
// If heartbeatJSON is null, assume we're testing. // If heartbeatJSON is null, assume we're testing.

View file

@ -6,30 +6,54 @@ class Octopush extends NotificationProvider {
name = "octopush"; name = "octopush";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
let config = { // Default - V2
headers: { if (notification.octopushVersion == 2 || !notification.octopushVersion) {
"api-key": notification.octopushAPIKey, let config = {
"api-login": notification.octopushLogin, headers: {
"cache-control": "no-cache" "api-key": notification.octopushAPIKey,
} "api-login": notification.octopushLogin,
}; "cache-control": "no-cache"
let data = {
"recipients": [
{
"phone_number": notification.octopushPhoneNumber
} }
], };
//octopush not supporting non ascii char let data = {
"text": msg.replace(/[^\x00-\x7F]/g, ""), "recipients": [
"type": notification.octopushSMSType, {
"purpose": "alert", "phone_number": notification.octopushPhoneNumber
"sender": notification.octopushSenderName }
}; ],
//octopush not supporting non ascii char
"text": msg.replace(/[^\x00-\x7F]/g, ""),
"type": notification.octopushSMSType,
"purpose": "alert",
"sender": notification.octopushSenderName
};
await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config)
} else if (notification.octopushVersion == 1) {
let data = {
"user_login": notification.octopushDMLogin,
"api_key": notification.octopushDMAPIKey,
"sms_recipients": notification.octopushDMPhoneNumber,
"sms_sender": notification.octopushDMSenderName,
"sms_type": (notification.octopushDMSMSType == "sms_premium") ? "FR" : "XXX",
"transactional": "1",
//octopush not supporting non ascii char
"sms_text": msg.replace(/[^\x00-\x7F]/g, ""),
};
let config = {
headers: {
"cache-control": "no-cache"
},
params: data
};
await axios.post("https://www.octopush-dm.com/api/sms/json", {}, config)
} else {
throw new Error("Unknown Octopush version!");
}
await axios.post("https://api.octopush.com/v1/public/sms-campaign/send", data, config)
return okMsg; return okMsg;
} catch (error) { } catch (error) {
this.throwGeneralAxiosError(error); this.throwGeneralAxiosError(error);

View file

@ -0,0 +1,41 @@
const NotificationProvider = require("./notification-provider");
const axios = require("axios");
class PromoSMS extends NotificationProvider {
name = "promosms";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully.";
try {
let config = {
headers: {
"Content-Type": "application/json",
"Authorization": "Basic " + Buffer.from(notification.promosmsLogin + ":" + notification.promosmsPassword).toString('base64'),
"Accept": "text/json",
}
};
let data = {
"recipients": [ notification.promosmsPhoneNumber ],
//Lets remove non ascii char
"text": msg.replace(/[^\x00-\x7F]/g, ""),
"type": Number(notification.promosmsSMSType),
"sender": notification.promosmsSenderName
};
let resp = await axios.post("https://promosms.com/api/rest/v3_2/sms", data, config);
if (resp.data.response.status !== 0) {
let error = "Something gone wrong. Api returned " + resp.data.response.status + ".";
this.throwGeneralAxiosError(error);
}
return okMsg;
} catch (error) {
this.throwGeneralAxiosError(error);
}
}
}
module.exports = PromoSMS;

View file

@ -8,7 +8,7 @@ class Pushbullet extends NotificationProvider {
name = "pushbullet"; name = "pushbullet";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
let pushbulletUrl = "https://api.pushbullet.com/v2/pushes"; let pushbulletUrl = "https://api.pushbullet.com/v2/pushes";

View file

@ -6,7 +6,7 @@ class Pushover extends NotificationProvider {
name = "pushover"; name = "pushover";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
let pushoverlink = "https://api.pushover.net/1/messages.json" let pushoverlink = "https://api.pushover.net/1/messages.json"
try { try {

View file

@ -6,7 +6,7 @@ class Pushy extends NotificationProvider {
name = "pushy"; name = "pushy";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
await axios.post(`https://api.pushy.me/push?api_key=${notification.pushyAPIKey}`, { await axios.post(`https://api.pushy.me/push?api_key=${notification.pushyAPIKey}`, {

View file

@ -9,7 +9,7 @@ class RocketChat extends NotificationProvider {
name = "rocket.chat"; name = "rocket.chat";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
if (heartbeatJSON == null) { if (heartbeatJSON == null) {
let data = { let data = {

View file

@ -6,7 +6,7 @@ class Signal extends NotificationProvider {
name = "signal"; name = "signal";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
let data = { let data = {

View file

@ -25,7 +25,7 @@ class Slack extends NotificationProvider {
} }
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
if (heartbeatJSON == null) { if (heartbeatJSON == null) {
let data = { let data = {

View file

@ -87,7 +87,7 @@ class Teams extends NotificationProvider {
}; };
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
if (heartbeatJSON == null) { if (heartbeatJSON == null) {

View file

@ -6,7 +6,7 @@ class Telegram extends NotificationProvider {
name = "telegram"; name = "telegram";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, { await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {

View file

@ -7,7 +7,7 @@ class Webhook extends NotificationProvider {
name = "webhook"; name = "webhook";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let okMsg = "Sent Successfully. "; let okMsg = "Sent Successfully.";
try { try {
let data = { let data = {

View file

@ -5,7 +5,9 @@ const Gotify = require("./notification-providers/gotify");
const Line = require("./notification-providers/line"); const Line = require("./notification-providers/line");
const LunaSea = require("./notification-providers/lunasea"); const LunaSea = require("./notification-providers/lunasea");
const Mattermost = require("./notification-providers/mattermost"); const Mattermost = require("./notification-providers/mattermost");
const Matrix = require("./notification-providers/matrix");
const Octopush = require("./notification-providers/octopush"); const Octopush = require("./notification-providers/octopush");
const PromoSMS = require("./notification-providers/promosms");
const Pushbullet = require("./notification-providers/pushbullet"); const Pushbullet = require("./notification-providers/pushbullet");
const Pushover = require("./notification-providers/pushover"); const Pushover = require("./notification-providers/pushover");
const Pushy = require("./notification-providers/pushy"); const Pushy = require("./notification-providers/pushy");
@ -34,7 +36,9 @@ class Notification {
new Line(), new Line(),
new LunaSea(), new LunaSea(),
new Mattermost(), new Mattermost(),
new Matrix(),
new Octopush(), new Octopush(),
new PromoSMS(),
new Pushbullet(), new Pushbullet(),
new Pushover(), new Pushover(),
new Pushy(), new Pushy(),

View file

@ -90,7 +90,7 @@ const io = new Server(server);
module.exports.io = io; module.exports.io = io;
// Must be after io instantiation // Must be after io instantiation
const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList } = require("./client"); const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo } = require("./client");
const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler"); const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
app.use(express.json()); app.use(express.json());
@ -180,10 +180,7 @@ exports.entryPage = "dashboard";
console.log("Adding socket handler"); console.log("Adding socket handler");
io.on("connection", async (socket) => { io.on("connection", async (socket) => {
socket.emit("info", { sendInfo(socket);
version: checkVersion.version,
latestVersion: checkVersion.latestVersion,
});
totalClient++; totalClient++;
@ -873,6 +870,8 @@ exports.entryPage = "dashboard";
msg: "Saved" msg: "Saved"
}); });
sendInfo(socket);
} catch (e) { } catch (e) {
callback({ callback({
ok: false, ok: false,

View file

@ -3,7 +3,7 @@
@import "node_modules/bootstrap/scss/bootstrap"; @import "node_modules/bootstrap/scss/bootstrap";
#app { #app {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji; font-family: BlinkMacSystemFont, segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
} }
h1 { h1 {

View file

@ -11,18 +11,18 @@ export default {
computed: { computed: {
color() { color() {
if (this.status === 0) { if (this.status === 0) {
return "danger" return "danger";
} }
if (this.status === 1) { if (this.status === 1) {
return "primary" return "primary";
} }
if (this.status === 2) { if (this.status === 2) {
return "warning" return "warning";
} }
return "secondary" return "secondary";
}, },
text() { text() {
@ -41,11 +41,11 @@ export default {
return this.$t("Unknown"); return this.$t("Unknown");
}, },
}, },
} };
</script> </script>
<style scoped> <style scoped>
span { span {
width: 64px; min-width: 64px;
} }
</style> </style>

View file

@ -0,0 +1,34 @@
<template>
<div class="mb-3">
<label for="homeserver-url" class="form-label">Homeserver URL (with http(s):// and optionally port)</label><span style="color: red;"><sup>*</sup></span>
<input id="homeserver-url" v-model="$parent.notification.homeserverUrl" type="text" class="form-control" :required="true">
</div>
<div class="mb-3">
<label for="internal-room-id" class="form-label">Internal Room Id</label><span style="color: red;"><sup>*</sup></span>
<input id="internal-room-id" v-model="$parent.notification.internalRoomId" type="text" class="form-control" required="true">
</div>
<div class="mb-3">
<label for="access-token" class="form-label">Access Token</label><span style="color: red;"><sup>*</sup></span>
<HiddenInput id="access-token" v-model="$parent.notification.accessToken" :required="true" autocomplete="one-time-code" :maxlength="500"></HiddenInput>
</div>
<div class="form-text">
<span style="color: red;"><sup>*</sup></span>Required
<p style="margin-top: 8px;">
You can find the internal room ID by looking in the advanced section of the room settings in your Matrix client. It should look like !QMdRCpUIfLwsfjxye6:home.server.
</p>
<p style="margin-top: 8px;">
It is highly recommended you create a new user and do not use your own Matrix user's access token as it will allow full access to your account and all the rooms you joined. Instead, create a new user and only invite it to the room that you want to receive the notification in. You can get the access token by running <code>curl -XPOST -d '{"type": "m.login.password", "identifier": {"user": "botusername", "type": "m.id.user"}, "password": "passwordforuser"}' "https://home.server/_matrix/client/r0/login"</code>.
</p>
</div>
</template>
<script>
import HiddenInput from "../HiddenInput.vue";
export default {
components: {
HiddenInput,
},
}
</script>

View file

@ -1,4 +1,14 @@
<template> <template>
<div class="mb-3">
<label for="octopush-version" class="form-label">Octopush API Version</label>
<select id="octopush-version" v-model="$parent.notification.octopushVersion" class="form-select">
<option value="2">Octopush (endpoint: api.octopush.com)</option>
<option value="1">Legacy Octopush-DM (endpoint: www.octopush-dm.com)</option>
</select>
<div class="form-text">
Do you use the legacy version of Octopush (2011-2020) or the new version?
</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">API KEY</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>

View file

@ -0,0 +1,39 @@
<template>
<div class="mb-3">
<label for="promosms-login" class="form-label">API LOGIN</label>
<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>
<HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
</div>
<div class="mb-3">
<label for="promosms-type-sms" class="form-label">{{ $t("SMS Type") }}</label>
<select id="promosms-type-sms" v-model="$parent.notification.promosmsSMSType" class="form-select">
<option value="0">{{ $t("promosmsTypeFlash") }}</option>
<option value="1">{{ $t("promosmsTypeEco") }}</option>
<option value="2">{{ $t("promosmsTypeFull") }}</option>
<option value="3">{{ $t("promosmsTypeSpeed") }}</option>
</select>
<i18n-t tag="div" keypath="Check PromoSMS prices" class="form-text">
<a href="https://promosms.com/cennik/" target="_blank">https://promosms.com/cennik/</a>
</i18n-t>
</div>
<div class="mb-3">
<label for="promosms-phone-number" class="form-label">{{ $t("promosmsPhoneNumber") }}</label>
<input id="promosms-phone-number" v-model="$parent.notification.promosmsPhoneNumber" type="text" class="form-control" required>
</div>
<div class="mb-3">
<label for="promosms-sender-name" class="form-label">{{ $t("promosmsSMSSender") }}</label>
<input id="promosms-sender-name" v-model="$parent.notification.promosmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
</div>
</template>
<script>
import HiddenInput from "../HiddenInput.vue";
export default {
components: {
HiddenInput,
},
};
</script>

View file

@ -10,11 +10,13 @@ import Teams from "./Teams.vue";
import Pushover from "./Pushover.vue"; import Pushover from "./Pushover.vue";
import Pushy from "./Pushy.vue"; import Pushy from "./Pushy.vue";
import Octopush from "./Octopush.vue"; import Octopush from "./Octopush.vue";
import PromoSMS from "./PromoSMS.vue";
import LunaSea from "./LunaSea.vue"; import LunaSea from "./LunaSea.vue";
import Apprise from "./Apprise.vue"; import Apprise from "./Apprise.vue";
import Pushbullet from "./Pushbullet.vue"; import Pushbullet from "./Pushbullet.vue";
import Line from "./Line.vue"; import Line from "./Line.vue";
import Mattermost from "./Mattermost.vue"; import Mattermost from "./Mattermost.vue";
import Matrix from "./Matrix.vue";
/** /**
* Manage all notification form. * Manage all notification form.
@ -34,11 +36,13 @@ const NotificationFormList = {
"pushover": Pushover, "pushover": Pushover,
"pushy": Pushy, "pushy": Pushy,
"octopush": Octopush, "octopush": Octopush,
"promosms": PromoSMS,
"lunasea": LunaSea, "lunasea": LunaSea,
"apprise": Apprise, "apprise": Apprise,
"pushbullet": Pushbullet, "pushbullet": Pushbullet,
"line": Line, "line": Line,
"mattermost": Mattermost "mattermost": Mattermost,
"matrix": Matrix,
} }
export default NotificationFormList export default NotificationFormList

View file

@ -49,7 +49,10 @@ const languageList = {
const rtlLangs = ["fa"]; const rtlLangs = ["fa"];
export const currentLocale = () => localStorage.locale || "en"; export const currentLocale = () => localStorage.locale
|| languageList[navigator.language] && navigator.language
|| languageList[navigator.language.substring(0, 2)] && navigator.language.substring(0, 2)
|| "en";
export const localeDirection = () => { export const localeDirection = () => {
return rtlLangs.includes(currentLocale()) ? "rtl" : "ltr"; return rtlLangs.includes(currentLocale()) ? "rtl" : "ltr";

View file

@ -190,6 +190,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Поддържа 50+ услуги за инвестяване)", apprise: "Apprise (Поддържа 50+ услуги за инвестяване)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -191,6 +191,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -235,11 +235,51 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",
line: "Line Messenger", line: "Line Messenger",
mattermost: "Mattermost", mattermost: "Mattermost",
"User Key": "User Key",
"Device": "Device",
"Message Title": "Message Title",
"Notification Sound": "Notification Sound",
"More info on:": "More info on: {0}",
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.",
"SMS Type": "SMS Type",
octopushTypePremium: "Premium (Fast - recommended for alerting)",
octopushTypeLowCost: "Low Cost (Slow, sometimes blocked by operator)",
"Check octopush prices": "Check octopush prices {0}.",
octopushPhoneNumber: "Phone number (intl format, eg : +33612345678) ",
octopushSMSSender: "SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)",
"LunaSea Device ID": "LunaSea Device ID",
"Apprise URL": "Apprise URL",
"Example:": "Example: {0}",
"Read more:": "Read more: {0}",
"Status:": "Status: {0}",
"Read more": "Read more",
appriseInstalled: "Apprise is installed.",
appriseNotInstalled: "Apprise is not installed. {0}",
"Access Token": "Access Token",
"Channel access token": "Channel access token",
"Line Developers Console": "Line Developers Console",
lineDevConsoleTo: "Line Developers Console - {0}",
"Basic Settings": "Basic Settings",
"User ID": "User ID",
"Messaging API": "Messaging API",
wayToGetLineChannelToken: "First access the {0}, create a provider and channel (Messaging API), then you can get the channel access token and user id from the above mentioned menu items.",
"Icon URL": "Icon URL",
aboutIconURL: "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.",
aboutMattermostChannelName: "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",
"matrix": "Matrix",
promosmsTypeEco: "SMS ECO - cheap but slow and often overloaded. Limited only to Polish recipients.",
promosmsTypeFlash: "SMS FLASH - Message will automatically show on recipient device. Limited only to Polish recipients.",
promosmsTypeFull: "SMS FULL - Premium tier of SMS, You can use Your Sender Name (You need to register name first). Reliable for alerts.",
promosmsTypeFull: "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).",
promosmsPhoneNumber: "Phone number (for Polish recipient You can skip area codes)",
promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
// End notification form // End notification form
"Status Page": "Status Page", "Status Page": "Status Page",
Method: "Method", Method: "Method",

View file

@ -17,7 +17,7 @@ export default {
pauseMonitorMsg: "¿Seguro que quieres pausar?", pauseMonitorMsg: "¿Seguro que quieres pausar?",
Settings: "Ajustes", Settings: "Ajustes",
Dashboard: "Panel", Dashboard: "Panel",
"New Update": "Vueva actualización", "New Update": "Nueva actualización",
Language: "Idioma", Language: "Idioma",
Appearance: "Apariencia", Appearance: "Apariencia",
Theme: "Tema", Theme: "Tema",
@ -61,11 +61,11 @@ export default {
Retries: "Reintentos", Retries: "Reintentos",
Advanced: "Avanzado", Advanced: "Avanzado",
"Upside Down Mode": "Modo invertido", "Upside Down Mode": "Modo invertido",
"Max. Redirects": "Máx. redirecciones", "Max. Redirects": "Redirecciones Máximas",
"Accepted Status Codes": "Códigos de estado aceptados", "Accepted Status Codes": "Códigos de estado aceptados",
Save: "Guardar", Save: "Guardar",
Notifications: "Notificaciones", Notifications: "Notificaciones",
"Not available, please setup.": "No disponible, por favor configurar.", "Not available, please setup.": "No disponible, por favor configúrelo.",
"Setup Notification": "Configurar notificación", "Setup Notification": "Configurar notificación",
Light: "Claro", Light: "Claro",
Dark: "Oscuro", Dark: "Oscuro",
@ -83,11 +83,11 @@ export default {
"New Password": "Nueva contraseña", "New Password": "Nueva contraseña",
"Repeat New Password": "Repetir nueva contraseña", "Repeat New Password": "Repetir nueva contraseña",
"Update Password": "Actualizar contraseña", "Update Password": "Actualizar contraseña",
"Disable Auth": "Deshabilitar Autenticación ", "Disable Auth": "Deshabilitar Autenticación",
"Enable Auth": "Habilitar Autenticación ", "Enable Auth": "Habilitar Autenticación",
Logout: "Cerrar sesión", Logout: "Cerrar sesión",
Leave: "Salir", Leave: "Salir",
"I understand, please disable": "Lo comprendo, por favor deshabilitar", "I understand, please disable": "Entiendo, por favor deshabilitar",
Confirm: "Confirmar", Confirm: "Confirmar",
Yes: "Sí", Yes: "Sí",
No: "No", No: "No",
@ -100,7 +100,7 @@ export default {
"Notification Type": "Tipo de notificación", "Notification Type": "Tipo de notificación",
Email: "Email", Email: "Email",
Test: "Test", Test: "Test",
"Certificate Info": "Información del certificado ", "Certificate Info": "Información del certificado",
"Resolver Server": "Servidor de resolución", "Resolver Server": "Servidor de resolución",
"Resource Record Type": "Tipo de Registro", "Resource Record Type": "Tipo de Registro",
"Last Result": "Último resultado", "Last Result": "Último resultado",
@ -108,78 +108,78 @@ export default {
"Repeat Password": "Repetir contraseña", "Repeat Password": "Repetir contraseña",
respTime: "Tiempo de resp. (ms)", respTime: "Tiempo de resp. (ms)",
notAvailableShort: "N/A", notAvailableShort: "N/A",
Create: "Create", Create: "Crear",
clearEventsMsg: "Are you sure want to delete all events for this monitor?", clearEventsMsg: "¿Está seguro de que desea eliminar todos los eventos de este monitor?",
clearHeartbeatsMsg: "Are you sure want to delete all heartbeats for this monitor?", clearHeartbeatsMsg: "¿Está seguro de que desea eliminar todos los latidos de este monitor?",
confirmClearStatisticsMsg: "Are you sure want to delete ALL statistics?", confirmClearStatisticsMsg: "¿Está seguro de que desea eliminar TODAS las estadísticas?",
"Clear Data": "Clear Data", "Clear Data": "Borrar Datos",
Events: "Events", Events: "Eventos",
Heartbeats: "Heartbeats", Heartbeats: "Latidos",
"Auto Get": "Auto Get", "Auto Get": "Obtener automáticamente",
enableDefaultNotificationDescription: "For every new monitor this notification will be enabled by default. You can still disable the notification separately for each monitor.", enableDefaultNotificationDescription: "Para cada nuevo monitor, esta notificación estará habilitada de forma predeterminada. Aún puede deshabilitar la notificación por separado para cada monitor.",
"Default enabled": "Default enabled", "Default enabled": "Habilitado por defecto",
"Also apply to existing monitors": "Also apply to existing monitors", "Also apply to existing monitors": "También se aplica a monitores existentes",
Export: "Export", Export: "Exportar",
Import: "Import", Import: "Importar",
backupDescription: "You can backup all monitors and all notifications into a JSON file.", backupDescription: "Puede hacer una copia de seguridad de todos los monitores y todas las notificaciones en un archivo JSON.",
backupDescription2: "PS: History and event data is not included.", backupDescription2: "PD: el historial y los datos de eventos no están incluidos.",
backupDescription3: "Sensitive data such as notification tokens is included in the export file, please keep it carefully.", backupDescription3: "Los datos confidenciales, como los tokens de notificación, se incluyen en el archivo de exportación. Guárdelo con cuidado.",
alertNoFile: "Please select a file to import.", alertNoFile: "Seleccione un archivo para importar.",
alertWrongFileType: "Please select a JSON file.", alertWrongFileType: "Seleccione un archivo JSON.",
twoFAVerifyLabel: "Please type in your token to verify that 2FA is working", twoFAVerifyLabel: "Ingrese su token para verificar que 2FA está funcionando",
tokenValidSettingsMsg: "Token is valid! You can now save the 2FA settings.", tokenValidSettingsMsg: "¡El token es válido! Ahora puede guardar la configuración de 2FA.",
confirmEnableTwoFAMsg: "Are you sure you want to enable 2FA?", confirmEnableTwoFAMsg: "¿Estás seguro de que quieres habilitar 2FA?",
confirmDisableTwoFAMsg: "Are you sure you want to disable 2FA?", confirmDisableTwoFAMsg: "¿Estás seguro de que quieres desactivar 2FA?",
"Apply on all existing monitors": "Apply on all existing monitors", "Apply on all existing monitors": "Aplicar en todos los monitores existentes",
"Verify Token": "Verify Token", "Verify Token": "Verificar token",
"Setup 2FA": "Setup 2FA", "Setup 2FA": "Configurar 2FA",
"Enable 2FA": "Enable 2FA", "Enable 2FA": "Habilitar 2FA",
"Disable 2FA": "Disable 2FA", "Disable 2FA": "Desactivar 2FA",
"2FA Settings": "2FA Settings", "2FA Settings": "Ajustes 2FA",
"Two Factor Authentication": "Two Factor Authentication", "Two Factor Authentication": "Autenticación de dos factores",
Active: "Active", Active: "Activo",
Inactive: "Inactive", Inactive: "Inactivo",
Token: "Token", Token: "Token",
"Show URI": "Show URI", "Show URI": "Mostrar URI",
"Clear all statistics": "Clear all Statistics", "Clear all statistics": "Borrar todas las estadísticas",
retryCheckEverySecond: "Retry every {0} seconds.", retryCheckEverySecond: "Reintentar cada {0} segundo.",
importHandleDescription: "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.", importHandleDescription: "Elija 'Omitir existente' si desea omitir todos los monitores o notificaciones con el mismo nombre. 'Sobrescribir' eliminará todos los monitores y notificaciones existentes.",
confirmImportMsg: "Are you sure to import the backup? Please make sure you've selected the right import option.", confirmImportMsg: "¿Estás seguro de importar la copia de seguridad? Asegúrese de haber seleccionado la opción de importación correcta.",
"Heartbeat Retry Interval": "Heartbeat Retry Interval", "Heartbeat Retry Interval": "Intervalo de reintento de latido",
"Import Backup": "Import Backup", "Import Backup": "Importar copia de seguridad",
"Export Backup": "Export Backup", "Export Backup": "Exportar copia de seguridad",
"Skip existing": "Skip existing", "Skip existing": "Omitir existente",
Overwrite: "Overwrite", Overwrite: "Sobrescribir",
Options: "Options", Options: "Opciones",
"Keep both": "Keep both", "Keep both": "Mantén ambos",
Tags: "Tags", Tags: "Etiquetas",
"Add New below or Select...": "Add New below or Select...", "Add New below or Select...": "Agregar nuevo a continuación o Seleccionar...",
"Tag with this name already exist.": "Tag with this name already exist.", "Tag with this name already exist.": "La etiqueta con este nombre ya existe.",
"Tag with this value already exist.": "Tag with this value already exist.", "Tag with this value already exist.": "La etiqueta con este valor ya existe.",
color: "color", color: "color",
"value (optional)": "value (optional)", "value (optional)": "valor (opcional)",
Gray: "Gray", Gray: "Gris",
Red: "Red", Red: "Rojo",
Orange: "Orange", Orange: "Naranja",
Green: "Green", Green: "Verde",
Blue: "Blue", Blue: "Azul",
Indigo: "Indigo", Indigo: "Índigo",
Purple: "Purple", Purple: "Morado",
Pink: "Pink", Pink: "Rosa",
"Search...": "Search...", "Search...": "Buscar...",
"Avg. Ping": "Avg. Ping", "Avg. Ping": "Ping promedio",
"Avg. Response": "Avg. Response", "Avg. Response": "Respuesta promedio",
"Entry Page": "Entry Page", "Entry Page": "Página de entrada",
statusPageNothing: "Nothing here, please add a group or a monitor.", statusPageNothing: "No hay nada aquí, agregue un grupo o un monitor.",
"No Services": "No Services", "No Services": "Sin servicio",
"All Systems Operational": "All Systems Operational", "All Systems Operational": "Todos los sistemas están operativos",
"Partially Degraded Service": "Partially Degraded Service", "Partially Degraded Service": "Servicio parcialmente degradado",
"Degraded Service": "Degraded Service", "Degraded Service": "Servicio degradado",
"Add Group": "Add Group", "Add Group": "Agregar Grupo",
"Add a monitor": "Add a monitor", "Add a monitor": "Agregar un monitor",
"Edit Status Page": "Edit Status Page", "Edit Status Page": "Editar página de estado",
"Go to Dashboard": "Go to Dashboard", "Go to Dashboard": "Ir al panel de control",
"Status Page": "Status Page", "Status Page": "Página de estado",
telegram: "Telegram", telegram: "Telegram",
webhook: "Webhook", webhook: "Webhook",
smtp: "Email (SMTP)", smtp: "Email (SMTP)",
@ -192,8 +192,9 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Admite más de 50 servicios de notificación)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",
line: "Line Messenger", line: "Line Messenger",
mattermost: "Mattermost", mattermost: "Mattermost",

View file

@ -191,6 +191,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (vahendab üle 65 teavitusteenust)", apprise: "Apprise (vahendab üle 65 teavitusteenust)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -199,6 +199,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -190,6 +190,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -191,6 +191,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (obsługuje 50+ usług powiadamiania)", apprise: "Apprise (obsługuje 50+ usług powiadamiania)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -191,6 +191,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -196,6 +196,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -191,6 +191,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (Support 50+ Notification services)", apprise: "Apprise (Support 50+ Notification services)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -192,6 +192,7 @@ export default {
pushover: "Pushover", pushover: "Pushover",
pushy: "Pushy", pushy: "Pushy",
octopush: "Octopush", octopush: "Octopush",
promosms: "PromoSMS",
lunasea: "LunaSea", lunasea: "LunaSea",
apprise: "Apprise (支援 50 多種通知)", apprise: "Apprise (支援 50 多種通知)",
pushbullet: "Pushbullet", pushbullet: "Pushbullet",

View file

@ -43,3 +43,9 @@ app.component("Editable", contenteditable);
app.component("FontAwesomeIcon", FontAwesomeIcon); app.component("FontAwesomeIcon", FontAwesomeIcon);
app.mount("#app"); app.mount("#app");
// Expose the vue instance for development
if (process.env.NODE_ENV === "development") {
console.log("Dev Only: window.app is the vue instance");
window.app = app._instance;
}

View file

@ -38,11 +38,15 @@ export default {
}, },
baseURL() { baseURL() {
if (this.$root.info.primaryBaseURL) {
return this.$root.info.primaryBaseURL;
}
if (env === "development" || localStorage.dev === "dev") { if (env === "development" || localStorage.dev === "dev") {
return axios.defaults.baseURL; return axios.defaults.baseURL;
} else { } else {
return location.protocol + "//" + location.host; return location.protocol + "//" + location.host;
} }
} },
} }
}; };

View file

@ -171,7 +171,7 @@
<input id="retry-interval" v-model="monitor.retryInterval" type="number" class="form-control" required min="20" step="1"> <input id="retry-interval" v-model="monitor.retryInterval" type="number" class="form-control" required min="20" step="1">
</div> </div>
<h2 class="mt-5 mb-2">{{ $t("Advanced") }}</h2> <h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3 form-check"> <div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3 form-check">
<input id="ignore-tls" v-model="monitor.ignoreTls" class="form-check-input" type="checkbox" value=""> <input id="ignore-tls" v-model="monitor.ignoreTls" class="form-check-input" type="checkbox" value="">
@ -180,7 +180,7 @@
</label> </label>
</div> </div>
<div class="my-3 form-check"> <div v-if="monitor.type !== 'push'" class="my-3 form-check">
<input id="upside-down" v-model="monitor.upsideDown" class="form-check-input" type="checkbox"> <input id="upside-down" v-model="monitor.upsideDown" class="form-check-input" type="checkbox">
<label class="form-check-label" for="upside-down"> <label class="form-check-label" for="upside-down">
{{ $t("Upside Down Mode") }} {{ $t("Upside Down Mode") }}

View file

@ -74,7 +74,7 @@ class TimeLogger {
this.startTime = dayjs().valueOf(); this.startTime = dayjs().valueOf();
} }
print(name) { print(name) {
if (exports.isDev && process && process.env.TIMELOGGER === "1") { if (exports.isDev && process.env.TIMELOGGER === "1") {
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms"); console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms");
} }
} }

View file

@ -86,7 +86,7 @@ export class TimeLogger {
} }
print(name: string) { print(name: string) {
if (isDev && process && process.env.TIMELOGGER === "1") { if (isDev && process.env.TIMELOGGER === "1") {
console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms") console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms")
} }
} }

View file

@ -36,7 +36,7 @@ describe("Init", () => {
}); });
it(`should be titled "${title}"`, async () => { it(`should be titled "${title}"`, async () => {
await expect(page.title()).resolves.toMatch(title); await expect(page.title()).resolves.toEqual(title);
}); });
// Setup Page // Setup Page
@ -75,11 +75,11 @@ describe("Init", () => {
await page.select("#language", "zh-HK"); await page.select("#language", "zh-HK");
let languageTitle = await page.evaluate(() => document.querySelector("[for=language]").innerText); let languageTitle = await page.evaluate(() => document.querySelector("[for=language]").innerText);
expect(languageTitle).toMatch("語言"); expect(languageTitle).toEqual("語言");
await page.select("#language", "en"); await page.select("#language", "en");
languageTitle = await page.evaluate(() => document.querySelector("[for=language]").innerText); languageTitle = await page.evaluate(() => document.querySelector("[for=language]").innerText);
expect(languageTitle).toMatch("Language"); expect(languageTitle).toEqual("Language");
}); });
it("Change Theme", async () => { it("Change Theme", async () => {
@ -103,21 +103,21 @@ describe("Init", () => {
it("Search Engine Visibility", async () => { it("Search Engine Visibility", async () => {
// Default // Default
let res = await axios.get(baseURL + "/robots.txt"); let res = await axios.get(baseURL + "/robots.txt");
expect(res.data).toMatch("Disallow: /"); expect(res.data).toContain("Disallow: /");
// Yes // Yes
await click(page, "#searchEngineIndexYes"); await click(page, "#searchEngineIndexYes");
await click(page, "form > div > .btn[type=submit]"); await click(page, "form > div > .btn[type=submit]");
await sleep(2000); await sleep(2000);
res = await axios.get(baseURL + "/robots.txt"); res = await axios.get(baseURL + "/robots.txt");
expect(res.data).not.toMatch("Disallow: /"); expect(res.data).not.toContain("Disallow: /");
// No // No
await click(page, "#searchEngineIndexNo"); await click(page, "#searchEngineIndexNo");
await click(page, "form > div > .btn[type=submit]"); await click(page, "form > div > .btn[type=submit]");
await sleep(2000); await sleep(2000);
res = await axios.get(baseURL + "/robots.txt"); res = await axios.get(baseURL + "/robots.txt");
expect(res.data).toMatch("Disallow: /"); expect(res.data).toContain("Disallow: /");
}); });
it("Entry Page", async () => { it("Entry Page", async () => {
@ -218,7 +218,7 @@ describe("Init", () => {
await page.goto(baseURL + "/status"); await page.goto(baseURL + "/status");
}); });
it(`should be titled "${title}"`, async () => { it(`should be titled "${title}"`, async () => {
await expect(page.title()).resolves.toMatch(title); await expect(page.title()).resolves.toEqual(title);
}); });
}); });
}); });

42
test/frontend.spec.js Normal file
View file

@ -0,0 +1,42 @@
// eslint-disable-next-line no-global-assign
global.localStorage = {};
global.navigator = {
language: "en"
};
const { currentLocale } = require("../src/i18n");
describe("Test i18n.js", () => {
it("currentLocale()", () => {
expect(currentLocale()).toEqual("en");
navigator.language = "zh-HK";
expect(currentLocale()).toEqual("zh-HK");
// Note that in Safari on iOS prior to 10.2, the country code returned is lowercase: "en-us", "fr-fr" etc.
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language
navigator.language = "zh-hk";
expect(currentLocale()).toEqual("en");
navigator.language = "en-US";
expect(currentLocale()).toEqual("en");
navigator.language = "ja-ZZ";
expect(currentLocale()).toEqual("ja");
navigator.language = "zz";
expect(currentLocale()).toEqual("en");
navigator.language = "zz-ZZ";
expect(currentLocale()).toEqual("en");
localStorage.locale = "en";
expect(currentLocale()).toEqual("en");
localStorage.locale = "zh-HK";
expect(currentLocale()).toEqual("zh-HK");
});
});

View file

@ -6,5 +6,4 @@ FROM ubuntu
# RUN ln -s /usr/bin/nodejs /usr/bin/node # RUN ln -s /usr/bin/nodejs /usr/bin/node
# RUN node -v # RUN node -v
COPY ./install.sh . RUN curl -o kuma_install.sh http://git.kuma.pet/install.sh && bash kuma_install.sh local /opt/uptime-kuma 3000 0.0.0.0
RUN bash install.sh local /opt/uptime-kuma 3000 0.0.0.0