commit
d62cb6147c
@ -0,0 +1,26 @@
|
||||
|
||||
name: Close Incorrect Issue
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
close-incorrect-issue:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node-version: [16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: node extra/close-incorrect-issue.js ${{ secrets.GITHUB_TOKEN }} ${{ github.event.issue.number }} ${{ github.event.issue.user.login }}
|
@ -0,0 +1,33 @@
|
||||
const PuppeteerEnvironment = require("jest-environment-puppeteer");
|
||||
const util = require("util");
|
||||
|
||||
class DebugEnv extends PuppeteerEnvironment {
|
||||
async handleTestEvent(event, state) {
|
||||
const ignoredEvents = [
|
||||
"setup",
|
||||
"add_hook",
|
||||
"start_describe_definition",
|
||||
"add_test",
|
||||
"finish_describe_definition",
|
||||
"run_start",
|
||||
"run_describe_start",
|
||||
"test_start",
|
||||
"hook_start",
|
||||
"hook_success",
|
||||
"test_fn_start",
|
||||
"test_fn_success",
|
||||
"test_done",
|
||||
"run_describe_finish",
|
||||
"run_finish",
|
||||
"teardown",
|
||||
"test_fn_failure",
|
||||
];
|
||||
if (!ignoredEvents.includes(event.name)) {
|
||||
console.log(
|
||||
new Date().toString() + ` Unhandled event [${event.name}] ` + util.inspect(event)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DebugEnv;
|
@ -1,6 +1,20 @@
|
||||
module.exports = {
|
||||
"launch": {
|
||||
"dumpio": true,
|
||||
"slowMo": 500,
|
||||
"headless": process.env.HEADLESS_TEST || false,
|
||||
"userDataDir": "./data/test-chrome-profile",
|
||||
args: [
|
||||
"--disable-setuid-sandbox",
|
||||
"--disable-gpu",
|
||||
"--disable-dev-shm-usage",
|
||||
"--no-default-browser-check",
|
||||
"--no-experiments",
|
||||
"--no-first-run",
|
||||
"--no-pings",
|
||||
"--no-sandbox",
|
||||
"--no-zygote",
|
||||
"--single-process",
|
||||
],
|
||||
}
|
||||
};
|
||||
|
@ -0,0 +1,10 @@
|
||||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD basic_auth_user TEXT default null;
|
||||
|
||||
ALTER TABLE monitor
|
||||
ADD basic_auth_pass TEXT default null;
|
||||
|
||||
COMMIT;
|
@ -0,0 +1,57 @@
|
||||
const github = require("@actions/github");
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const token = process.argv[2];
|
||||
const issueNumber = process.argv[3];
|
||||
const username = process.argv[4];
|
||||
|
||||
const client = github.getOctokit(token).rest;
|
||||
|
||||
const issue = {
|
||||
owner: "louislam",
|
||||
repo: "uptime-kuma",
|
||||
number: issueNumber,
|
||||
};
|
||||
|
||||
const labels = (
|
||||
await client.issues.listLabelsOnIssue({
|
||||
owner: issue.owner,
|
||||
repo: issue.repo,
|
||||
issue_number: issue.number
|
||||
})
|
||||
).data.map(({ name }) => name);
|
||||
|
||||
if (labels.length === 0) {
|
||||
console.log("Bad format here");
|
||||
|
||||
await client.issues.addLabels({
|
||||
owner: issue.owner,
|
||||
repo: issue.repo,
|
||||
issue_number: issue.number,
|
||||
labels: ["invalid-format"]
|
||||
});
|
||||
|
||||
// Add the issue closing comment
|
||||
await client.issues.createComment({
|
||||
owner: issue.owner,
|
||||
repo: issue.repo,
|
||||
issue_number: issue.number,
|
||||
body: `@${username}: Hello! :wave:\n\nThis issue is being automatically closed because it does not follow the issue template. Please DO NOT open a blank issue.`
|
||||
});
|
||||
|
||||
// Close the issue
|
||||
await client.issues.update({
|
||||
owner: issue.owner,
|
||||
repo: issue.repo,
|
||||
issue_number: issue.number,
|
||||
state: "closed"
|
||||
});
|
||||
} else {
|
||||
console.log("Pass!");
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,60 @@
|
||||
console.log("== Uptime Kuma Remove 2FA Tool ==");
|
||||
console.log("Loading the database");
|
||||
|
||||
const Database = require("../server/database");
|
||||
const { R } = require("redbean-node");
|
||||
const readline = require("readline");
|
||||
const TwoFA = require("../server/2fa");
|
||||
const args = require("args-parser")(process.argv);
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
Database.init(args);
|
||||
await Database.connect();
|
||||
|
||||
try {
|
||||
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
|
||||
if (!process.env.TEST_BACKEND) {
|
||||
const user = await R.findOne("user");
|
||||
if (! user) {
|
||||
throw new Error("user not found, have you installed?");
|
||||
}
|
||||
|
||||
console.log("Found user: " + user.username);
|
||||
|
||||
let ans = await question("Are you sure want to remove 2FA? [y/N]");
|
||||
|
||||
if (ans.toLowerCase() === "y") {
|
||||
await TwoFA.disable2FA(user.id);
|
||||
console.log("2FA has been removed successfully.");
|
||||
}
|
||||
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error: " + e.message);
|
||||
}
|
||||
|
||||
await Database.close();
|
||||
rl.close();
|
||||
|
||||
console.log("Finished.");
|
||||
};
|
||||
|
||||
function question(question) {
|
||||
return new Promise((resolve) => {
|
||||
rl.question(question, (answer) => {
|
||||
resolve(answer);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!process.env.TEST_BACKEND) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
main,
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,14 @@
|
||||
const { checkLogin } = require("./util-server");
|
||||
const { R } = require("redbean-node");
|
||||
|
||||
class TwoFA {
|
||||
|
||||
static async disable2FA(userID) {
|
||||
return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [
|
||||
userID,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = TwoFA;
|
@ -0,0 +1,47 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
const { setting } = require("../util-server");
|
||||
const { getMonitorRelativeURL } = require("../../src/util");
|
||||
const { DOWN, UP } = require("../../src/util");
|
||||
|
||||
class GoogleChat extends NotificationProvider {
|
||||
|
||||
name = "GoogleChat";
|
||||
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
try {
|
||||
// Google Chat message formatting: https://developers.google.com/chat/api/guides/message-formats/basic
|
||||
|
||||
let textMsg = ''
|
||||
if (heartbeatJSON && heartbeatJSON.status === UP) {
|
||||
textMsg = `✅ Application is back online\n`;
|
||||
} else if (heartbeatJSON && heartbeatJSON.status === DOWN) {
|
||||
textMsg = `🔴 Application went down\n`;
|
||||
}
|
||||
|
||||
if (monitorJSON && monitorJSON.name) {
|
||||
textMsg += `*${monitorJSON.name}*\n`;
|
||||
}
|
||||
|
||||
textMsg += `${msg}`;
|
||||
|
||||
const baseURL = await setting("primaryBaseURL");
|
||||
if (baseURL && monitorJSON) {
|
||||
textMsg += `\n${baseURL + getMonitorRelativeURL(monitorJSON.id)}`;
|
||||
}
|
||||
|
||||
const data = {
|
||||
"text": textMsg,
|
||||
};
|
||||
|
||||
await axios.post(notification.googleChatWebhookURL, data);
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GoogleChat;
|
@ -0,0 +1,44 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
|
||||
class SerwerSMS extends NotificationProvider {
|
||||
|
||||
name = "serwersms";
|
||||
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
|
||||
try {
|
||||
let config = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
let data = {
|
||||
"username": notification.serwersmsUsername,
|
||||
"password": notification.serwersmsPassword,
|
||||
"phone": notification.serwersmsPhoneNumber,
|
||||
"text": msg.replace(/[^\x00-\x7F]/g, ""),
|
||||
"sender": notification.serwersmsSenderName,
|
||||
};
|
||||
|
||||
let resp = await axios.post("https://api2.serwersms.pl/messages/send_sms", data, config);
|
||||
|
||||
if (!resp.data.success) {
|
||||
if (resp.data.error) {
|
||||
let error = `SerwerSMS.pl API returned error code ${resp.data.error.code} (${resp.data.error.type}) with error message: ${resp.data.error.message}`;
|
||||
this.throwGeneralAxiosError(error);
|
||||
} else {
|
||||
let error = "SerwerSMS.pl API returned an unexpected response";
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SerwerSMS;
|
@ -0,0 +1,41 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
const { setting } = require("../util-server");
|
||||
const { getMonitorRelativeURL } = require("../../src/util");
|
||||
|
||||
class Stackfield extends NotificationProvider {
|
||||
|
||||
name = "stackfield";
|
||||
|
||||
async send(notification, msg, monitorJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
try {
|
||||
// Stackfield message formatting: https://www.stackfield.com/help/formatting-messages-2001
|
||||
|
||||
let textMsg = "+Uptime Kuma Alert+";
|
||||
|
||||
if (monitorJSON && monitorJSON.name) {
|
||||
textMsg += `\n*${monitorJSON.name}*`;
|
||||
}
|
||||
|
||||
textMsg += `\n${msg}`;
|
||||
|
||||
const baseURL = await setting("primaryBaseURL");
|
||||
if (baseURL) {
|
||||
textMsg += `\n${baseURL + getMonitorRelativeURL(monitorJSON.id)}`;
|
||||
}
|
||||
|
||||
const data = {
|
||||
"Title": textMsg,
|
||||
};
|
||||
|
||||
await axios.post(notification.stackfieldwebhookURL, data);
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Stackfield;
|
@ -0,0 +1,47 @@
|
||||
const NotificationProvider = require("./notification-provider");
|
||||
const axios = require("axios");
|
||||
const { DOWN, UP } = require("../../src/util");
|
||||
|
||||
class WeCom extends NotificationProvider {
|
||||
|
||||
name = "WeCom";
|
||||
|
||||
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||
let okMsg = "Sent Successfully.";
|
||||
|
||||
try {
|
||||
let WeComUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=" + notification.weComBotKey;
|
||||
let config = {
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
};
|
||||
let body = this.composeMessage(heartbeatJSON, msg);
|
||||
await axios.post(WeComUrl, body, config);
|
||||
return okMsg;
|
||||
} catch (error) {
|
||||
this.throwGeneralAxiosError(error);
|
||||
}
|
||||
}
|
||||
|
||||
composeMessage(heartbeatJSON, msg) {
|
||||
let title;
|
||||
if (msg != null && heartbeatJSON != null && heartbeatJSON['status'] == UP) {
|
||||
title = "UptimeKuma Monitor Up";
|
||||
}
|
||||
if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] == DOWN) {
|
||||
title = "UptimeKuma Monitor Down";
|
||||
}
|
||||
if (msg != null) {
|
||||
title = "UptimeKuma Message";
|
||||
}
|
||||
return {
|
||||
msgtype: "text",
|
||||
text: {
|
||||
content: title + msg
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WeCom;
|
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="my-3 py-3">
|
||||
<h5 @click="isOpen = !isOpen">
|
||||
<div
|
||||
class="
|
||||
w-50
|
||||
d-flex
|
||||
justify-content-between
|
||||
align-items-center
|
||||
pe-2
|
||||
"
|
||||
>
|
||||
<span class="pb-2">{{ heading }}</span>
|
||||
<font-awesome-icon
|
||||
icon="chevron-down"
|
||||
class="animated"
|
||||
:class="{ open: isOpen }"
|
||||
/>
|
||||
</div>
|
||||
</h5>
|
||||
<transition name="slide-fade-up">
|
||||
<div v-if="isOpen" class="mt-3">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
heading: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
defaultOpen: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isOpen: this.defaultOpen,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/vars.scss";
|
||||
|
||||
h5:after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 50%;
|
||||
padding-top: 8px;
|
||||
border-bottom: 1px solid $dark-border-color;
|
||||
}
|
||||
|
||||
.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.animated {
|
||||
transition: all 0.2s $easing-in;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="google-chat-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||
<input id="google-chat-webhook-url" v-model="$parent.notification.googleChatWebhookURL" type="text" class="form-control" required>
|
||||
|
||||
<div class="form-text">
|
||||
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
|
||||
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
|
||||
<a href="https://developers.google.com/chat/how-tos/webhooks" target="_blank">https://developers.google.com/chat/how-tos/webhooks</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="serwersms-username" class="form-label">{{ $t('serwersmsAPIUser') }}</label>
|
||||
<input id="serwersms-username" v-model="$parent.notification.serwersmsUsername" type="text" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="serwersms-key" class="form-label">{{ $t('serwersmsAPIPassword') }}</label>
|
||||
<HiddenInput id="serwersms-key" v-model="$parent.notification.serwersmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="serwersms-phone-number" class="form-label">{{ $t("serwersmsPhoneNumber") }}</label>
|
||||
<input id="serwersms-phone-number" v-model="$parent.notification.serwersmsPhoneNumber" type="text" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="serwersms-sender-name" class="form-label">{{ $t("serwersmsSenderName") }}</label>
|
||||
<input id="serwersms-sender-name" v-model="$parent.notification.serwersmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../HiddenInput.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
};
|
||||
</script>
|
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="stackfield-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||
<input id="stackfield-webhook-url" v-model="$parent.notification.stackfieldwebhookURL" type="text" class="form-control" required>
|
||||
|
||||
<div class="form-text">
|
||||
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
|
||||
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
|
||||
<a href="https://www.stackfield.com/developer-api#AnchorAPI2" target="_blank">https://www.stackfield.com/developer-api#AnchorAPI2</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="WeCom Bot Key" class="form-label">{{ $t("WeCom Bot Key") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||
<input id="WeCom Bot Key" v-model="$parent.notification.weComBotKey" type="text" class="form-control" required>
|
||||
<div class="form-text">
|
||||
<p><span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}</p>
|
||||
</div>
|
||||
<i18n-t tag="p" keypath="Read more:">
|
||||
<a href="https://work.weixin.qq.com/api/doc/90000/90136/91770" target="_blank">https://work.weixin.qq.com/api/doc/90000/90136/91770</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<div class="logo d-flex flex-column justify-content-center align-items-center">
|
||||
<object class="my-4" width="200" height="200" data="/icon.svg" />
|
||||
<div class="fs-4 fw-bold">Uptime Kuma</div>
|
||||
<div>{{ $t("Version") }}: {{ $root.info.version }}</div>
|
||||
<div class="my-1 update-link"><a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">{{ $t("Check Update On GitHub") }}</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.logo {
|
||||
margin: 4em 1em;
|
||||
}
|
||||
.update-link {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="my-4">
|
||||
<label for="language" class="form-label">
|
||||
{{ $t("Language") }}
|
||||
</label>
|
||||
<select id="language" v-model="$root.language" class="form-select">
|
||||
<option
|
||||
v-for="(lang, i) in $i18n.availableLocales"
|
||||
:key="`Lang${i}`"
|
||||
:value="lang"
|
||||
>
|
||||
{{ $i18n.messages[lang].languageName }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<label for="timezone" class="form-label">{{ $t("Theme") }}</label>
|
||||
<div>
|
||||
<div
|
||||
class="btn-group"
|
||||
role="group"
|
||||
aria-label="Basic checkbox toggle button group"
|
||||
>
|
||||
<input
|
||||
id="btncheck1"
|
||||
v-model="$root.userTheme"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="theme"
|
||||
autocomplete="off"
|
||||
value="light"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck1">
|
||||
{{ $t("Light") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="btncheck2"
|
||||
v-model="$root.userTheme"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="theme"
|
||||
autocomplete="off"
|
||||
value="dark"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck2">
|
||||
{{ $t("Dark") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="btncheck3"
|
||||
v-model="$root.userTheme"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="theme"
|
||||
autocomplete="off"
|
||||
value="auto"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck3">
|
||||
{{ $t("Auto") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label>
|
||||
<div>
|
||||
<div
|
||||
class="btn-group"
|
||||
role="group"
|
||||
aria-label="Basic checkbox toggle button group"
|
||||
>
|
||||
<input
|
||||
id="btncheck4"
|
||||
v-model="$root.userHeartbeatBar"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="heartbeatBarTheme"
|
||||
autocomplete="off"
|
||||
value="normal"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck4">
|
||||
{{ $t("Normal") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="btncheck5"
|
||||
v-model="$root.userHeartbeatBar"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="heartbeatBarTheme"
|
||||
autocomplete="off"
|
||||
value="bottom"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck5">
|
||||
{{ $t("Bottom") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="btncheck6"
|
||||
v-model="$root.userHeartbeatBar"
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="heartbeatBarTheme"
|
||||
autocomplete="off"
|
||||
value="none"
|
||||
/>
|
||||
<label class="btn btn-outline-primary" for="btncheck6">
|
||||
{{ $t("None") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.btn-check:active + .btn-outline-primary,
|
||||
.btn-check:checked + .btn-outline-primary,
|
||||
.btn-check:hover + .btn-outline-primary {
|
||||
color: #fff;
|
||||
|
||||
.dark & {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.list-group-item {
|
||||
background-color: $dark-bg2;
|
||||
color: $dark-font-color;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="my-4">
|
||||
<h4 class="mt-4 mb-2">{{ $t("Export Backup") }}</h4>
|
||||
|
||||
<p>
|
||||
{{ $t("backupDescription") }} <br />
|
||||
({{ $t("backupDescription2") }}) <br />
|
||||
</p>
|
||||
|
||||
<div class="mb-2">
|
||||
<button class="btn btn-primary" @click="downloadBackup">
|
||||
{{ $t("Export") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<strong>{{ $t("backupDescription3") }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<h4 class="mt-4 mb-2">{{ $t("Import Backup") }}</h4>
|
||||
|
||||
<label class="form-label">{{ $t("Options") }}:</label>
|
||||
<br />
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="radioKeep"
|
||||
v-model="importHandle"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="radioImportHandle"
|
||||
value="keep"
|
||||
/>
|
||||
<label class="form-check-label" for="radioKeep">
|
||||
{{ $t("Keep both") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="radioSkip"
|
||||
v-model="importHandle"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="radioImportHandle"
|
||||
value="skip"
|
||||
/>
|
||||
<label class="form-check-label" for="radioSkip">
|
||||
{{ $t("Skip existing") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="radioOverwrite"
|
||||
v-model="importHandle"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="radioImportHandle"
|
||||
value="overwrite"
|
||||
/>
|
||||
<label class="form-check-label" for="radioOverwrite">
|
||||
{{ $t("Overwrite") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-text mb-2">
|
||||
{{ $t("importHandleDescription") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<input
|
||||
id="importBackup"
|
||||
type="file"
|
||||
class="form-control"
|
||||
accept="application/json"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-2 justify-content-end">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-primary"
|
||||
:disabled="processing"
|
||||
@click="confirmImport"
|
||||
>
|
||||
<div
|
||||
v-if="processing"
|
||||
class="spinner-border spinner-border-sm me-1"
|
||||
></div>
|
||||
{{ $t("Import") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="importAlert"
|
||||
class="alert alert-danger mt-3"
|
||||
style="padding: 6px 16px"
|
||||
>
|
||||
{{ importAlert }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Confirm
|
||||
ref="confirmImport"
|
||||
btn-style="btn-danger"
|
||||
:yes-text="$t('Yes')"
|
||||
:no-text="$t('No')"
|
||||
@yes="importBackup"
|
||||
>
|
||||
{{ $t("confirmImportMsg") }}
|
||||
</Confirm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Confirm from "../../components/Confirm.vue";
|
||||
import dayjs from "dayjs";
|
||||
import { useToast } from "vue-toastification";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Confirm,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
processing: false,
|
||||
importHandle: "skip",
|
||||
importAlert: null,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
confirmImport() {
|
||||
this.$refs.confirmImport.show();
|
||||
},
|
||||
|
||||
downloadBackup() {
|
||||
let time = dayjs().format("YYYY_MM_DD-hh_mm_ss");
|
||||
let fileName = `Uptime_Kuma_Backup_${time}.json`;
|
||||
let monitorList = Object.values(this.$root.monitorList);
|
||||
let exportData = {
|
||||
version: this.$root.info.version,
|
||||
notificationList: this.$root.notificationList,
|
||||
monitorList: monitorList,
|
||||
};
|
||||
exportData = JSON.stringify(exportData, null, 4);
|
||||
let downloadItem = document.createElement("a");
|
||||
downloadItem.setAttribute(
|
||||
"href",
|
||||
"data:application/json;charset=utf-8," +
|
||||
encodeURIComponent(exportData)
|
||||
);
|
||||
downloadItem.setAttribute("download", fileName);
|
||||
downloadItem.click();
|
||||
},
|
||||
|
||||
importBackup() {
|
||||
this.processing = true;
|
||||
let uploadItem = document.getElementById("importBackup").files;
|
||||
|
||||
if (uploadItem.length <= 0) {
|
||||
this.processing = false;
|
||||
return (this.importAlert = this.$t("alertNoFile"));
|
||||
}
|
||||
|
||||
if (uploadItem.item(0).type !== "application/json") {
|
||||
this.processing = false;
|
||||
return (this.importAlert = this.$t("alertWrongFileType"));
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.readAsText(uploadItem.item(0));
|
||||
|
||||
fileReader.onload = (item) => {
|
||||
this.$root.uploadBackup(
|
||||
item.target.result,
|
||||
this.importHandle,
|
||||
(res) => {
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
toast.success(res.msg);
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.dark {
|
||||
#importBackup {
|
||||
&::file-selector-button {
|
||||
color: $primary;
|
||||
background-color: $dark-bg;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled):not([readonly])::file-selector-button {
|
||||
color: $dark-font-color2;
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div>
|
||||
<form class="my-4" @submit.prevent="saveGeneral">
|
||||
<!-- Timezone -->
|
||||
<div class="mb-4">
|
||||
<label for="timezone" class="form-label">
|
||||
{{ $t("Timezone") }}
|
||||
</label>
|
||||
<select id="timezone" v-model="$root.userTimezone" class="form-select">
|
||||
<option value="auto">
|
||||
{{ $t("Auto") }}: {{ guessTimezone }}
|
||||
</option>
|
||||
<option
|
||||
v-for="(timezone, index) in timezoneList"
|
||||
:key="index"
|
||||
:value="timezone.value"
|
||||
>
|
||||
{{ timezone.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Search Engine -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">
|
||||
{{ $t("Search Engine Visibility") }}
|
||||
</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="searchEngineIndexYes"
|
||||
v-model="settings.searchEngineIndex"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="flexRadioDefault"
|
||||
:value="true"
|
||||
required
|
||||
/>
|
||||
<label class="form-check-label" for="searchEngineIndexYes">
|
||||
{{ $t("Allow indexing") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="searchEngineIndexNo"
|
||||
v-model="settings.searchEngineIndex"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="flexRadioDefault"
|
||||
:value="false"
|
||||
required
|
||||
/>
|
||||
<label class="form-check-label" for="searchEngineIndexNo">
|
||||
{{ $t("Discourage search engines from indexing site") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Entry Page -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">{{ $t("Entry Page") }}</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="entryPageYes"
|
||||
v-model="settings.entryPage"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="statusPage"
|
||||
value="dashboard"
|
||||
required
|
||||
/>
|
||||
<label class="form-check-label" for="entryPageYes">
|
||||
{{ $t("Dashboard") }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="entryPageNo"
|
||||
v-model="settings.entryPage"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="statusPage"
|
||||
value="statusPage"
|
||||
required
|
||||
/>
|
||||
<label class="form-check-label" for="entryPageNo">
|
||||
{{ $t("Status Page") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Primary Base URL -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="primaryBaseURL">
|
||||
{{ $t("Primary Base URL") }}
|
||||
</label>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<input
|
||||
id="primaryBaseURL"
|
||||
v-model="settings.primaryBaseURL"
|
||||
class="form-control"
|
||||
name="primaryBaseURL"
|
||||
placeholder="https://"
|
||||
pattern="https?://.+"
|
||||
/>
|
||||
<button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryBaseURL">
|
||||
{{ $t("Auto Get") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-text"></div>
|
||||
</div>
|
||||
|
||||
<!-- Steam API Key -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="steamAPIKey">
|
||||
{{ $t("Steam API Key") }}
|
||||
</label>
|
||||
<HiddenInput
|
||||
id="steamAPIKey"
|
||||
v-model="settings.steamAPIKey"
|
||||
autocomplete="one-time-code"
|
||||
/>
|
||||
<div class="form-text">
|
||||
{{ $t("steamApiKeyDescription") }}
|
||||
<a href="https://steamcommunity.com/dev" target="_blank">
|
||||
https://steamcommunity.com/dev
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Save Button -->
|
||||
<div>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
{{ $t("Save") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../../components/HiddenInput.vue";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import { timezoneList } from "../../util-frontend";
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
timezoneList: timezoneList(),
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
settings() {
|
||||
return this.$parent.$parent.$parent.settings;
|
||||
},
|
||||
saveSettings() {
|
||||
return this.$parent.$parent.$parent.saveSettings;
|
||||
},
|
||||
settingsLoaded() {
|
||||
return this.$parent.$parent.$parent.settingsLoaded;
|
||||
},
|
||||
guessTimezone() {
|
||||
return dayjs.tz.guess();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
saveGeneral() {
|
||||
localStorage.timezone = this.$root.userTimezone;
|
||||
this.saveSettings();
|
||||
},
|
||||
autoGetPrimaryBaseURL() {
|
||||
this.settings.primaryBaseURL = location.protocol + "//" + location.host;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="my-4">
|
||||
<label for="keepDataPeriodDays" class="form-label">
|
||||
{{
|
||||
$t("clearDataOlderThan", [
|
||||
settings.keepDataPeriodDays,
|
||||
])
|
||||
}}
|
||||
</label>
|
||||
<input
|
||||
id="keepDataPeriodDays"
|
||||
v-model="settings.keepDataPeriodDays"
|
||||
type="number"
|
||||
class="form-control"
|
||||
required
|
||||
min="1"
|
||||
step="1"
|
||||
/>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<button class="btn btn-primary" type="button" @click="saveSettings()">
|
||||
{{ $t("Save") }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<div class="my-3">
|
||||
<button class="btn btn-outline-info me-2" @click="shrinkDatabase">
|
||||
{{ $t("Shrink Database") }} ({{ databaseSizeDisplay }})
|
||||
</button>
|
||||
<div class="form-text mt-2 mb-4 ms-2">{{ $t("shrinkDatabaseDescription") }}</div>
|
||||
</div>
|
||||
<button
|
||||
id="clearAllStats-btn"
|
||||
class="btn btn-outline-danger me-2 mb-2"
|
||||
@click="confirmClearStatistics"
|
||||
>
|
||||
{{ $t("Clear all statistics") }}
|
||||
</button>
|
||||
</div>
|
||||
<Confirm
|
||||
ref="confirmClearStatistics"
|
||||
btn-style="btn-danger"
|
||||
:yes-text="$t('Yes')"
|
||||
:no-text="$t('No')"
|
||||
@yes="clearStatistics"
|
||||
>
|
||||
{{ $t("confirmClearStatisticsMsg") }}
|
||||
</Confirm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Confirm from "../../components/Confirm.vue";
|
||||
import { debug } from "../../util.ts";
|
||||
import { useToast } from "vue-toastification";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Confirm,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
databaseSize: 0,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
settings() {
|
||||
return this.$parent.$parent.$parent.settings;
|
||||
},
|
||||
saveSettings() {
|
||||
return this.$parent.$parent.$parent.saveSettings;
|
||||
},
|
||||
settingsLoaded() {
|
||||
return this.$parent.$parent.$parent.settingsLoaded;
|
||||
},
|
||||
databaseSizeDisplay() {
|
||||
return (
|
||||
Math.round((this.databaseSize / 1024 / 1024) * 10) / 10 + " MB"
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loadDatabaseSize();
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadDatabaseSize() {
|
||||
debug("load database size");
|
||||
this.$root.getSocket().emit("getDatabaseSize", (res) => {
|
||||
if (res.ok) {
|
||||
this.databaseSize = res.size;
|
||||
debug("database size: " + res.size);
|
||||
} else {
|
||||
debug(res);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
shrinkDatabase() {
|
||||
this.$root.getSocket().emit("shrinkDatabase", (res) => {
|
||||
if (res.ok) {
|
||||
this.loadDatabaseSize();
|
||||
toast.success("Done");
|
||||
} else {
|
||||
debug(res);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
confirmClearStatistics() {
|
||||
this.$refs.confirmClearStatistics.show();
|
||||
},
|
||||
|
||||
clearStatistics() {
|
||||
this.$root.clearStatistics((res) => {
|
||||
if (res.ok) {
|
||||
this.$router.go();
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="notification-list my-4">
|
||||
<p v-if="$root.notificationList.length === 0">
|
||||
{{ $t("Not available, please setup.") }}
|
||||
</p>
|
||||
<p v-else>
|
||||
{{ $t("notificationDescription") }}
|
||||
</p>
|
||||
|
||||
<ul class="list-group mb-3" style="border-radius: 1rem;">
|
||||
<li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item">
|
||||
{{ notification.name }}<br>
|
||||
<a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="btn btn-primary me-2" type="button" @click="$refs.notificationDialog.show()">
|
||||
{{ $t("Setup Notification") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<NotificationDialog ref="notificationDialog" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NotificationDialog from "../../components/NotificationDialog.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NotificationDialog
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.dark {
|
||||
.list-group-item {
|
||||
background-color: $dark-bg2;
|
||||
color: $dark-font-color;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,355 @@
|
||||
export default {
|
||||
languageName: "Slovenščina",
|
||||
checkEverySecond: "Preveri na vsakih {0} sekund",
|
||||
retryCheckEverySecond: "Ponovno poskusi na vsakih {0} sekund",
|
||||
retriesDescription: "Maksimalno število poskusov predenj se storitev označi kot 'ne deluje' in se pošlje obvestilo",
|
||||
ignoreTLSError: "Ignoriraj TLS/SSL napake za HTTPS spletne strani",
|
||||
upsideDownModeDescription: "Negiraj status. Če je storitev deluje bo označena kot 'ne deluje'.",
|
||||
maxRedirectDescription: "Maksimalno število sledečih preusmeritev. 0 onemogoči preusmeritve.",
|
||||
acceptedStatusCodesDescription: "Izberi kode statusa veljavna kot uspešen odgovor.",
|
||||
passwordNotMatchMsg: "Ponovljeno geslo se ne ujema.",
|
||||
notificationDescription: "Obvestila morajo biti dodeljena monitorju, da delujejo.",
|
||||
keywordDescription: "Iskana ključna beseda v surovem HTML ali JSON odgovoru. Iskanje je občutljivo na začetnico.",
|
||||
pauseDashboardHome: "Pavza",
|
||||
deleteMonitorMsg: "Ste prepričani, da želite izbrisati ta monitor?",
|
||||
deleteNotificationMsg: "Ste prepričani, da želite izbrisati to obvestilo za vse monitorje?",
|
||||
resoverserverDescription: "Cloudflare je privzeti strežnik. DNS strežnik lahko spremenite kadarkoli.",
|
||||
rrtypeDescription: "Izberite RR tip, ki ga želite spremljati",
|
||||
pauseMonitorMsg: "Ste prepričani, da želite pavzirati?",
|
||||
enableDefaultNotificationDescription: "To obvestilo bo kot privzeto omogočeno za vse nove monitorje. Še vedno ga lahko izključite posebej za vsak monitor.",
|
||||
clearEventsMsg: "Ste prepričani da želite izbrisati vse dogodke tega monitorja?",
|
||||
clearHeartbeatsMsg: "Ste prepričani da želite izbrisati vse srčne utripe tega monitorja?",
|
||||
confirmClearStatisticsMsg: "Ste prepričani da želite izbrisati VSO statistiko?",
|
||||
importHandleDescription: "Izberite 'Preskoči obstoječe', če želite preskočiti vsak monitor ali obvestilo z istim imenom. 'Prepiši' bo prepisal vse obstoječe monitorje in obvestila.",
|
||||
confirmImportMsg: "Ste prepričani da želite uvoziti varnostno kopijo? Preverite da ste izbrali pravo opcijo za uvoz.",
|
||||
twoFAVerifyLabel: "Prosimo vnesite žeton za potrditev 2FA:",
|
||||
tokenValidSettingsMsg: "Žeton je veljaven! Sedaj lahko shranite 2FA nastavitev.",
|
||||
confirmEnableTwoFAMsg: "Ste prepričani, da želite omogočiti 2FA?",
|
||||
confirmDisableTwoFAMsg: "Ste prepričani, da želite onemogočiti 2FA?",
|
||||
Settings: "Nastavitve",
|
||||
Dashboard: "Nadzorna plošča",
|
||||
"New Update": "Nova posodobitev",
|
||||
Language: "Jezik",
|
||||
Appearance: "Izgled",
|
||||
Theme: "Teme",
|
||||
General: "Splošno",
|
||||
"Primary Base URL": "Primaren URL",
|
||||
Version: "Različica",
|
||||
"Check Update On GitHub": "Preveri posodobitev na GitHub-u",
|
||||
List: "Seznam",
|
||||
Add: "Dodaj",
|
||||
"Add New Monitor": "Dodaj nov monitor",
|
||||
"Quick Stats": "Hitro stanje",
|
||||
Up: "Deluje",
|
||||
Down: "Ne deluje",
|
||||
Pending: "Na čakanju",
|
||||
Unknown: "Neznano",
|
||||
Pause: "Pavza",
|
||||
Name: "Ime",
|
||||
Status: "Status",
|
||||
DateTime: "DateTime",
|
||||
Message: "Sporočilo",
|
||||
"No important events": "Ni pomembnih dogodkov",
|
||||
Resume: "Nadaljuj",
|
||||
Edit: "Uredi",
|
||||
Delete: "Izbriši",
|
||||
Current: "Trenutno",
|
||||
Uptime: "Uptime",
|
||||
"Cert Exp.": "Potek certifikata",
|
||||
days: "dni",
|
||||
day: "dan",
|
||||
"-day": "-dni",
|
||||
hour: "ura",
|
||||
"-hour": "-ur",
|
||||
Response: "Odgovor",
|
||||
Ping: "Ping",
|
||||
"Monitor Type": "Tip monitorja",
|
||||
Keyword: "Ključna beseda",
|
||||
"Friendly Name": "Ime za prikaz",
|
||||
URL: "URL",
|
||||
Hostname: "Hostname",
|
||||
Port: "Vrata",
|
||||
"Heartbeat Interval": "Interval srčnega utripa",
|
||||
Retries: "Ponovni poskusi",
|
||||
"Heartbeat Retry Interval": "Ponovni poskus srčnega utripa",
|
||||
Advanced: "Napredno",
|
||||
"Upside Down Mode": "Negiran način",
|
||||
"Max. Redirects": "Max. preusmeritev",
|
||||
"Accepted Status Codes": "Sprejete kode statusa",
|
||||
"Push URL": "Push URL",
|
||||
needPushEvery: "Pokliči ta URL vsakih {0} sekund.",
|
||||
pushOptionalParams: "Dodatni parametri: {0}",
|
||||
Save: "Shrani",
|
||||
Notifications: "Obvestila",
|
||||
"Not available, please setup.": "Ni na voljo, prosimo nastavite.",
|
||||
"Setup Notification": "Nastavi obvestila",
|
||||
Light: "Svetlo",
|
||||
Dark: "Temno",
|
||||
Auto: "Auto",
|
||||
"Theme - Heartbeat Bar": "Tema - vrstica srčnega utripa",
|
||||
Normal: "Normalna",
|
||||
Bottom: "Spodaj",
|
||||
None: "Brez",
|
||||
Timezone: "Časovni pas",
|
||||
"Search Engine Visibility": "Vidljivost v spletnih iskalnikih",
|
||||
"Allow indexing": "Dovoli indeksiranje",
|
||||
"Discourage search engines from indexing site": "Odvračaj spletne iskalnike od indeksiranja te strani",
|
||||
"Change Password": "Zamenjaj geslo",
|
||||
"Current Password": "Trenutno geslo",
|
||||
"New Password": "Novo geslo",
|
||||
"Repeat New Password": "Ponovi novo geslo",
|
||||
"Update Password": "Posodobi geslo",
|
||||
"Disable Auth": "Onemogoči auth",
|
||||
"Enable Auth": "Omogoči auth",
|
||||
Logout: "Odjava",
|
||||
Leave: "Zapusti",
|
||||
"I understand, please disable": "Razumem, prosim onemogočite",
|
||||
Confirm: "Potrdi",
|
||||
Yes: "Da",
|
||||
No: "Ne",
|
||||
Username: "Uporabniško ime",
|
||||
Password: "Geslo",
|
||||
"Remember me": "Zapomni si me",
|
||||
Login: "Vpis",
|
||||
"No Monitors, please": "Prosim, brez monitorjev",
|
||||
"add one": "Dodaj enega",
|
||||
"Notification Type": "Tip obvestila",
|
||||
Email: "Email",
|
||||
Test: "Test",
|
||||
"Certificate Info": "Informacije certifikata",
|
||||
"Resolver Server": "Strežnik za razreševanje",
|
||||
"Resource Record Type": "Vrsta zapisa o viru",
|
||||
"Last Result": "Zadnji rezultat",
|
||||
"Create your admin account": "Ustvari administratorski račun",
|
||||
"Repeat Password": "Ponovi geslo",
|
||||
"Import Backup": "Uvozi varnostno kopijo",
|
||||
"Export Backup": "Izvozi varnostno kopijo",
|
||||
Export: "Izvozi",
|
||||
Import: "Uvozi",
|
||||
respTime: "Odzivni čas (ms)",
|
||||
notAvailableShort: "N/A",
|
||||
"Default enabled": "Privzeto omogočeno",
|
||||
"Apply on all existing monitors": "Uporabi na vseh obstoječih monitorjih",
|
||||
Create: "Ustvari",
|
||||
"Clear Data": "Izbriši podatke",
|
||||
Events: "Dogodki",
|
||||
Heartbeats: "Srčni utripi",
|
||||
"Auto Get": "Auto Get",
|
||||
backupDescription: "Izvozite lahko vse monitorje in obvestila v JSON datoteko.",
|
||||
backupDescription2: "Pomni: Zgodovina in podatki dogodkov niso vključeni.",
|
||||
backupDescription3: "Občutljivi podatki, kot žetoni za obvestila so vlkjučeni v datoteko za izvoz; prosimo hranite na varnem.",
|
||||
alertNoFile: "Izberite datoteko za Uvoz.",
|
||||
alertWrongFileType: "Prosimo izberite JSON datoteko.",
|
||||
"Clear all statistics": "Pobrišite vso statistiko",
|
||||
"Skip existing": "Preskoči obstoječe",
|
||||
Overwrite: "Prepiši",
|
||||
Options: "Možnosti",
|
||||
"Keep both": "Ohrani oboje",
|
||||
"Verify Token": "Potrdi žeton",
|
||||
"Setup 2FA": "Nastavi 2FA",
|
||||
"Enable 2FA": "Omogoči 2FA",
|
||||
"Disable 2FA": "Onemogoči 2FA",
|
||||
"2FA Settings": "2FA nastavitve",
|
||||
"Two Factor Authentication": "Preverjanje pristnosti z dvema dejavnikoma",
|
||||
Active: "Aktivno",
|
||||
Inactive: "Neaktivno",
|
||||
Token: "Žeton",
|
||||
"Show URI": "Prikaži URI",
|
||||
Tags: "Značke",
|
||||
"Add New below or Select...": "Dodaj novo spodaj ali izberi iz seznama...",
|
||||
"Tag with this name already exist.": "Značka s tem imenom že obstaja.",
|
||||
"Tag with this value already exist.": "Značka s to vrednostjo že obstaja.",
|
||||
color: "barva",
|
||||
"value (optional)": "vrednost (po želji)",
|
||||
Gray: "Siva",
|
||||
Red: "Rdeča",
|
||||
Orange: "Oranžna",
|
||||
Green: "Zelena",
|
||||
Blue: "Modra",
|
||||
Indigo: "Indigo",
|
||||
Purple: "Vijolična",
|
||||
Pink: "Roza",
|
||||
"Search...": "Išči...",
|
||||
"Avg. Ping": "Avg. Ping",
|
||||
"Avg. Response": "Avg. odziv",
|
||||
"Entry Page": "Vstopna stran",
|
||||
statusPageNothing: "Nikjer nič... Dodajte skupino ali monitor.",
|
||||
"No Services": "Ni storitev",
|
||||
"All Systems Operational": "Vsi sistemi delujejo",
|
||||
"Partially Degraded Service": "Delno poslabšana storitev",
|
||||
"Degraded Service": "Poslabšana storitev",
|
||||
"Add Group": "Dodaj skupino",
|
||||
"Add a monitor": "Dodaj monitor",
|
||||
"Edit Status Page": "Uredi statusno stran",
|
||||
"Go to Dashboard": "Pojdi na nadzorno ploščo",
|
||||
"Status Page": "Status",
|
||||
defaultNotificationName: "Moje {notification} Obvestilo ({number})",
|
||||
here: "tukaj",
|
||||
Required: "Obvezno",
|
||||
telegram: "Telegram",
|
||||
"Bot Token": "Robotkov žetonček",
|
||||
wayToGetTelegramToken: "Lahko dobiš žeton od {0}.",
|
||||
"Chat ID": "ID pogovora",
|
||||
supportTelegramChatID: "Direkten pogovor pomoči / Skupina / ID kanala",
|
||||
wayToGetTelegramChatID: "Id lahko dobiš, če pošlješ sporočilo robotku in odpreš ta URL, da bi videl chat_id:",
|
||||
"YOUR BOT TOKEN HERE": "ROBOTKOV ŽETON TUKAJ",
|
||||
chatIDNotFound: "Ne najdem Chat Id-ja; prvo pošlji sporočilo robotku",
|
||||
webhook: "Webhook",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Vrsta vsebine",
|
||||
webhookJsonDesc: "{0} je v redu za vsak moderen HTTP strežnik, kot recimo Express.js",
|
||||
webhookFormDataDesc: "{multipart} je v redu za PHP. JSON bo moral biti razčlenjen s {decodeFunction}",
|
||||
smtp: "Email (SMTP)",
|
||||
secureOptionNone: "Brez / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Ignoriraj TLS napako",
|
||||
"From Email": "Od Email",
|
||||
emailCustomSubject: "Poljubna zadeva",
|
||||
"To Email": "Za Email",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
discord: "Discord",
|
||||
"Discord Webhook URL": "Discord Webhook URL",
|
||||
wayToGetDiscordURL: "To lahko dibiš v Server Settings -> Integrations -> Create Webhook",
|
||||
"Bot Display Name": "Prikazno ime robotka",
|
||||
"Prefix Custom Message": "Predpona poljubnega sporočila",
|
||||
"Hello @everyone is...": "Pozdravljen {'@'}everyone je...",
|
||||
teams: "Microsoft Teams",
|
||||
"Webhook URL": "Webhook URL",
|
||||
wayToGetTeamsURL: "Izvedi kako narediš webhook URL {0}.",
|
||||
signal: "Signal",
|
||||
Number: "Številka",
|
||||
Recipients: "Prejemniki",
|
||||
needSignalAPI: "Imeti moraš signal klienta z REST API.",
|
||||
wayToCheckSignalURL: "Kako se to naredi, lahko preveriš na tem URL-ju:",
|
||||
signalImportant: "POMEMBNO: Ne moreš mešati skupin in številk v prejemnikih!",
|
||||
gotify: "Gotify",
|
||||
"Application Token": "Žeton za aplikacijo",
|
||||
"Server URL": "URL Strežnika",
|
||||
Priority: "Prioriteta",
|
||||
slack: "Slack",
|
||||
"Icon Emoji": "Emoji ikona",
|
||||
"Channel Name": "Ime kanala",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
aboutWebhooks: "Več o webhook-ih: {0}",
|
||||
aboutChannelName: "Vnesi ime kanala na {0} Channel Name polje, če želiš preskočiti webhook kanal. npr.: #drug-kanal",
|
||||
aboutKumaURL: "Če pustite polje Uptime Kuma URL prazno, bo nastavljeno privzeto na GitHub stran projekta.",
|
||||
emojiCheatSheet: "Emoji plonk listek: {0}",
|
||||
"rocket.chat": "Rocket.Chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (podpira 50+ storitev za obveščevanje)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"User Key": "User Key",
|
||||
Device: "Naprava",
|
||||
"Message Title": "Naslov sporočila",
|
||||
"Notification Sound": "Zvok obvestila",
|
||||
"More info on:": "Več informacij na: {0}",
|
||||
pushoverDesc1: "Prioriteta nujnosti (2) ima privzeto nastavitev 30 sekund časa med ponovni poskusi in poteče po 1 uri.",
|
||||
pushoverDesc2: "Če želite pošiljati obvestila na različne naprave izpolnite polje 'Naprava'.",
|
||||
"SMS Type": "Vrsta SMS-a",
|
||||
octopushTypePremium: "Premium (hitro - priporočljivo za opozarjanje)",
|
||||
octopushTypeLowCost: "Cenovno ugodno (počasno - včasih jih blokira operater)",
|
||||
checkPrice: "preveri {0} cene:",
|
||||
apiCredentials: "API poverilnice",
|
||||
octopushLegacyHint: "Uporabljate legacy verzijo Octopush-a (2011-2020) ali novo verzijo?",
|
||||
"Check octopush prices": "Preveri octopush cene {0}.",
|
||||
octopushPhoneNumber: "Telefonska številka (npr.: +386031234567) ",
|
||||
octopushSMSSender: "Ime SMS pošiljatelja: 3-11 alfanumeričnih znakov in presledki (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "LunaSea Device ID",
|
||||
"Apprise URL": "Apprise URL",
|
||||
"Example:": "Primer: {0}",
|
||||
"Read more:": "Preberi več: {0}",
|
||||
"Status:": "Status: {0}",
|
||||
"Read more": "Preberi več",
|
||||
appriseInstalled: "Apprise je nameščen.",
|
||||
appriseNotInstalled: "Apprise ni nameščen. {0}",
|
||||
"Access Token": "Žeton za dostop",
|
||||
"Channel access token": "Žeton za dostop do kanala",
|
||||
"Line Developers Console": "Line Developers Console",
|
||||
lineDevConsoleTo: "Line Developers Console - {0}",
|
||||
"Basic Settings": "Osnovne nastavitve",
|
||||
"User ID": "User ID",
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "Prvo odpri {0}, ustvarite ponudnika in kanal (Messaging API), potem lahko žeton za dostop do kanala in ID uporabnika dobite iz zgoraj navedenih elementov menija.",
|
||||
"Icon URL": "URL ikone",
|
||||
aboutIconURL: "V razdelku \"URL ikone\" lahko zagotovite povezavo do slike, ki bo nadomestila privzeto sliko profila. Ne bo uporabljena, če je nastavljena ikona Emoji.",
|
||||
aboutMattermostChannelName: "V razdelku \"URL ikone\" lahko zagotovite povezavo do slike, ki bo nadomestila privzeto sliko profila. Ne bo uporabljena, če je nastavljena ikona Emoji",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - poceni, vendar počasen in pogosto preobremenjen. Omejeno samo na poljske prejemnike.",
|
||||
promosmsTypeFlash: "SMS FLASH - sporočilo se samodejno prikaže v napravi prejemnika. Omejeno samo na poljske prejemnike.",
|
||||
promosmsTypeFull: "SMS FULL - Premium raven SMS, Uporabite lahko svoje ime pošiljatelja (najprej morate registrirati ime). Zanesljivo za opozorila.",
|
||||
promosmsTypeSpeed: "SMS SPEED - Najvišja prednost v sistemu. Zelo hitro in zanesljivo, vendar drago (približno dvakratnik cene SMS FULL)..",
|
||||
promosmsPhoneNumber: "Telefonska številka (za poljskega prejemnika Lahko preskočite področne oznake",
|
||||
promosmsSMSSender: "Ime pošiljatelja SMS : vnaprej registrirano ime ali eno od privzetih: SMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookUrl": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "Homeserver URL (z http(s):// in vrata po želji)",
|
||||
"Internal Room Id": "Interni ID sobe",
|
||||
matrixDesc1: "Notranji ID sobe lahko poiščete v naprednem razdelku nastavitev sobe v odjemalcu Matrix. Izgledati mora kot !QMdRCpUIfLwsfjxye6:home.server",
|
||||
matrixDesc2: "Zelo priporočljivo je, da ustvarite novega uporabnika in ne uporabljate svojega žetona za dostop uporabnika Matrix, saj bo omogočil popoln dostop do vašega računa in vseh sob, ki ste se jim pridružili. Namesto tega ustvarite novega uporabnika in ga povabite le v sobo, v kateri želite prejemati obvestila. Token dostopa lahko dobite tako, da zaženete {0}",
|
||||
Method: "Metoda",
|
||||
Body: "Telo",
|
||||
Headers: "Glave",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "Glave zahtevka niso veljavni JSON: ",
|
||||
BodyInvalidFormat: "Telo zahteve ni veljaven JSON: ",
|
||||
"Monitor History": "Zgodovina",
|
||||
clearDataOlderThan: "Ohrani zgodovino {0} dni.",
|
||||
PasswordsDoNotMatch: "Gesli se ne ujemata.",
|
||||
records: "vnosi",
|
||||
"One record": "En vnos",
|
||||
steamApiKeyDescription: "Za spremljanje igralnega strežnika Steam potrebujete ključ spletnega vmesnika Steam. Ključ API lahko registrirate tukaj: ",
|
||||
"Current User": "Trenuten uporabnik",
|
||||
recent: "Nedavno",
|
||||
Done: "Zaključi",
|
||||
Info: "Info",
|
||||
Security: "Varnost",
|
||||
"Steam API Key": "Steam API Key",
|
||||
"Shrink Database": "Stisni bazo",
|
||||
"Pick a RR-Type...": "Izberi RR tip...",
|
||||
"Pick Accepted Status Codes...": "Izbiranje sprejetih kod stanja...",
|
||||
Default: "Privzeto",
|
||||
"HTTP Options": "HTTP možnosti",
|
||||
"Create Incident": "Ustvari incident",
|
||||
Title: "Naslov",
|
||||
Content: "Vsebina",
|
||||
Style: "Stil",
|
||||
info: "info",
|
||||
warning: "opozorilo",
|
||||
danger: "nevarnost",
|
||||
primary: "primarno",
|
||||
light: "svetlo",
|
||||
dark: "temno",
|
||||
Post: "Objavi",
|
||||
"Please input title and content": "Vnesi naslov in vsebino",
|
||||
Created: "Ustvarjeno",
|
||||
"Last Updated": "Nazadnje posodobljeno",
|
||||
Unpin: "Odpni",
|
||||
"Switch to Light Theme": "Preklopi na svetlo temo",
|
||||
"Switch to Dark Theme": "Preklopi na temno temo",
|
||||
"Show Tags": "Prikaži značke",
|
||||
"Hide Tags": "Skrij značke",
|
||||
Description: "Opis",
|
||||
"No monitors available.": "Nobenega monitorja ni na voljo.",
|
||||
"Add one": "Dodaj enega",
|
||||
"No Monitors": "Ni monitorjev",
|
||||
"Add one": "Dodaj enega",
|
||||
"Untitled Group": "Skupina brez imena",
|
||||
Services: "Storitve",
|
||||
Discard: "zavrzi",
|
||||
Cancel: "Prekliči",
|
||||
"Powered by": "Powered by",
|
||||
shrinkDatabaseDescription: "Sprožitev podatkovne zbirke VACUUM za SQLite. Če je vaša zbirka podatkov ustvarjena po različici 1.10.0, je funkcija AUTO_VACUUM že omogočena in ta ukrep ni potreben.",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "API uporabniško ime (vključno z webapi_ prefix)",
|
||||
serwersmsAPIPassword: "API geslo",
|
||||
serwersmsPhoneNumber: "Telefonska številka",
|
||||
serwersmsSenderName: "Ime SMS pošiljatelja (registrirani prek portala za stranke)",
|
||||
"stackfield": "Stackfield",
|
||||
};
|
@ -0,0 +1,33 @@
|
||||
import { currentLocale } from "../i18n";
|
||||
import { setPageLocale } from "../util-frontend";
|
||||
const langModules = import.meta.glob("../languages/*.js");
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
language: currentLocale(),
|
||||
};
|
||||
},
|
||||
|
||||
async created() {
|
||||
if (this.language !== "en") {
|
||||
await this.changeLang(this.language);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
async language(lang) {
|
||||
await this.changeLang(lang);
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async changeLang(lang) {
|
||||
let message = (await langModules["../languages/" + lang + ".js"]()).default;
|
||||
this.$i18n.setLocaleMessage(lang, message);
|
||||
this.$i18n.locale = lang;
|
||||
localStorage.locale = lang;
|
||||
setPageLocale();
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Reference in new issue