mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-08-12 02:27:24 +08:00
Merge branch 'master' into #1209-Logout-button-in-navbar
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<router-view />
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setPageLocale } from "./util-frontend";
|
||||
export default {
|
||||
created() {
|
||||
setPageLocale();
|
||||
},
|
||||
created() {
|
||||
setPageLocale();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@@ -22,6 +22,18 @@ textarea.form-control {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
border-radius: 0.75rem;
|
||||
|
||||
.dark & {
|
||||
.list-group-item {
|
||||
background-color: $dark-bg;
|
||||
color: $dark-font-color;
|
||||
border-color: $dark-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 20px;
|
||||
@@ -412,6 +424,10 @@ textarea.form-control {
|
||||
background-color: rgba(239, 239, 239, 0.7);
|
||||
border-radius: 8px;
|
||||
|
||||
&.no-bg {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0 solid #eee;
|
||||
background-color: rgba(245, 245, 245, 0.9);
|
||||
@@ -453,6 +469,10 @@ textarea.form-control {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.prism-editor__textarea {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
// Localization
|
||||
|
||||
@import "localization.scss";
|
||||
|
@@ -11,23 +11,23 @@
|
||||
<table class="text-start">
|
||||
<tbody>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Subject:</td>
|
||||
<td class="px-3">{{ $t("Subject:") }}</td>
|
||||
<td>{{ formatSubject(cert.subject) }}</td>
|
||||
</tr>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Valid To:</td>
|
||||
<td class="px-3">{{ $t("Valid To:") }}</td>
|
||||
<td><Datetime :value="cert.validTo" /></td>
|
||||
</tr>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Days Remaining:</td>
|
||||
<td class="px-3">{{ $t("Days Remaining:") }}</td>
|
||||
<td>{{ cert.daysRemaining }}</td>
|
||||
</tr>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Issuer:</td>
|
||||
<td class="px-3">{{ $t("Issuer:") }}</td>
|
||||
<td>{{ formatSubject(cert.issuer) }}</td>
|
||||
</tr>
|
||||
<tr class="my-3">
|
||||
<td class="px-3">Fingerprint:</td>
|
||||
<td class="px-3">{{ $t("Fingerprint:") }}</td>
|
||||
<td>{{ cert.fingerprint }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Modal } from "bootstrap"
|
||||
import { Modal } from "bootstrap";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -42,19 +42,20 @@ export default {
|
||||
default: "No",
|
||||
},
|
||||
},
|
||||
emits: [ "yes" ],
|
||||
data: () => ({
|
||||
modal: null,
|
||||
}),
|
||||
mounted() {
|
||||
this.modal = new Modal(this.$refs.modal)
|
||||
this.modal = new Modal(this.$refs.modal);
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.modal.show()
|
||||
this.modal.show();
|
||||
},
|
||||
yes() {
|
||||
this.$emit("yes");
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -57,6 +57,7 @@ export default {
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: [ "update:modelValue" ],
|
||||
data() {
|
||||
return {
|
||||
visibility: "password",
|
||||
|
@@ -5,12 +5,12 @@
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
import { sleep } from "../util.ts"
|
||||
import { sleep } from "../util.ts";
|
||||
|
||||
export default {
|
||||
|
||||
props: {
|
||||
value: [String, Number],
|
||||
value: [ String, Number ],
|
||||
time: {
|
||||
type: Number,
|
||||
default: 0.3,
|
||||
@@ -25,12 +25,12 @@ export default {
|
||||
return {
|
||||
output: "",
|
||||
frameDuration: 30,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
isNum() {
|
||||
return typeof this.value === "number"
|
||||
return typeof this.value === "number";
|
||||
},
|
||||
},
|
||||
|
||||
@@ -45,7 +45,7 @@ export default {
|
||||
} else {
|
||||
for (let i = 1; i < frames; i++) {
|
||||
this.output += step;
|
||||
await sleep(15)
|
||||
await sleep(15);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,5 +59,5 @@ export default {
|
||||
|
||||
methods: {},
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -4,12 +4,12 @@
|
||||
|
||||
<script>
|
||||
import dayjs from "dayjs";
|
||||
import relativeTime from "dayjs/plugin/relativeTime"
|
||||
import utc from "dayjs/plugin/utc"
|
||||
import timezone from "dayjs/plugin/timezone" // dependent on utc plugin
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(relativeTime)
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone"; // dependent on utc plugin
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -29,5 +29,5 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -38,7 +38,7 @@ export default {
|
||||
beatMargin: 4,
|
||||
move: false,
|
||||
maxBeat: -1,
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -69,12 +69,12 @@ export default {
|
||||
if (start < 0) {
|
||||
// Add empty placeholder
|
||||
for (let i = start; i < 0; i++) {
|
||||
placeholders.push(0)
|
||||
placeholders.push(0);
|
||||
}
|
||||
start = 0;
|
||||
}
|
||||
|
||||
return placeholders.concat(this.beatList.slice(start))
|
||||
return placeholders.concat(this.beatList.slice(start));
|
||||
},
|
||||
|
||||
wrapStyle() {
|
||||
@@ -84,7 +84,7 @@ export default {
|
||||
return {
|
||||
padding: `${topBottom}px ${leftRight}px`,
|
||||
width: "100%",
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
barStyle() {
|
||||
@@ -94,12 +94,12 @@ export default {
|
||||
return {
|
||||
transition: "all ease-in-out 0.25s",
|
||||
transform: `translateX(${width}px)`,
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
return {
|
||||
transform: "translateX(0)",
|
||||
}
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
@@ -109,7 +109,7 @@ export default {
|
||||
height: this.beatHeight + "px",
|
||||
margin: this.beatMargin + "px",
|
||||
"--hover-scale": this.hoverScale,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
},
|
||||
@@ -120,7 +120,7 @@ export default {
|
||||
|
||||
setTimeout(() => {
|
||||
this.move = false;
|
||||
}, 300)
|
||||
}, 300);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
@@ -162,15 +162,15 @@ export default {
|
||||
methods: {
|
||||
resize() {
|
||||
if (this.$refs.wrap) {
|
||||
this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2))
|
||||
this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2));
|
||||
}
|
||||
},
|
||||
|
||||
getBeatTitle(beat) {
|
||||
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ``);
|
||||
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -48,18 +48,19 @@ export default {
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: [ "update:modelValue" ],
|
||||
data() {
|
||||
return {
|
||||
visibility: "password",
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
model: {
|
||||
get() {
|
||||
return this.modelValue
|
||||
return this.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("update:modelValue", value)
|
||||
this.$emit("update:modelValue", value);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -74,5 +75,5 @@ export default {
|
||||
this.visibility = "password";
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -9,7 +9,9 @@
|
||||
<a v-if="searchText != ''" class="search-icon" @click="clearSearchText">
|
||||
<font-awesome-icon icon="times" />
|
||||
</a>
|
||||
<input v-model="searchText" class="form-control search-input" :placeholder="$t('Search...')" />
|
||||
<form>
|
||||
<input v-model="searchText" class="form-control search-input" :placeholder="$t('Search...')" autocomplete="off" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="monitor-list" :class="{ scrollbar: scrollbar }">
|
||||
@@ -19,7 +21,7 @@
|
||||
|
||||
<router-link v-for="(item, index) in sortedMonitorList" :key="index" :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }">
|
||||
<div class="row">
|
||||
<div class="col-9 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }">
|
||||
<div class="col-9 col-md-8 small-padding" :class="{ 'monitor-item': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }">
|
||||
<div class="info">
|
||||
<Uptime :monitor="item" type="24" :pill="true" />
|
||||
{{ item.name }}
|
||||
@@ -34,7 +36,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="$root.userHeartbeatBar == 'bottom'" class="row">
|
||||
<div class="col-12">
|
||||
<div class="col-12 bottom-style">
|
||||
<HeartbeatBar size="small" :monitor-id="item.id" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -170,7 +172,7 @@ export default {
|
||||
|
||||
.dark {
|
||||
.footer {
|
||||
// background-color: $dark-bg;
|
||||
// background-color: $dark-bg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,14 +198,21 @@ export default {
|
||||
max-width: 15em;
|
||||
}
|
||||
|
||||
.monitorItem {
|
||||
.monitor-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tags {
|
||||
padding-left: 62px;
|
||||
margin-top: 4px;
|
||||
padding-left: 67px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.bottom-style {
|
||||
padding-left: 67px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -69,7 +69,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Modal } from "bootstrap";
|
||||
import { ucfirst } from "../util.ts";
|
||||
|
||||
import Confirm from "./Confirm.vue";
|
||||
import NotificationFormList from "./notifications";
|
||||
@@ -79,7 +78,7 @@ export default {
|
||||
Confirm,
|
||||
},
|
||||
props: {},
|
||||
emits: ["added"],
|
||||
emits: [ "added" ],
|
||||
data() {
|
||||
return {
|
||||
model: null,
|
||||
|
@@ -24,7 +24,7 @@ import timezone from "dayjs/plugin/timezone";
|
||||
import "chartjs-adapter-dayjs";
|
||||
import { LineChart } from "vue-chart-3";
|
||||
import { useToast } from "vue-toastification";
|
||||
import { UP, DOWN, PENDING } from "../util.ts";
|
||||
import { DOWN } from "../util.ts";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
@@ -220,6 +220,7 @@ export default {
|
||||
if (newPeriod == "0") {
|
||||
newPeriod = null;
|
||||
this.heartbeatList = null;
|
||||
this.$root.storage().removeItem(`chart-period-${this.monitorId}`);
|
||||
} else {
|
||||
this.loading = true;
|
||||
|
||||
@@ -228,6 +229,7 @@ export default {
|
||||
toast.error(res.msg);
|
||||
} else {
|
||||
this.heartbeatList = res.data;
|
||||
this.$root.storage()[`chart-period-${this.monitorId}`] = newPeriod;
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
@@ -248,6 +250,12 @@ export default {
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// Load chart period from storage if saved
|
||||
let period = this.$root.storage()[`chart-period-${this.monitorId}`];
|
||||
if (period != null) {
|
||||
this.chartPeriodHrs = Math.min(period, 6);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -278,7 +286,7 @@ export default {
|
||||
|
||||
.dropdown-item {
|
||||
border-radius: 0.3rem;
|
||||
padding: 2px 16px 4px 16px;
|
||||
padding: 2px 16px 4px;
|
||||
|
||||
.dark & {
|
||||
background: $dark-bg;
|
||||
@@ -286,6 +294,7 @@ export default {
|
||||
|
||||
.dark &:hover {
|
||||
background: $dark-font-color;
|
||||
color: $dark-font-color2;
|
||||
}
|
||||
}
|
||||
|
||||
|
206
src/components/ProxyDialog.vue
Normal file
206
src/components/ProxyDialog.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<form @submit.prevent="submit">
|
||||
<div ref="modal" class="modal fade" tabindex="-1" data-bs-backdrop="static">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 id="exampleModalLabel" class="modal-title">
|
||||
{{ $t("Setup Proxy") }}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" />
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="proxy-protocol" class="form-label">{{ $t("Proxy Protocol") }}</label>
|
||||
<select id="proxy-protocol" v-model="proxy.protocol" class="form-select">
|
||||
<option value="https">HTTPS</option>
|
||||
<option value="http">HTTP</option>
|
||||
<option value="socks">SOCKS</option>
|
||||
<option value="socks5">SOCKS v5</option>
|
||||
<option value="socks4">SOCKS v4</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="proxy-host" class="form-label">{{ $t("Proxy Server") }}</label>
|
||||
<div class="d-flex">
|
||||
<input id="proxy-host" v-model="proxy.host" type="text" class="form-control" required :placeholder="$t('Server Address')">
|
||||
<input v-model="proxy.port" type="number" class="form-control ms-2" style="width: 100px;" required min="1" max="65535" :placeholder="$t('Port')">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input id="mark-auth" v-model="proxy.auth" class="form-check-input" type="checkbox">
|
||||
<label for="mark-auth" class="form-check-label">{{ $t("Proxy server has authentication") }}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="proxy.auth" class="mb-3">
|
||||
<label for="proxy-username" class="form-label">{{ $t("User") }}</label>
|
||||
<input id="proxy-username" v-model="proxy.username" type="text" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div v-if="proxy.auth" class="mb-3">
|
||||
<label for="proxy-password" class="form-label">{{ $t("Password") }}</label>
|
||||
<input id="proxy-password" v-model="proxy.password" type="password" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 mt-4">
|
||||
<hr class="dropdown-divider mb-4">
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input id="mark-active" v-model="proxy.active" class="form-check-input" type="checkbox">
|
||||
<label for="mark-active" class="form-check-label">{{ $t("enabled") }}</label>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
{{ $t("enableProxyDescription") }}
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input id="mark-default" v-model="proxy.default" class="form-check-input" type="checkbox">
|
||||
<label for="mark-default" class="form-check-label">{{ $t("setAsDefault") }}</label>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
{{ $t("setAsDefaultProxyDescription") }}
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input id="apply-existing" v-model="proxy.applyExisting" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="apply-existing">{{ $t("Apply on all existing monitors") }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button v-if="id" type="button" class="btn btn-danger" :disabled="processing" @click="deleteConfirm">
|
||||
{{ $t("Delete") }}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary" :disabled="processing">
|
||||
<div v-if="processing" class="spinner-border spinner-border-sm me-1"></div>
|
||||
{{ $t("Save") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<Confirm ref="confirmDelete" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="deleteProxy">
|
||||
{{ $t("deleteProxyMsg") }}
|
||||
</Confirm>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Modal } from "bootstrap";
|
||||
|
||||
import Confirm from "./Confirm.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Confirm,
|
||||
},
|
||||
props: {},
|
||||
emits: [ "added" ],
|
||||
data() {
|
||||
return {
|
||||
model: null,
|
||||
processing: false,
|
||||
id: null,
|
||||
proxy: {
|
||||
protocol: null,
|
||||
host: null,
|
||||
port: null,
|
||||
auth: false,
|
||||
username: null,
|
||||
password: null,
|
||||
active: false,
|
||||
default: false,
|
||||
applyExisting: false,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.modal = new Modal(this.$refs.modal);
|
||||
},
|
||||
|
||||
methods: {
|
||||
deleteConfirm() {
|
||||
this.modal.hide();
|
||||
this.$refs.confirmDelete.show();
|
||||
},
|
||||
|
||||
show(proxyID) {
|
||||
if (proxyID) {
|
||||
this.id = proxyID;
|
||||
|
||||
for (let proxy of this.$root.proxyList) {
|
||||
if (proxy.id === proxyID) {
|
||||
this.proxy = proxy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.id = null;
|
||||
this.proxy = {
|
||||
protocol: "https",
|
||||
host: null,
|
||||
port: null,
|
||||
auth: false,
|
||||
username: null,
|
||||
password: null,
|
||||
active: true,
|
||||
default: false,
|
||||
applyExisting: false,
|
||||
};
|
||||
}
|
||||
|
||||
this.modal.show();
|
||||
},
|
||||
|
||||
submit() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("addProxy", this.proxy, this.id, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
this.modal.hide();
|
||||
|
||||
// Emit added event, doesn't emit edit.
|
||||
if (! this.id) {
|
||||
this.$emit("added", res.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
deleteProxy() {
|
||||
this.processing = true;
|
||||
this.$root.getSocket().emit("deleteProxy", this.id, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
this.modal.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/vars.scss";
|
||||
|
||||
.dark {
|
||||
.modal-dialog .form-text, .modal-dialog p {
|
||||
color: $dark-font-color;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -145,7 +145,7 @@ export default {
|
||||
|
||||
.mobile {
|
||||
.item {
|
||||
padding: 13px 0 10px 0;
|
||||
padding: 13px 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -41,7 +41,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -49,7 +49,7 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/vars.scss";
|
||||
|
||||
h5:after {
|
||||
h5::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 50%;
|
||||
|
@@ -19,6 +19,19 @@
|
||||
</div>
|
||||
<p v-if="showURI && twoFAStatus == false" class="text-break mt-2">{{ uri }}</p>
|
||||
|
||||
<div v-if="!(uri && twoFAStatus == false)" class="mb-3">
|
||||
<label for="current-password" class="form-label">
|
||||
{{ $t("Current Password") }}
|
||||
</label>
|
||||
<input
|
||||
id="current-password"
|
||||
v-model="currentPassword"
|
||||
type="password"
|
||||
class="form-control"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button v-if="uri == null && twoFAStatus == false" class="btn btn-primary" type="button" @click="prepare2FA()">
|
||||
{{ $t("Enable 2FA") }}
|
||||
</button>
|
||||
@@ -33,7 +46,7 @@
|
||||
<input v-model="token" type="text" maxlength="6" class="form-control">
|
||||
<button class="btn btn-outline-primary" type="button" @click="verifyToken()">{{ $t("Verify Token") }}</button>
|
||||
</div>
|
||||
<p v-show="tokenValid" class="mt-2" style="color: green">{{ $t("tokenValidSettingsMsg") }}</p>
|
||||
<p v-show="tokenValid" class="mt-2" style="color: green;">{{ $t("tokenValidSettingsMsg") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,11 +72,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Modal } from "bootstrap"
|
||||
import { Modal } from "bootstrap";
|
||||
import Confirm from "./Confirm.vue";
|
||||
import VueQrcode from "vue-qrcode"
|
||||
import { useToast } from "vue-toastification"
|
||||
const toast = useToast()
|
||||
import VueQrcode from "vue-qrcode";
|
||||
import { useToast } from "vue-toastification";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -73,35 +86,36 @@ export default {
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
currentPassword: "",
|
||||
processing: false,
|
||||
uri: null,
|
||||
tokenValid: false,
|
||||
twoFAStatus: null,
|
||||
token: null,
|
||||
showURI: false,
|
||||
}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.modal = new Modal(this.$refs.modal)
|
||||
this.modal = new Modal(this.$refs.modal);
|
||||
this.getStatus();
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.modal.show()
|
||||
this.modal.show();
|
||||
},
|
||||
|
||||
confirmEnableTwoFA() {
|
||||
this.$refs.confirmEnableTwoFA.show()
|
||||
this.$refs.confirmEnableTwoFA.show();
|
||||
},
|
||||
|
||||
confirmDisableTwoFA() {
|
||||
this.$refs.confirmDisableTwoFA.show()
|
||||
this.$refs.confirmDisableTwoFA.show();
|
||||
},
|
||||
|
||||
prepare2FA() {
|
||||
this.processing = true;
|
||||
|
||||
this.$root.getSocket().emit("prepare2FA", (res) => {
|
||||
this.$root.getSocket().emit("prepare2FA", this.currentPassword, (res) => {
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
@@ -109,49 +123,51 @@ export default {
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
save2FA() {
|
||||
this.processing = true;
|
||||
|
||||
this.$root.getSocket().emit("save2FA", (res) => {
|
||||
this.$root.getSocket().emit("save2FA", this.currentPassword, (res) => {
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
this.$root.toastRes(res)
|
||||
this.$root.toastRes(res);
|
||||
this.getStatus();
|
||||
this.currentPassword = "";
|
||||
this.modal.hide();
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
disable2FA() {
|
||||
this.processing = true;
|
||||
|
||||
this.$root.getSocket().emit("disable2FA", (res) => {
|
||||
this.$root.getSocket().emit("disable2FA", this.currentPassword, (res) => {
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
this.$root.toastRes(res)
|
||||
this.$root.toastRes(res);
|
||||
this.getStatus();
|
||||
this.currentPassword = "";
|
||||
this.modal.hide();
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
verifyToken() {
|
||||
this.$root.getSocket().emit("verifyToken", this.token, (res) => {
|
||||
this.$root.getSocket().emit("verifyToken", this.token, this.currentPassword, (res) => {
|
||||
if (res.ok) {
|
||||
this.tokenValid = res.valid;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
getStatus() {
|
||||
@@ -161,10 +177,10 @@ export default {
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -22,33 +22,33 @@ export default {
|
||||
return Math.round(this.$root.uptimeList[key] * 10000) / 100 + "%";
|
||||
}
|
||||
|
||||
return this.$t("notAvailableShort")
|
||||
return this.$t("notAvailableShort");
|
||||
},
|
||||
|
||||
color() {
|
||||
if (this.lastHeartBeat.status === 0) {
|
||||
return "danger"
|
||||
return "danger";
|
||||
}
|
||||
|
||||
if (this.lastHeartBeat.status === 1) {
|
||||
return "primary"
|
||||
return "primary";
|
||||
}
|
||||
|
||||
if (this.lastHeartBeat.status === 2) {
|
||||
return "warning"
|
||||
return "warning";
|
||||
}
|
||||
|
||||
return "secondary"
|
||||
return "secondary";
|
||||
},
|
||||
|
||||
lastHeartBeat() {
|
||||
if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) {
|
||||
return this.$root.lastHeartbeatList[this.monitor.id]
|
||||
return this.$root.lastHeartbeatList[this.monitor.id];
|
||||
}
|
||||
|
||||
return {
|
||||
status: -1,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
className() {
|
||||
@@ -59,7 +59,7 @@ export default {
|
||||
return "";
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@@ -28,5 +28,5 @@ export default {
|
||||
this.$parent.notification.gotifyPriority = 8;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="mattermost-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color:red;"><sup>*</sup></span></label>
|
||||
<label for="mattermost-webhook-url" class="form-label">{{ $t("Webhook URL") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||
<input id="mattermost-webhook-url" v-model="$parent.notification.mattermostWebhookUrl" type="text" class="form-control" required>
|
||||
<label for="mattermost-username" class="form-label">{{ $t("Username") }}</label>
|
||||
<input id="mattermost-username" v-model="$parent.notification.mattermostusername" type="text" class="form-control">
|
||||
@@ -11,7 +11,7 @@
|
||||
<label for="mattermost-channel" class="form-label">{{ $t("Channel Name") }}</label>
|
||||
<input id="mattermost-channel-name" v-model="$parent.notification.mattermostchannel" type="text" class="form-control">
|
||||
<div class="form-text">
|
||||
<span style="color:red;"><sup>*</sup></span>{{ $t("Required") }}
|
||||
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}
|
||||
<i18n-t tag="p" keypath="aboutWebhooks" style="margin-top: 8px;">
|
||||
<a href="https://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">https://docs.mattermost.com/developer/webhooks-incoming.html</a>
|
||||
</i18n-t>
|
||||
|
34
src/components/notifications/OneBot.vue
Normal file
34
src/components/notifications/OneBot.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<div class="mb-3">
|
||||
<label for="onebot-http-addr" class="form-label">{{ $t("onebotHttpAddress") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||
<input id="HttpUrl" v-model="$parent.notification.httpAddr" type="text" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="onebot-access-token" class="form-label">AccessToken<span style="color: red;"><sup>*</sup></span></label>
|
||||
<input id="HttpUrl" v-model="$parent.notification.accessToken" type="text" class="form-control" required>
|
||||
<div class="form-text">
|
||||
<p>{{ $t("onebotSafetyTips") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="onebot-msg-type" class="form-label">{{ $t("onebotMessageType") }}</label>
|
||||
<select id="onebot-msg-type" v-model="$parent.notification.msgType" class="form-select">
|
||||
<option value="group">{{ $t("onebotGroupMessage") }}</option>
|
||||
<option value="private">{{ $t("onebotPrivateMessage") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="onebot-reciever-id" class="form-label">{{ $t("onebotUserOrGroupId") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||
<input id="secretKey" v-model="$parent.notification.recieverId" type="text" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-text">
|
||||
<i18n-t tag="p" keypath="Read more:">
|
||||
<a href="https://github.com/botuniverse/onebot-11" target="_blank">https://github.com/botuniverse/onebot-11</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
19
src/components/notifications/PushDeer.vue
Normal file
19
src/components/notifications/PushDeer.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="pushdeer-key" class="form-label">{{ $t("PushDeer Key") }}</label>
|
||||
<HiddenInput id="pushdeer-key" v-model="$parent.notification.pushdeerKey" :required="true" autocomplete="one-time-code" placeholder="PDUxxxx"></HiddenInput>
|
||||
</div>
|
||||
|
||||
<i18n-t tag="p" keypath="More info on:" style="margin-top: 8px;">
|
||||
<a href="http://www.pushdeer.com/" rel="noopener noreferrer" target="_blank">http://www.pushdeer.com/</a>
|
||||
</i18n-t>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../HiddenInput.vue";
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
};
|
||||
</script>
|
@@ -63,5 +63,5 @@ export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -24,11 +24,13 @@ import AliyunSMS from "./AliyunSms.vue";
|
||||
import DingDing from "./DingDing.vue";
|
||||
import Bark from "./Bark.vue";
|
||||
import SerwerSMS from "./SerwerSMS.vue";
|
||||
import Stackfield from './Stackfield.vue';
|
||||
import Stackfield from "./Stackfield.vue";
|
||||
import WeCom from "./WeCom.vue";
|
||||
import GoogleChat from "./GoogleChat.vue";
|
||||
import Gorush from "./Gorush.vue";
|
||||
import Alerta from "./Alerta.vue";
|
||||
import OneBot from "./OneBot.vue";
|
||||
import PushDeer from "./PushDeer.vue";
|
||||
|
||||
/**
|
||||
* Manage all notification form.
|
||||
@@ -67,6 +69,8 @@ const NotificationFormList = {
|
||||
"GoogleChat": GoogleChat,
|
||||
"gorush": Gorush,
|
||||
"alerta": Alerta,
|
||||
"OneBot": OneBot,
|
||||
"PushDeer": PushDeer,
|
||||
};
|
||||
|
||||
export default NotificationFormList;
|
||||
|
@@ -44,6 +44,7 @@ export default {
|
||||
.logo {
|
||||
margin: 4em 1em;
|
||||
}
|
||||
|
||||
.update-link {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
@@ -69,7 +69,7 @@
|
||||
|
||||
<div class="mb-2">
|
||||
<input
|
||||
id="importBackup"
|
||||
id="import-backend"
|
||||
type="file"
|
||||
class="form-control"
|
||||
accept="application/json"
|
||||
@@ -94,7 +94,7 @@
|
||||
<div
|
||||
v-if="importAlert"
|
||||
class="alert alert-danger mt-3"
|
||||
style="padding: 6px 16px"
|
||||
style="padding: 6px 16px;"
|
||||
>
|
||||
{{ importAlert }}
|
||||
</div>
|
||||
@@ -159,7 +159,7 @@ export default {
|
||||
|
||||
importBackup() {
|
||||
this.processing = true;
|
||||
let uploadItem = document.getElementById("importBackup").files;
|
||||
let uploadItem = document.getElementById("import-backend").files;
|
||||
|
||||
if (uploadItem.length <= 0) {
|
||||
this.processing = false;
|
||||
@@ -198,7 +198,7 @@ export default {
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.dark {
|
||||
#importBackup {
|
||||
#import-backend {
|
||||
&::file-selector-button {
|
||||
color: $primary;
|
||||
background-color: $dark-bg;
|
||||
|
@@ -189,4 +189,3 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
@@ -52,7 +52,7 @@
|
||||
|
||||
<script>
|
||||
import Confirm from "../../components/Confirm.vue";
|
||||
import { debug } from "../../util.ts";
|
||||
import { log } from "../../util.ts";
|
||||
import { useToast } from "vue-toastification";
|
||||
|
||||
const toast = useToast();
|
||||
@@ -91,13 +91,13 @@ export default {
|
||||
|
||||
methods: {
|
||||
loadDatabaseSize() {
|
||||
debug("load database size");
|
||||
log.debug("monitorhistory", "load database size");
|
||||
this.$root.getSocket().emit("getDatabaseSize", (res) => {
|
||||
if (res.ok) {
|
||||
this.databaseSize = res.size;
|
||||
debug("database size: " + res.size);
|
||||
log.debug("monitorhistory", "database size: " + res.size);
|
||||
} else {
|
||||
debug(res);
|
||||
log.debug("monitorhistory", res);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -108,7 +108,7 @@ export default {
|
||||
this.loadDatabaseSize();
|
||||
toast.success("Done");
|
||||
} else {
|
||||
debug(res);
|
||||
log.debug("monitorhistory", res);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -129,5 +129,3 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
48
src/components/settings/Proxies.vue
Normal file
48
src/components/settings/Proxies.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Proxies -->
|
||||
<div class="proxy-list my-4">
|
||||
<p v-if="$root.proxyList.length === 0">
|
||||
{{ $t("Not available, please setup.") }}
|
||||
</p>
|
||||
<p v-else>
|
||||
{{ $t("proxyDescription") }}
|
||||
</p>
|
||||
|
||||
<ul class="list-group mb-3" style="border-radius: 1rem;">
|
||||
<li v-for="(proxy, index) in $root.proxyList" :key="index" class="list-group-item">
|
||||
{{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
|
||||
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("Default") }}</span><br>
|
||||
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="btn btn-primary me-2" type="button" @click="$refs.proxyDialog.show()">
|
||||
{{ $t("Setup Proxy") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ProxyDialog ref="proxyDialog" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ProxyDialog from "../../components/ProxyDialog.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ProxyDialog
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.dark {
|
||||
.list-group-item {
|
||||
background-color: $dark-bg2;
|
||||
color: $dark-font-color;
|
||||
}
|
||||
}
|
||||
</style>
|
144
src/components/settings/ReverseProxy.vue
Normal file
144
src/components/settings/ReverseProxy.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div>
|
||||
<h4 class="mt-4">Cloudflare Tunnel</h4>
|
||||
|
||||
<div class="my-3">
|
||||
<div>
|
||||
cloudflared:
|
||||
<span v-if="installed === true" class="text-primary">{{ $t("Installed") }}</span>
|
||||
<span v-else-if="installed === false" class="text-danger">{{ $t("Not installed") }}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ $t("Status") }}:
|
||||
<span v-if="running" class="text-primary">{{ $t("Running") }}</span>
|
||||
<span v-else-if="!running" class="text-danger">{{ $t("Not running") }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="false">
|
||||
{{ message }}
|
||||
</div>
|
||||
|
||||
<div v-if="errorMessage" class="mt-3">
|
||||
{{ $t("Message:") }}
|
||||
<textarea v-model="errorMessage" class="form-control" readonly></textarea>
|
||||
</div>
|
||||
|
||||
<i18n-t v-if="installed === false" tag="p" keypath="wayToGetCloudflaredURL">
|
||||
<a
|
||||
href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/"
|
||||
target="_blank"
|
||||
>{{ $t("cloudflareWebsite") }}</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<!-- If installed show token input -->
|
||||
<div v-if="installed" class="mb-2">
|
||||
<div class="mb-4">
|
||||
<label class="form-label" for="cloudflareTunnelToken">
|
||||
Cloudflare Tunnel {{ $t("Token") }}
|
||||
</label>
|
||||
<HiddenInput
|
||||
id="cloudflareTunnelToken"
|
||||
v-model="cloudflareTunnelToken"
|
||||
autocomplete="one-time-code"
|
||||
:readonly="running"
|
||||
/>
|
||||
<div class="form-text">
|
||||
<div v-if="cloudflareTunnelToken" class="mb-3">
|
||||
<span v-if="!running" class="remove-token" @click="removeToken">{{ $t("Remove Token") }}</span>
|
||||
</div>
|
||||
|
||||
{{ $t("Don't know how to get the token? Please read the guide:") }}<br />
|
||||
<a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel" target="_blank">
|
||||
https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button v-if="!running" class="btn btn-primary" type="submit" @click="start">
|
||||
{{ $t("Start") }} cloudflared
|
||||
</button>
|
||||
|
||||
<button v-if="running" class="btn btn-danger" type="submit" @click="$refs.confirmStop.show();">
|
||||
{{ $t("Stop") }} cloudflared
|
||||
</button>
|
||||
|
||||
<Confirm ref="confirmStop" btn-style="btn-danger" :yes-text="$t('Stop') + ' cloudflared'" :no-text="$t('Cancel')" @yes="stop">
|
||||
{{ $t("The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.") }}
|
||||
|
||||
<div class="mt-3">
|
||||
<label for="current-password2" class="form-label">
|
||||
{{ $t("Current Password") }}
|
||||
</label>
|
||||
<input
|
||||
id="current-password2"
|
||||
v-model="currentPassword"
|
||||
type="password"
|
||||
class="form-control"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</Confirm>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-4">{{ $t("Other Software") }}</h4>
|
||||
<div>
|
||||
{{ $t("For example: nginx, Apache and Traefik.") }} <br />
|
||||
{{ $t("Please read") }} <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy</a>.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../../components/HiddenInput.vue";
|
||||
import Confirm from "../Confirm.vue";
|
||||
|
||||
const prefix = "cloudflared_";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
Confirm
|
||||
},
|
||||
data() {
|
||||
// See /src/mixins/socket.js
|
||||
return this.$root.cloudflared;
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
created() {
|
||||
this.$root.getSocket().emit(prefix + "join");
|
||||
},
|
||||
unmounted() {
|
||||
this.$root.getSocket().emit(prefix + "leave");
|
||||
},
|
||||
methods: {
|
||||
start() {
|
||||
this.$root.getSocket().emit(prefix + "start", this.cloudflareTunnelToken);
|
||||
},
|
||||
stop() {
|
||||
this.$root.getSocket().emit(prefix + "stop", this.currentPassword, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
});
|
||||
},
|
||||
removeToken() {
|
||||
this.$root.getSocket().emit(prefix + "removeToken");
|
||||
this.cloudflareTunnelToken = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.remove-token {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
@@ -192,6 +192,12 @@
|
||||
<p>Пожалуйста, используйте с осторожностью.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'uk-UA' ">
|
||||
<p>Ви впевнені, що бажаєте <strong>вимкнути авторизацію</strong>?</p>
|
||||
<p>Це підходить для <strong>тих, у кого встановлена інша авторизація</strong> пееред відкриттям Uptime Kuma, наприклад Cloudflare Access.</p>
|
||||
<p>Будь ласка, використовуйте з обережністю.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'fa' ">
|
||||
<p>آیا مطمئن هستید که میخواهید <strong>احراز هویت را غیر فعال کنید</strong>?</p>
|
||||
<p>این ویژگی برای کسانی است که <strong> لایه امنیتی شخص ثالث دیگر بر روی این آدرس فعال کردهاند</strong>، مانند Cloudflare Access.</p>
|
||||
@@ -215,14 +221,14 @@
|
||||
<p>Dette er for <strong>de som har tredjepartsautorisering</strong> foran Uptime Kuma, for eksempel Cloudflare Access.</p>
|
||||
<p>Vennligst vær forsiktig.</p>
|
||||
</template>
|
||||
|
||||
|
||||
<template v-else-if="$i18n.locale === 'cs-CZ' ">
|
||||
<p>Opravdu chcete <strong>deaktivovat autentifikaci</strong>?</p>
|
||||
<p>Tato možnost je určena pro případy, kdy <strong>máte autentifikaci zajištěnou třetí stranou</strong> ještě před přístupem do Uptime Kuma, například prostřednictvím Cloudflare Access.</p>
|
||||
<p>Používejte ji prosím s rozmyslem.</p>
|
||||
</template>
|
||||
|
||||
<template v-else-if="$i18n.locale === 'vi-VN' ">
|
||||
<template v-else-if="$i18n.locale === 'vi-VN' ">
|
||||
<p>Bạn có muốn <strong>TẮT XÁC THỰC</strong> không?</p>
|
||||
<p>Điều này rất nguy hiểm<strong>BẤT KỲ AI</strong> cũng có thể truy cập và cướp quyền điều khiển.</p>
|
||||
<p>Vui lòng <strong>cẩn thận</strong>.</p>
|
||||
@@ -234,6 +240,19 @@
|
||||
<p>It is designed for scenarios <strong>where you intend to implement third-party authentication</strong> in front of Uptime Kuma such as Cloudflare Access, Authelia or other authentication mechanisms.</p>
|
||||
<p>Please use this option carefully!</p>
|
||||
</template>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="current-password2" class="form-label">
|
||||
{{ $t("Current Password") }}
|
||||
</label>
|
||||
<input
|
||||
id="current-password2"
|
||||
v-model="password.currentPassword"
|
||||
type="password"
|
||||
class="form-control"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</Confirm>
|
||||
</div>
|
||||
</template>
|
||||
@@ -310,7 +329,12 @@ export default {
|
||||
|
||||
disableAuth() {
|
||||
this.settings.disableAuth = true;
|
||||
this.saveSettings();
|
||||
|
||||
// Need current password to disable auth
|
||||
// Set it to empty if done
|
||||
this.saveSettings(() => {
|
||||
this.password.currentPassword = "";
|
||||
}, this.password.currentPassword);
|
||||
},
|
||||
|
||||
enableAuth() {
|
||||
@@ -331,7 +355,7 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
h5:after {
|
||||
h5::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 50%;
|
||||
|
@@ -29,7 +29,8 @@ const languageList = {
|
||||
"pl": "Polski",
|
||||
"et-EE": "eesti",
|
||||
"vi-VN": "Tiếng Việt",
|
||||
"zh-TW": "繁體中文 (台灣)"
|
||||
"zh-TW": "繁體中文 (台灣)",
|
||||
"uk-UA": "Український",
|
||||
};
|
||||
|
||||
let messages = {
|
||||
@@ -42,7 +43,7 @@ for (let lang in languageList) {
|
||||
};
|
||||
}
|
||||
|
||||
const rtlLangs = ["fa"];
|
||||
const rtlLangs = [ "fa" ];
|
||||
|
||||
export const currentLocale = () => localStorage.locale
|
||||
|| languageList[navigator.language] && navigator.language
|
||||
|
@@ -38,6 +38,8 @@ import {
|
||||
faPen,
|
||||
faExternalLinkSquareAlt,
|
||||
faSpinner,
|
||||
faUndo,
|
||||
faPlusCircle,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
library.add(
|
||||
@@ -75,6 +77,8 @@ library.add(
|
||||
faPen,
|
||||
faExternalLinkSquareAlt,
|
||||
faSpinner,
|
||||
faUndo,
|
||||
faPlusCircle,
|
||||
);
|
||||
|
||||
export { FontAwesomeIcon };
|
||||
|
@@ -197,7 +197,7 @@ export default {
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"Status Page": "Статус страница",
|
||||
"Status Pages": "Статус страница",
|
||||
"Status Pages": "Статус страници",
|
||||
"Primary Base URL": "Основен базов URL адрес",
|
||||
"Push URL": "Генериран Push URL адрес",
|
||||
needPushEvery: "Необходимо е да извършвате заявка към този URL адрес на всеки {0} секунди",
|
||||
@@ -343,7 +343,7 @@ export default {
|
||||
"No Monitors": "Няма монитори",
|
||||
"Untitled Group": "Група без заглавие",
|
||||
Services: "Услуги",
|
||||
Discard: "Премахни",
|
||||
Discard: "Отмени",
|
||||
Cancel: "Отмени",
|
||||
"Powered by": "Създадено чрез",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
@@ -371,4 +371,75 @@ export default {
|
||||
alertaAlertState: "Състояние на тревога",
|
||||
alertaRecoverState: "Състояние на възстановяване",
|
||||
deleteStatusPageMsg: "Сигурни ли сте, че желаете да изтриете тази статус страница?",
|
||||
Proxies: "Проксита",
|
||||
default: "По подразбиране",
|
||||
enabled: "Включено",
|
||||
setAsDefault: "Зададен по подразбиране",
|
||||
deleteProxyMsg: "Сигурни ли сте, че желаете да изтриете това прокси за всички монитори?",
|
||||
proxyDescription: "Прокситата трябва да бъдат зададени към монитор за да функционират.",
|
||||
enableProxyDescription: "Това прокси няма да има ефект върху заявките за мониторинг, докато не бъде активирано. Може да контролирате временното деактивиране на проксито от всички монитори чрез статуса на активиране.",
|
||||
setAsDefaultProxyDescription: "Това проки ще бъде включено по подразбиране за новите монитори. Може да го изключите по отделно за всеки един монитор.",
|
||||
"Certificate Chain": "Верига на сертификата",
|
||||
Valid: "Валиден",
|
||||
Invalid: "Невалиден",
|
||||
AccessKeyId: "ID на ключ за достъп",
|
||||
SecretAccessKey: "Тайна на ключа за достъп",
|
||||
PhoneNumbers: "Телефонни номера",
|
||||
TemplateCode: "Шаблон Код",
|
||||
SignName: "Знак име",
|
||||
"Sms template must contain parameters: ": "SMS шаблонът трябва да съдържа следните параметри: ",
|
||||
"Bark Endpoint": "Bark крайна точка",
|
||||
WebHookUrl: "URL адрес на уеб кука",
|
||||
SecretKey: "Таен ключ",
|
||||
"For safety, must use secret key": "За сигурност, трябва да се използва таен ключ",
|
||||
"Device Token": "Токен за устройство",
|
||||
Platform: "Платформа",
|
||||
iOS: "iOS",
|
||||
Android: "Android",
|
||||
Huawei: "Huawei",
|
||||
High: "Висок",
|
||||
Retry: "Повтори",
|
||||
Topic: "Тема",
|
||||
"WeCom Bot Key": "WeCom бот ключ",
|
||||
"Setup Proxy": "Настройка за прокси",
|
||||
"Proxy Protocol": "Прокси протокол",
|
||||
"Proxy Server": "Прокси сървър",
|
||||
"Proxy server has authentication": "Прокси сървърът е с удостоверяване",
|
||||
User: "Потребител",
|
||||
Installed: "Инсталиран",
|
||||
"Not installed": "Не е инсталиран",
|
||||
Running: "Работи",
|
||||
"Not running": "Не работи",
|
||||
"Remove Token": "Премахни токен",
|
||||
Start: "Старт",
|
||||
Stop: "Стоп",
|
||||
"Uptime Kuma": "Uptime Kuma",
|
||||
"Add New Status Page": "Добави нова статус страница",
|
||||
Slug: "Слъг",
|
||||
"Accept characters:": "Приеми символи:",
|
||||
startOrEndWithOnly: "Започва или завършва само с {0}",
|
||||
"No consecutive dashes": "Без последователни тирета",
|
||||
Next: "Следващ",
|
||||
"The slug is already taken. Please choose another slug.": "Този слъг вече се използва. Моля изберете друг.",
|
||||
"No Proxy": "Без прокси",
|
||||
"HTTP Basic Auth": "HTTP основно удостоверяване",
|
||||
"New Status Page": "Нова статус страница",
|
||||
"Page Not Found": "Страницата не е открита",
|
||||
"Reverse Proxy": "Ревърс прокси",
|
||||
Backup: "Архивиране",
|
||||
About: "Относно",
|
||||
wayToGetCloudflaredURL: "(Свалете \"cloudflared\" от {0})",
|
||||
cloudflareWebsite: "Cloudflare уебсайт",
|
||||
"Message:": "Съобщение:",
|
||||
"Don't know how to get the token? Please read the guide:": "Не знаете как да вземете токен? Моля, прочетете ръководството:",
|
||||
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Текущата връзка може да прекъсне ако в момента сте свързани чрез \"Cloudflare Tunnel\". Сигурни ли сте, че желаете да го спрете? Въведете Вашата текуща парола за да потвърдите.",
|
||||
"Other Software": "Друг софтуер",
|
||||
"For example: nginx, Apache and Traefik.": "Например: Nginx, Apache и Traefik.",
|
||||
"Please read": "Моля, прочетете",
|
||||
"Subject:": "Тема:",
|
||||
"Valid To:": "Валиден до:",
|
||||
"Days Remaining:": "Оставащи дни:",
|
||||
"Issuer:": "Издател:",
|
||||
"Fingerprint:": "Пръстов отпечатък:",
|
||||
"No status pages": "Няма статус страници",
|
||||
};
|
||||
|
@@ -337,7 +337,7 @@ export default {
|
||||
"Hide Tags": "Tags ausblenden",
|
||||
Description: "Beschreibung",
|
||||
"No monitors available.": "Keine Monitore verfügbar.",
|
||||
"Add one": "Füge eins hinzu",
|
||||
"Add one": "Hinzufügen",
|
||||
"No Monitors": "Keine Monitore",
|
||||
"Untitled Group": "Gruppe ohne Titel",
|
||||
Services: "Dienste",
|
||||
@@ -350,7 +350,7 @@ export default {
|
||||
serwersmsAPIPassword: "API Passwort",
|
||||
serwersmsPhoneNumber: "Telefonnummer",
|
||||
serwersmsSenderName: "Name des SMS-Absenders (über Kundenportal registriert)",
|
||||
"stackfield": "Stackfield",
|
||||
stackfield: "Stackfield",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
apiCredentials: "API Zugangsdaten",
|
||||
smtpDkimSettings: "DKIM Einstellungen",
|
||||
@@ -362,4 +362,87 @@ export default {
|
||||
smtpDkimHashAlgo: "Hash-Algorithmus (Optional)",
|
||||
smtpDkimheaderFieldNames: "Zu validierende Header-Schlüssel (optional)",
|
||||
smtpDkimskipFields: "Zu ignorierende Header Schlüssel (optional)",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API Endpunkt",
|
||||
alertaEnvironment: "Umgebung",
|
||||
alertaApiKey: "API Schlüssel",
|
||||
alertaAlertState: "Alarmstatus",
|
||||
alertaRecoverState: "Wiederherstellungsstatus",
|
||||
deleteStatusPageMsg: "Bist du sicher, dass du diese Status-Seite löschen willst?",
|
||||
Proxies: "Proxies",
|
||||
default: "Standard",
|
||||
enabled: "Aktiviert",
|
||||
setAsDefault: "Als Standard setzen",
|
||||
deleteProxyMsg: "Bist du sicher, dass du diesen Proxy für alle Monitore löschen willst?",
|
||||
proxyDescription: "Proxies müssen einem Monitor zugewiesen werden, um zu funktionieren.",
|
||||
enableProxyDescription: "Dieser Proxy wird keinen Effekt auf Monitor-Anfragen haben, bis er aktiviert ist. Du kannst ihn temporär von allen Monitoren nach Aktivierungsstatus deaktivieren.",
|
||||
setAsDefaultProxyDescription: "Dieser Proxy wird standardmäßig für alle neuen Monitore aktiviert sein. Du kannst den Proxy immernoch für jeden Monitor einzeln deaktivieren.",
|
||||
"Certificate Chain": "Zertifikatskette",
|
||||
Valid: "Gültig",
|
||||
Invalid: "Ungültig",
|
||||
AccessKeyId: "AccessKey ID",
|
||||
SecretAccessKey: "AccessKey Secret",
|
||||
PhoneNumbers: "Telefonnummern",
|
||||
TemplateCode: "Vorlagencode",
|
||||
SignName: "Signaturname",
|
||||
"Sms template must contain parameters: ": "SMS Vorlage muss folgende Parameter enthalten: ",
|
||||
"Bark Endpoint": "Bark Endpunkt",
|
||||
WebHookUrl: "Webhook URL",
|
||||
SecretKey: "Geheimer Schlüssel",
|
||||
"For safety, must use secret key": "Zur Sicherheit muss ein geheimer Schlüssel verwendet werden",
|
||||
"Device Token": "Gerätetoken",
|
||||
Platform: "Platform",
|
||||
iOS: "iOS",
|
||||
Android: "Android",
|
||||
Huawei: "Huawei",
|
||||
High: "Hoch",
|
||||
Retry: "Wiederholungen",
|
||||
Topic: "Thema",
|
||||
"WeCom Bot Key": "WeCom Bot Schlüssel",
|
||||
"Setup Proxy": "Proxy einrichten",
|
||||
"Proxy Protocol": "Proxy Protokoll",
|
||||
"Proxy Server": "Proxy Server",
|
||||
"Proxy server has authentication": "Proxy server hat Authentifizierung",
|
||||
User: "Benutzer",
|
||||
Installed: "Installiert",
|
||||
"Not installed": "Nicht installiert",
|
||||
Running: "Läuft",
|
||||
"Not running": "Gestoppt",
|
||||
"Remove Token": "Token entfernen",
|
||||
Start: "Start",
|
||||
Stop: "Stop",
|
||||
"Uptime Kuma": "Uptime Kuma",
|
||||
"Add New Status Page": "Neue Status-Seite hinzufügen",
|
||||
Slug: "Slug",
|
||||
"Accept characters:": "Akzeptierte Zeichen:",
|
||||
startOrEndWithOnly: "Nur mit {0} anfangen und enden",
|
||||
"No consecutive dashes": "Keine aufeinanderfolgenden Bindestriche",
|
||||
Next: "Weiter",
|
||||
"The slug is already taken. Please choose another slug.": "Der Slug ist bereits in Verwendung. Bitte wähle einen anderen.",
|
||||
"No Proxy": "Kein Proxy",
|
||||
"HTTP Basic Auth": "HTTP Basisauthentifizierung",
|
||||
"New Status Page": "Neue Status-Seite",
|
||||
"Page Not Found": "Seite nicht gefunden",
|
||||
"Reverse Proxy": "Reverse Proxy",
|
||||
Backup: "Sicherung",
|
||||
About: "Über",
|
||||
wayToGetCloudflaredURL: "(Lade cloudflared von {0} herunter)",
|
||||
cloudflareWebsite: "Cloudflare Website",
|
||||
"Message:": "Nachricht:",
|
||||
"Don't know how to get the token? Please read the guide:": "Du weißt nicht, wie man den Token bekommt? Lies die Anleitung dazu:",
|
||||
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Die aktuelle Verbindung kann unterbrochen werden, wenn du aktuell über Cloudflare Tunnel verbunden bist. Bist du sicher, dass du es stoppen willst? Gib zur Bestätigung dein aktuelles Passwort ein.",
|
||||
"Other Software": "Andere Software",
|
||||
"For example: nginx, Apache and Traefik.": "Zum Beispiel: nginx, Apache und Traefik.",
|
||||
"Please read": "Bitte lesen",
|
||||
"Subject:": "Betreff:",
|
||||
"Valid To:": "Gültig bis:",
|
||||
"Days Remaining:": "Tage verbleibend:",
|
||||
"Issuer:": "Aussteller:",
|
||||
"Fingerprint:": "Fingerabdruck:",
|
||||
"No status pages": "Keine Status-Seiten",
|
||||
Customize: "Anpassen",
|
||||
"Custom Footer": "Eigener Footer",
|
||||
"Custom CSS": "Eigenes CSS",
|
||||
};
|
||||
|
@@ -309,6 +309,10 @@ export default {
|
||||
"One record": "One record",
|
||||
steamApiKeyDescription: "For monitoring a Steam Game Server you need a Steam Web-API key. You can register your API key here: ",
|
||||
"Current User": "Current User",
|
||||
topic: "Topic",
|
||||
topicExplanation: "MQTT topic to monitor",
|
||||
successMessage: "Success Message",
|
||||
successMessageExplanation: "MQTT message that will be considered as success",
|
||||
recent: "Recent",
|
||||
Done: "Done",
|
||||
Info: "Info",
|
||||
@@ -331,21 +335,21 @@ export default {
|
||||
dark: "dark",
|
||||
Post: "Post",
|
||||
"Please input title and content": "Please input title and content",
|
||||
"Created": "Created",
|
||||
Created: "Created",
|
||||
"Last Updated": "Last Updated",
|
||||
"Unpin": "Unpin",
|
||||
Unpin: "Unpin",
|
||||
"Switch to Light Theme": "Switch to Light Theme",
|
||||
"Switch to Dark Theme": "Switch to Dark Theme",
|
||||
"Show Tags": "Show Tags",
|
||||
"Hide Tags": "Hide Tags",
|
||||
"Description": "Description",
|
||||
Description: "Description",
|
||||
"No monitors available.": "No monitors available.",
|
||||
"Add one": "Add one",
|
||||
"No Monitors": "No Monitors",
|
||||
"Untitled Group": "Untitled Group",
|
||||
"Services": "Services",
|
||||
"Discard": "Discard",
|
||||
"Cancel": "Cancel",
|
||||
Services: "Services",
|
||||
Discard: "Discard",
|
||||
Cancel: "Cancel",
|
||||
"Powered by": "Powered by",
|
||||
shrinkDatabaseDescription: "Trigger database VACUUM for SQLite. If your database is created after 1.10.0, AUTO_VACUUM is already enabled and this action is not needed.",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
@@ -354,6 +358,9 @@ export default {
|
||||
serwersmsPhoneNumber: "Phone number",
|
||||
serwersmsSenderName: "SMS Sender Name (registered via customer portal)",
|
||||
stackfield: "Stackfield",
|
||||
Customize: "Customize",
|
||||
"Custom Footer": "Custom Footer",
|
||||
"Custom CSS": "Custom CSS",
|
||||
smtpDkimSettings: "DKIM Settings",
|
||||
smtpDkimDesc: "Please refer to the Nodemailer DKIM {0} for usage.",
|
||||
documentation: "documentation",
|
||||
@@ -371,4 +378,85 @@ export default {
|
||||
alertaAlertState: "Alert State",
|
||||
alertaRecoverState: "Recover State",
|
||||
deleteStatusPageMsg: "Are you sure want to delete this status page?",
|
||||
Proxies: "Proxies",
|
||||
default: "Default",
|
||||
enabled: "Enabled",
|
||||
setAsDefault: "Set As Default",
|
||||
deleteProxyMsg: "Are you sure want to delete this proxy for all monitors?",
|
||||
proxyDescription: "Proxies must be assigned to a monitor to function.",
|
||||
enableProxyDescription: "This proxy will not effect on monitor requests until it is activated. You can control temporarily disable the proxy from all monitors by activation status.",
|
||||
setAsDefaultProxyDescription: "This proxy will be enabled by default for new monitors. You can still disable the proxy separately for each monitor.",
|
||||
"Certificate Chain": "Certificate Chain",
|
||||
Valid: "Valid",
|
||||
Invalid: "Invalid",
|
||||
AccessKeyId: "AccessKey ID",
|
||||
SecretAccessKey: "AccessKey Secret",
|
||||
PhoneNumbers: "PhoneNumbers",
|
||||
TemplateCode: "TemplateCode",
|
||||
SignName: "SignName",
|
||||
"Sms template must contain parameters: ": "Sms template must contain parameters: ",
|
||||
"Bark Endpoint": "Bark Endpoint",
|
||||
WebHookUrl: "WebHookUrl",
|
||||
SecretKey: "SecretKey",
|
||||
"For safety, must use secret key": "For safety, must use secret key",
|
||||
"Device Token": "Device Token",
|
||||
Platform: "Platform",
|
||||
iOS: "iOS",
|
||||
Android: "Android",
|
||||
Huawei: "Huawei",
|
||||
High: "High",
|
||||
Retry: "Retry",
|
||||
Topic: "Topic",
|
||||
"WeCom Bot Key": "WeCom Bot Key",
|
||||
"Setup Proxy": "Setup Proxy",
|
||||
"Proxy Protocol": "Proxy Protocol",
|
||||
"Proxy Server": "Proxy Server",
|
||||
"Proxy server has authentication": "Proxy server has authentication",
|
||||
User: "User",
|
||||
Installed: "Installed",
|
||||
"Not installed": "Not installed",
|
||||
Running: "Running",
|
||||
"Not running": "Not running",
|
||||
"Remove Token": "Remove Token",
|
||||
Start: "Start",
|
||||
Stop: "Stop",
|
||||
"Uptime Kuma": "Uptime Kuma",
|
||||
"Add New Status Page": "Add New Status Page",
|
||||
Slug: "Slug",
|
||||
"Accept characters:": "Accept characters:",
|
||||
"startOrEndWithOnly": "Start or end with {0} only",
|
||||
"No consecutive dashes": "No consecutive dashes",
|
||||
Next: "Next",
|
||||
"The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.",
|
||||
"No Proxy": "No Proxy",
|
||||
"HTTP Basic Auth": "HTTP Basic Auth",
|
||||
"New Status Page": "New Status Page",
|
||||
"Page Not Found": "Page Not Found",
|
||||
"Reverse Proxy": "Reverse Proxy",
|
||||
Backup: "Backup",
|
||||
About: "About",
|
||||
wayToGetCloudflaredURL: "(Download cloudflared from {0})",
|
||||
cloudflareWebsite: "Cloudflare Website",
|
||||
"Message:": "Message:",
|
||||
"Don't know how to get the token? Please read the guide:": "Don't know how to get the token? Please read the guide:",
|
||||
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.",
|
||||
"Other Software": "Other Software",
|
||||
"For example: nginx, Apache and Traefik.": "For example: nginx, Apache and Traefik.",
|
||||
"Please read": "Please read",
|
||||
"Subject:": "Subject:",
|
||||
"Valid To:": "Valid To:",
|
||||
"Days Remaining:": "Days Remaining:",
|
||||
"Issuer:": "Issuer:",
|
||||
"Fingerprint:": "Fingerprint:",
|
||||
"No status pages": "No status pages",
|
||||
"Domain Name Expiry Notification": "Domain Name Expiry Notification",
|
||||
"Proxy": "Proxy",
|
||||
"Date Created": "Date Created",
|
||||
onebotHttpAddress: "OneBot HTTP Address",
|
||||
onebotMessageType: "OneBot Message Type",
|
||||
onebotGroupMessage: "Group",
|
||||
onebotPrivateMessage: "Private",
|
||||
onebotUserOrGroupId: "Group/User ID",
|
||||
onebotSafetyTips: "For safety, must set access token",
|
||||
"PushDeer Key": "PushDeer Key",
|
||||
};
|
||||
|
@@ -197,7 +197,7 @@ export default {
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"Status Page": "Státusz oldal",
|
||||
"Status Pages": "Státusz oldal",
|
||||
"Status Pages": "Státusz oldalak",
|
||||
"Primary Base URL": "Elsődleges URL",
|
||||
"Push URL": "Meghívandó URL",
|
||||
needPushEvery: "Ezt az URL-t kell meghívni minden {0} másodpercben.",
|
||||
@@ -370,4 +370,5 @@ export default {
|
||||
alertaApiKey: "API kulcs",
|
||||
alertaAlertState: "Figyelmeztetési állapot",
|
||||
alertaRecoverState: "Visszaállási állapot",
|
||||
deleteStatusPageMsg: "Biztos, hogy törölni akarja a státusz oldalt?",
|
||||
};
|
||||
|
@@ -180,8 +180,8 @@ export default {
|
||||
"Add a monitor": "Добавить монитор",
|
||||
"Edit Status Page": "Редактировать",
|
||||
"Go to Dashboard": "Панель управления",
|
||||
"Status Page": "Мониторинг",
|
||||
"Status Pages": "Панель мониторингов",
|
||||
"Status Page": "Страница статуса",
|
||||
"Status Pages": "Страницы статуса",
|
||||
Discard: "Отмена",
|
||||
"Create Incident": "Создать инцидент",
|
||||
"Switch to Dark Theme": "Тёмная тема",
|
||||
@@ -311,28 +311,28 @@ export default {
|
||||
"One record": "Одна запись",
|
||||
steamApiKeyDescription: "Для мониторинга игрового сервера Steam вам необходим Web-API ключ Steam. Зарегистрировать его можно здесь: ",
|
||||
"Certificate Chain": "Цепочка сертификатов",
|
||||
"Valid": "Действительный",
|
||||
Valid: "Действительный",
|
||||
"Hide Tags": "Скрыть тэги",
|
||||
Title: "Название инцидента:",
|
||||
Content: "Содержание инцидента:",
|
||||
Post: "Опубликовать",
|
||||
"Cancel": "Отмена",
|
||||
"Created": "Создано",
|
||||
"Unpin": "Открепить",
|
||||
Cancel: "Отмена",
|
||||
Created: "Создано",
|
||||
Unpin: "Открепить",
|
||||
"Show Tags": "Показать тэги",
|
||||
"recent": "Сейчас",
|
||||
recent: "Сейчас",
|
||||
"3h": "3 часа",
|
||||
"6h": "6 часов",
|
||||
"24h": "24 часа",
|
||||
"1w": "1 неделя",
|
||||
"No monitors available.": "Нет доступных мониторов",
|
||||
"Add one": "Добавить новый",
|
||||
"Backup": "Резервная копия",
|
||||
"Security": "Безопасность",
|
||||
Backup: "Резервная копия",
|
||||
Security: "Безопасность",
|
||||
"Shrink Database": "Сжать Базу Данных",
|
||||
"Current User": "Текущий пользователь",
|
||||
"About": "О программе",
|
||||
"Description": "Описание",
|
||||
About: "О программе",
|
||||
Description: "Описание",
|
||||
"Powered by": "Работает на основе скрипта от",
|
||||
shrinkDatabaseDescription: "Включает VACUUM для базы данных SQLite. Если ваша база данных была создана на версии 1.10.0 и более, AUTO_VACUUM уже включен и это действие не требуется.",
|
||||
deleteStatusPageMsg: "Вы действительно хотите удалить эту страницу статуса сервисов?",
|
||||
@@ -343,14 +343,58 @@ export default {
|
||||
primary: "ОСНОВНОЙ",
|
||||
light: "СВЕТЛЫЙ",
|
||||
dark: "ТЕМНЫЙ",
|
||||
"New Status Page": "Новый мониторинг",
|
||||
"New Status Page": "Новая страница статуса",
|
||||
"Show update if available": "Показывать доступные обновления",
|
||||
"Also check beta release": "Проверять обновления для бета версий",
|
||||
"Add New Status Page": "Добавить страницу мониторинга",
|
||||
"Next": "Далее",
|
||||
"Add New Status Page": "Добавить страницу статуса",
|
||||
Next: "Далее",
|
||||
"Accept characters: a-z 0-9 -": "Разрешены символы: a-z 0-9 -",
|
||||
"Start or end with a-z 0-9 only": "Начало и окончание имени только на символы: a-z 0-9",
|
||||
"No consecutive dashes --": "Запрещено использовать тире --",
|
||||
"HTTP Options": "HTTP Опции",
|
||||
"Basic Auth": "HTTP Авторизация",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
GoogleChat: "Google Chat (только Google Workspace)",
|
||||
apiCredentials: "API реквизиты",
|
||||
Done: "Готово",
|
||||
Info: "Инфо",
|
||||
"Steam API Key": "Steam API-Ключ",
|
||||
"Pick a RR-Type...": "Выберите RR-Тип...",
|
||||
"Pick Accepted Status Codes...": "Выберите принятые коды состояния...",
|
||||
Default: "По умолчанию",
|
||||
"Please input title and content": "Пожалуйста, введите название и содержание",
|
||||
"Last Updated": "Последнее Обновление",
|
||||
"Untitled Group": "Группа без названия",
|
||||
Services: "Сервисы",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "API Пользователь (включая префикс webapi_)",
|
||||
serwersmsAPIPassword: "API Пароль",
|
||||
serwersmsPhoneNumber: "Номер телефона",
|
||||
serwersmsSenderName: "SMS Имя Отправителя (регистрированный через пользовательский портал)",
|
||||
stackfield: "Stackfield",
|
||||
smtpDkimSettings: "DKIM Настройки",
|
||||
smtpDkimDesc: "Пожалуйста ознакомьтесь с {0} Nodemailer DKIM для использования.",
|
||||
documentation: "документацией",
|
||||
smtpDkimDomain: "Имя Домена",
|
||||
smtpDkimKeySelector: "Ключ",
|
||||
smtpDkimPrivateKey: "Приватный ключ",
|
||||
smtpDkimHashAlgo: "Алгоритм хэша (опционально)",
|
||||
smtpDkimheaderFieldNames: "Заголовок ключей для подписи (опционально)",
|
||||
smtpDkimskipFields: "Заколовок ключей не для подписи (опционально)",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "Конечная точка API",
|
||||
alertaEnvironment: "Среда",
|
||||
alertaApiKey: "Ключ API",
|
||||
alertaAlertState: "Состояние алерта",
|
||||
alertaRecoverState: "Состояние восстановления",
|
||||
Proxies: "Прокси",
|
||||
default: "По умолчанию",
|
||||
enabled: "Включено",
|
||||
setAsDefault: "Установлено по умолчанию",
|
||||
deleteProxyMsg: "Вы действительно хотите удалить этот прокси для всех мониторов?",
|
||||
proxyDescription: "Прокси должны быть привязаны к монитору, чтобы работать.",
|
||||
enableProxyDescription: "Этот прокси не будет влиять на запросы монитора, пока не будет активирован. Вы можете контролировать временное отключение прокси для всех мониторов через статус активации.",
|
||||
setAsDefaultProxyDescription: "Этот прокси будет по умолчанию включен для новых мониторов. Вы всё ещё можете отдельно отключать прокси в каждом мониторе.",
|
||||
};
|
||||
|
392
src/languages/uk-UA.js
Normal file
392
src/languages/uk-UA.js
Normal file
@@ -0,0 +1,392 @@
|
||||
export default {
|
||||
languageName: "Український",
|
||||
checkEverySecond: "Перевірка кожні {0} секунд",
|
||||
retriesDescription: "Максимальна кількість спроб перед позначенням сервісу як недоступного та надсиланням повідомлення",
|
||||
ignoreTLSError: "Ігнорувати помилку TLS/SSL для сайтів HTTPS",
|
||||
upsideDownModeDescription: "Реверс статусу сервісу. Якщо сервіс доступний, він позначається як НЕДОСТУПНИЙ.",
|
||||
maxRedirectDescription: "Максимальна кількість перенаправлень. Поставте 0, щоб вимкнути перенаправлення.",
|
||||
acceptedStatusCodesDescription: "Виберіть коди статусів для визначення доступності сервісу.",
|
||||
passwordNotMatchMsg: "Повторення паролю не збігається.",
|
||||
notificationDescription: "Прив'яжіть повідомлення до моніторів.",
|
||||
keywordDescription: "Пошук слова в чистому HTML або JSON-відповіді (чутливо до регістру)",
|
||||
pauseDashboardHome: "Пауза",
|
||||
deleteMonitorMsg: "Ви дійсно хочете видалити цей монітор?",
|
||||
deleteNotificationMsg: "Ви дійсно хочете видалити це повідомлення для всіх моніторів?",
|
||||
resolverserverDescription: "Cloudflare є сервером за замовчуванням. Ви завжди можете змінити цей сервер.",
|
||||
rrtypeDescription: "Виберіть тип ресурсного запису, який ви хочете відстежувати",
|
||||
pauseMonitorMsg: "Ви дійсно хочете поставити на паузу?",
|
||||
Settings: "Налаштування",
|
||||
Dashboard: "Панель управління",
|
||||
"New Update": "Оновлення",
|
||||
Language: "Мова",
|
||||
Appearance: "Зовнішній вигляд",
|
||||
Theme: "Тема",
|
||||
General: "Загальне",
|
||||
Version: "Версія",
|
||||
"Check Update On GitHub": "Перевірити оновлення на GitHub",
|
||||
List: "Список",
|
||||
Add: "Додати",
|
||||
"Add New Monitor": "Новий монітор",
|
||||
"Quick Stats": "Статистика",
|
||||
Up: "Доступний",
|
||||
Down: "Недоступний",
|
||||
Pending: "Очікування",
|
||||
Unknown: "Невідомо",
|
||||
Pause: "Пауза",
|
||||
Name: "Ім'я",
|
||||
Status: "Статус",
|
||||
DateTime: "Дата і час",
|
||||
Message: "Повідомлення",
|
||||
"No important events": "Важливих подій немає",
|
||||
Resume: "Відновити",
|
||||
Edit: "Змінити",
|
||||
Delete: "Видалити",
|
||||
Current: "Поточний",
|
||||
Uptime: "Аптайм",
|
||||
"Cert Exp.": "Сертифікат спливає",
|
||||
days: "днів",
|
||||
day: "день",
|
||||
"-day": " днів",
|
||||
hour: "година",
|
||||
"-hour": " години",
|
||||
Response: "Відповідь",
|
||||
Ping: "Пінг",
|
||||
"Monitor Type": "Тип монітора",
|
||||
Keyword: "Ключове слово",
|
||||
"Friendly Name": "Ім'я",
|
||||
URL: "URL",
|
||||
Hostname: "Ім'я хоста",
|
||||
Port: "Порт",
|
||||
"Heartbeat Interval": "Частота опитування",
|
||||
Retries: "Спроб",
|
||||
Advanced: "Додатково",
|
||||
"Upside Down Mode": "Реверс статусу",
|
||||
"Max. Redirects": "Макс. кількість перенаправлень",
|
||||
"Accepted Status Codes": "Припустимі коди статусу",
|
||||
Save: "Зберегти",
|
||||
Notifications: "Повідомлення",
|
||||
"Not available, please setup.": "Доступних сповіщень немає, необхідно створити.",
|
||||
"Setup Notification": "Створити сповіщення",
|
||||
Light: "Світла",
|
||||
Dark: "Темна",
|
||||
Auto: "Авто",
|
||||
"Theme - Heartbeat Bar": "Тема - Смуга частоти опитування",
|
||||
Normal: "Звичайний",
|
||||
Bottom: "Знизу",
|
||||
None: "Відсутня",
|
||||
Timezone: "Часовий пояс",
|
||||
"Search Engine Visibility": "Індексація пошуковими системами:",
|
||||
"Allow indexing": "Дозволити індексування",
|
||||
"Discourage search engines from indexing site": "Заборонити індексування",
|
||||
"Change Password": "Змінити пароль",
|
||||
"Current Password": "Поточний пароль",
|
||||
"New Password": "Новий пароль",
|
||||
"Repeat New Password": "Повтор нового пароля",
|
||||
"Update Password": "Оновити пароль",
|
||||
"Disable Auth": "Вимкнути авторизацію",
|
||||
"Enable Auth": "Увімкнути авторизацію",
|
||||
Logout: "Вийти",
|
||||
Leave: "Відміна",
|
||||
"I understand, please disable": "Я розумію, все одно відключити",
|
||||
Confirm: "Підтвердити",
|
||||
Yes: "Так",
|
||||
No: "Ні",
|
||||
Username: "Логін",
|
||||
Password: "Пароль",
|
||||
"Remember me": "Запам'ятати мене",
|
||||
Login: "Вхід до системи",
|
||||
"No Monitors, please": "Моніторів немає, будь ласка",
|
||||
"No Monitors": "Монітори відсутні",
|
||||
"add one": "створіть новий",
|
||||
"Notification Type": "Тип повідомлення",
|
||||
Email: "Пошта",
|
||||
Test: "Перевірка",
|
||||
"Certificate Info": "Інформація про сертифікат",
|
||||
"Resolver Server": "DNS сервер",
|
||||
"Resource Record Type": "Тип ресурсного запису",
|
||||
"Last Result": "Останній результат",
|
||||
"Create your admin account": "Створіть обліковий запис адміністратора",
|
||||
"Repeat Password": "Повторіть пароль",
|
||||
respTime: "Час відповіді (мс)",
|
||||
notAvailableShort: "Н/д",
|
||||
Create: "Створити",
|
||||
clearEventsMsg: "Ви дійсно хочете видалити всю статистику подій цього монітора?",
|
||||
clearHeartbeatsMsg: "Ви дійсно хочете видалити всю статистику опитувань цього монітора?",
|
||||
confirmClearStatisticsMsg: "Ви дійсно хочете видалити ВСЮ статистику?",
|
||||
"Clear Data": "Видалити статистику",
|
||||
Events: "Події",
|
||||
Heartbeats: "Опитування",
|
||||
"Auto Get": "Авто-отримання",
|
||||
enableDefaultNotificationDescription: "Для кожного нового монітора це повідомлення буде включено за замовчуванням. Ви все ще можете відключити повідомлення в кожному моніторі окремо.",
|
||||
"Default enabled": "Використовувати за промовчанням",
|
||||
"Also apply to existing monitors": "Застосувати до існуючих моніторів",
|
||||
Export: "Експорт",
|
||||
Import: "Імпорт",
|
||||
backupDescription: "Ви можете зберегти резервну копію всіх моніторів та повідомлень у вигляді JSON-файлу",
|
||||
backupDescription2: "P.S.: Історія та події збережені не будуть",
|
||||
backupDescription3: "Важливі дані, такі як токени повідомлень, додаються під час експорту, тому зберігайте файли в безпечному місці",
|
||||
alertNoFile: "Виберіть файл для імпорту.",
|
||||
alertWrongFileType: "Виберіть JSON-файл.",
|
||||
twoFAVerifyLabel: "Будь ласка, введіть свій токен, щоб перевірити роботу 2FA",
|
||||
tokenValidSettingsMsg: "Токен дійсний! Тепер ви можете зберегти налаштування 2FA.",
|
||||
confirmEnableTwoFAMsg: "Ви дійсно хочете увімкнути 2FA?",
|
||||
confirmDisableTwoFAMsg: "Ви дійсно хочете вимкнути 2FA?",
|
||||
"Apply on all existing monitors": "Застосувати до всіх існуючих моніторів",
|
||||
"Verify Token": "Перевірити токен",
|
||||
"Setup 2FA": "Налаштування 2FA",
|
||||
"Enable 2FA": "Увімкнути 2FA",
|
||||
"Disable 2FA": "Вимкнути 2FA",
|
||||
"2FA Settings": "Налаштування 2FA",
|
||||
"Two Factor Authentication": "Двофакторна аутентифікація",
|
||||
Active: "Активно",
|
||||
Inactive: "Неактивно",
|
||||
Token: "Токен",
|
||||
"Show URI": "Показати URI",
|
||||
"Clear all statistics": "Очистити статистику",
|
||||
retryCheckEverySecond: "Повтор кожні {0} секунд",
|
||||
importHandleDescription: "Виберіть \"Пропустити існуючі\", якщо ви хочете пропустити кожен монітор або повідомлення з таким же ім'ям. \"Перезаписати\" видалить кожен існуючий монітор або повідомлення та додасть заново. Варіант \"Не перевіряти\" примусово відновлює всі монітори і повідомлення, навіть якщо вони вже існують.",
|
||||
confirmImportMsg: "Ви дійсно хочете відновити резервну копію? Переконайтеся, що ви вибрали відповідний варіант імпорту.",
|
||||
"Heartbeat Retry Interval": "Інтервал повтору опитування",
|
||||
"Import Backup": "Імпорт",
|
||||
"Export Backup": "Експорт",
|
||||
"Skip existing": "Пропустити існуючі",
|
||||
Overwrite: "Перезаписати",
|
||||
Options: "Опції",
|
||||
"Keep both": "Не перевіряти",
|
||||
Tags: "Теги",
|
||||
"Add New below or Select...": "Додати новий або вибрати...",
|
||||
"Tag with this name already exist.": "Такий тег вже існує.",
|
||||
"Tag with this value already exist.": "Тег із таким значенням вже існує.",
|
||||
color: "колір",
|
||||
"value (optional)": "значення (опціонально)",
|
||||
Gray: "Сірий",
|
||||
Red: "Червоний",
|
||||
Orange: "Помаранчевий",
|
||||
Green: "Зелений",
|
||||
Blue: "Синій",
|
||||
Indigo: "Індиго",
|
||||
Purple: "Пурпурний",
|
||||
Pink: "Рожевий",
|
||||
"Search...": "Пошук...",
|
||||
"Avg. Ping": "Середнє значення пінгу",
|
||||
"Avg. Response": "Середній час відповіді",
|
||||
"Entry Page": "Головна сторінка",
|
||||
statusPageNothing: "Тут порожньо. Додайте групу або монітор.",
|
||||
"No Services": "Немає сервісів",
|
||||
"All Systems Operational": "Всі системи працюють у штатному режимі",
|
||||
"Partially Degraded Service": "Сервіси працюють частково",
|
||||
"Degraded Service": "Всі сервіси не працюють",
|
||||
"Add Group": "Додати групу",
|
||||
"Add a monitor": "Додати монітор",
|
||||
"Edit Status Page": "Редагувати",
|
||||
"Go to Dashboard": "Панель управління",
|
||||
"Status Page": "Сторінка статусу",
|
||||
"Status Pages": "Сторінки статусу",
|
||||
Discard: "Скасування",
|
||||
"Create Incident": "Створити інцидент",
|
||||
"Switch to Dark Theme": "Темна тема",
|
||||
"Switch to Light Theme": "Світла тема",
|
||||
telegram: "Telegram",
|
||||
webhook: "Вебхук",
|
||||
smtp: "Email (SMTP)",
|
||||
discord: "Discord",
|
||||
teams: "Microsoft Teams",
|
||||
signal: "Signal",
|
||||
gotify: "Gotify",
|
||||
slack: "Slack",
|
||||
"rocket.chat": "Rocket.chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (Підтримка 50+ сервісів повідомлень)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
"Primary Base URL": "Основна URL",
|
||||
"Push URL": "URL пуша",
|
||||
needPushEvery: "До цієї URL необхідно звертатися кожні {0} секунд",
|
||||
pushOptionalParams: "Опціональні параметри: {0}",
|
||||
defaultNotificationName: "Моє повідомлення {notification} ({number})",
|
||||
here: "тут",
|
||||
Required: "Потрібно",
|
||||
"Bot Token": "Токен бота",
|
||||
wayToGetTelegramToken: "Ви можете взяти токен тут - {0}.",
|
||||
"Chat ID": "ID чату",
|
||||
supportTelegramChatID: "Підтримуються ID чатів, груп та каналів",
|
||||
wayToGetTelegramChatID: "Ви можете взяти ID вашого чату, відправивши повідомлення боту і перейшовши по цьому URL для перегляду chat_id:",
|
||||
"YOUR BOT TOKEN HERE": "ВАШ ТОКЕН БОТА ТУТ",
|
||||
chatIDNotFound: "ID чату не знайдено; будь ласка, відправте спочатку повідомлення боту",
|
||||
"Post URL": "Post URL",
|
||||
"Content Type": "Тип контенту",
|
||||
webhookJsonDesc: "{0} підходить для будь-яких сучасних HTTP-серверів, наприклад Express.js",
|
||||
webhookFormDataDesc: "{multipart} підходить для PHP. JSON-вивід необхідно буде обробити за допомогою {decodeFunction}",
|
||||
secureOptionNone: "Ні / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "Ігнорувати помилки TLS",
|
||||
"From Email": "Від кого",
|
||||
emailCustomSubject: "Своя тема",
|
||||
"To Email": "Кому",
|
||||
smtpCC: "Копія",
|
||||
smtpBCC: "Прихована копія",
|
||||
"Discord Webhook URL": "Discord Вебхук URL",
|
||||
wayToGetDiscordURL: "Ви можете створити його в Параметрах сервера -> Інтеграції -> Створити вебхук",
|
||||
"Bot Display Name": "Ім'я бота, що відображається",
|
||||
"Prefix Custom Message": "Свій префікс повідомлення",
|
||||
"Hello @everyone is...": "Привіт {'@'}everyone це...",
|
||||
"Webhook URL": "URL вебхука",
|
||||
wayToGetTeamsURL: "Як створити URL вебхука ви можете дізнатися тут - {0}.",
|
||||
Номер: "Номер",
|
||||
Recipients: "Одержувачі",
|
||||
needSignalAPI: "Вам необхідний клієнт Signal із підтримкою REST API.",
|
||||
wayToCheckSignalURL: "Пройдіть по цьому URL, щоб дізнатися як налаштувати такий клієнт:",
|
||||
signalImportant: "ВАЖЛИВО: Не можна змішувати в Одержувачах групи та номери!",
|
||||
"Application Token": "Токен програми",
|
||||
"Server URL": "URL сервера",
|
||||
Priority: "Пріоритет",
|
||||
"Icon Emoji": "Іконка Emoji",
|
||||
"Channel Name": "Ім'я каналу",
|
||||
"Uptime Kuma URL": "Uptime Kuma URL",
|
||||
aboutWebhooks: "Більше інформації про вебхуки: {0}",
|
||||
aboutChannelName: "Введіть ім'я каналу в поле {0} Ім'я каналу, якщо ви хочете обійти канал вебхука. Наприклад: #other-channel",
|
||||
aboutKumaURL: "Якщо поле Uptime Kuma URL в налаштуваннях залишиться порожнім, за замовчуванням буде використовуватися посилання на проект на GitHub.",
|
||||
emojiCheatSheet: "Шпаргалка по Emoji: {0}",
|
||||
"User Key": "Ключ користувача",
|
||||
Device: "Пристрій",
|
||||
"Message Title": "Заголовок повідомлення",
|
||||
"Notification Sound": "Звук повідомлення",
|
||||
"More info on:": "Більше інформації: {0}",
|
||||
pushoverDesc1: "Екстренний пріоритет (2) має таймуут повтору за замовчуванням 30 секунд і закінчується через 1 годину.",
|
||||
pushoverDesc2: "Якщо ви бажаєте надсилати повідомлення різним пристроям, необхідно заповнити поле Пристрій.",
|
||||
"SMS Type": "Тип SMS",
|
||||
octopushTypePremium: "Преміум (Швидкий - рекомендується для алертів)",
|
||||
octopushTypeLowCost: "Дешевий (Повільний - іноді блокується операторами)",
|
||||
checkPrice: "Тарифи {0}:",
|
||||
octopushLegacyHint: "Ви використовуєте стару версію Octopush (2011-2020) або нову?",
|
||||
"Check octopush prices": "Тарифи Octopush {0}.",
|
||||
octopushPhoneNumber: "Номер телефону (між. формат, наприклад: +380123456789)",
|
||||
octopushSMSSender: "Ім'я відправника SMS: 3-11 символів алвафіту, цифр та пробілів (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "ID пристрою LunaSea",
|
||||
"Apprise URL": "Apprise URL",
|
||||
"Example:": "Приклад: {0}",
|
||||
"Read more:": "Докладніше: {0}",
|
||||
"Status:": "Статус: {0}",
|
||||
"Read more": "Докладніше",
|
||||
appriseInstalled: "Apprise встановлено.",
|
||||
appriseNotInstalled: "Apprise не встановлено. {0}",
|
||||
"Access Token": "Токен доступу",
|
||||
"Channel access token": "Токен доступу каналу",
|
||||
"Line Developers Console": "Консоль розробників Line",
|
||||
lineDevConsoleTo: "Консоль розробників Line - {0}",
|
||||
"Basic Settings": "Базові налаштування",
|
||||
"User ID": "ID користувача",
|
||||
"Messaging API": "API повідомлень",
|
||||
wayToGetLineChannelToken: "Спочатку зайдіть в {0}, створіть провайдера та канал (API повідомлень), потім ви зможете отримати токен доступу каналу та ID користувача з вищезгаданих пунктів меню.",
|
||||
"Icon URL": "URL іконки",
|
||||
aboutIconURL: "Ви можете надати посилання на іконку в полі \"URL іконки\", щоб перевизначити картинку профілю за замовчуванням. Не використовується, якщо задана іконка Emoji.",
|
||||
aboutMattermostChannelName: "Ви можете перевизначити канал за замовчуванням, в який пише вебхук, ввівши ім'я каналу в полі \"Ім'я каналу\". Це необхідно включити в налаштуваннях вебхука Mattermost. Наприклад: #other-channel",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - дешево та повільно, часто перевантажений. Тільки для одержувачів з Польщі.",
|
||||
promosmsTypeFlash: "SMS FLASH - повідомлення автоматично з'являться на пристрої одержувача. Тільки для одержувачів з Польщі.",
|
||||
promosmsTypeFull: "SMS FULL - преміум-рівень SMS, можна використовувати своє ім'я відправника (попередньо зареєструвавши його). Надійно для алертів.",
|
||||
promosmsTypeSpeed: "SMS SPEED - найвищий пріоритет у системі. Дуже швидко і надійно, але дуже дорого (вдвічі дорожче, ніж SMS FULL).",
|
||||
promosmsPhoneNumber: "Номер телефону (для одержувачів з Польщі можна пропустити код регіону)",
|
||||
promosmsSMSSender: "Ім'я відправника SMS: Зареєстроване або одне з імен за замовчуванням: InfoSMS, SMS Info, MaxSMS, INFO, SMS",
|
||||
"Feishu WebHookURL": "Feishu WebHookURL",
|
||||
matrixHomeserverURL: "URL сервера (разом з http(s):// і опціонально порт)",
|
||||
"Internal Room Id": "Внутрішній ID кімнати",
|
||||
matrixDesc1: "Внутрішній ID кімнати можна знайти в Подробицях у параметрах каналу вашого Matrix клієнта. Він повинен виглядати приблизно як !QMdRCpUIfLwsfjxye6:home.server.",
|
||||
matrixDesc2: "Рекомендується створити нового користувача і не використовувати токен доступу особистого користувача Matrix, тому що це спричиняє повний доступ до облікового запису та до кімнат, в яких ви є. Замість цього створіть нового користувача і запросіть його тільки в ту кімнату, в якій ви хочете отримувати повідомлення.Токен доступу можна отримати, виконавши команду {0}",
|
||||
Method: "Метод",
|
||||
Body: "Тіло",
|
||||
Headers: "Заголовки",
|
||||
PushUrl: "URL пуша",
|
||||
HeadersInvalidFormat: "Заголовки запиту некоректні JSON: ",
|
||||
BodyInvalidFormat: "Тіло запиту некоректне JSON: ",
|
||||
"Monitor History": "Статистика",
|
||||
clearDataOlderThan: "Зберігати статистику за {0} днів.",
|
||||
PasswordsDoNotMatch: "Паролі не співпадають.",
|
||||
records: "записів",
|
||||
"One record": "Один запис",
|
||||
steamApiKeyDescription: "Для моніторингу ігрового сервера Steam вам потрібен Web-API ключ Steam. Зареєструвати його можна тут: ",
|
||||
"Certificate Chain": "Ланцюжок сертифікатів",
|
||||
Valid: "Дійсний",
|
||||
"Hide Tags": "Приховати теги",
|
||||
Title: "Назва інциденту:",
|
||||
Content: "Зміст інциденту:",
|
||||
Post: "Опублікувати",
|
||||
Cancel: "Скасувати",
|
||||
Created: "Створено",
|
||||
Unpin: "Відкріпити",
|
||||
"Show Tags": "Показати теги",
|
||||
recent: "Зараз",
|
||||
"3h": "3 години",
|
||||
"6h": "6 годин",
|
||||
"24h": "24 години",
|
||||
"1w": "1 тиждень",
|
||||
"No monitors available.": "Немає доступних моніторів",
|
||||
"Add one": "Додати новий",
|
||||
Backup: "Резервна копія",
|
||||
Security: "Безпека",
|
||||
"Shrink Database": "Стиснути базу даних",
|
||||
"Current User": "Поточний користувач",
|
||||
About: "Про програму",
|
||||
Description: "Опис",
|
||||
"Powered by": "Працює на основі скрипту від",
|
||||
shrinkDatabaseDescription: "Включає VACUUM для бази даних SQLite. Якщо база даних була створена на версії 1.10.0 і більше, AUTO_VACUUM вже включений і ця дія не потрібна.",
|
||||
Style: "Стиль",
|
||||
info: "ІНФО",
|
||||
warning: "УВАГА",
|
||||
danger: "ПОМИЛКА",
|
||||
primary: "ОСНОВНИЙ",
|
||||
light: "СВІТЛИЙ",
|
||||
dark: "ТЕМНИЙ",
|
||||
"New Status Page": "Нова сторінка статусу",
|
||||
"Show update if available": "Показувати доступні оновлення",
|
||||
"Also check beta release": "Перевіряти оновлення для бета версій",
|
||||
"Add New Status Page": "Додати сторінку статусу",
|
||||
Next: "Далі",
|
||||
"Acz characters: a-z 0-9 -": "Дозволені символи: a-z 0-9 -",
|
||||
"Start or end with a-z 0-9 only": "Початок та закінчення імені лише на символи: a-z 0-9",
|
||||
"No consecutive dashes --": "Заборонено використовувати тире --",
|
||||
"HTTP Options": "HTTP Опції",
|
||||
"Basic Auth": "HTTP Авторизація",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
GoogleChat: "Google Chat (тільки Google Workspace)",
|
||||
apiCredentials: "API реквізити",
|
||||
Done: "Готово",
|
||||
Info: "Інфо",
|
||||
"Steam API Key": "Steam API-Ключ",
|
||||
"Pick a RR-Type...": "Виберіть RR-тип...",
|
||||
"Pick Accepted Status Codes...": "Виберіть прийняті коди стану...",
|
||||
Default: "За замовчуванням",
|
||||
"Please input title and content": "Будь ласка, введіть назву та зміст",
|
||||
"Last Updated": "Останнє Оновлення",
|
||||
"Untitled Group": "Група без назви",
|
||||
Services: "Сервіси",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "API Користувач (включаючи префікс webapi_)",
|
||||
serwersmsAPIPassword: "API Пароль",
|
||||
serwersmsPhoneNumber: "Номер телефону",
|
||||
serwersmsSenderName: "SMS ім'я відправника (реєстрований через портал користувача)",
|
||||
stackfield: "Stackfield",
|
||||
smtpDkimSettings: "DKIM Налаштування",
|
||||
smtpDkimDesc: "Повернутися до Nodemailer DKIM {0} для використання.",
|
||||
documentation: "документація",
|
||||
smtpDkimDomain: "Ім'я домена",
|
||||
smtpDkimKeySelector: "Ключ",
|
||||
smtpDkimPrivateKey: "Приватний ключ",
|
||||
smtpDkimHashAlgo: "Алгоритм хеша (опціонально)",
|
||||
smtpDkimheaderFieldNames: "Заголовок ключів для підпису (опціонально)",
|
||||
smtpDkimskipFields: "Заколовок ключів не для підпису (опціонально)",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "Кінцева точка API",
|
||||
alertaEnvironment: "Середовище",
|
||||
alertaApiKey: "Ключ API",
|
||||
alertaAlertState: "Стан алерту",
|
||||
alertaRecoverState: "Стан відновлення",
|
||||
deleteStatusPageMsg: "Дійсно хочете видалити цю сторінку статусів?",
|
||||
};
|
@@ -88,8 +88,8 @@ export default {
|
||||
Dark: "黑暗",
|
||||
Auto: "自动",
|
||||
"Theme - Heartbeat Bar": "主题 - 心跳栏",
|
||||
Normal: "正常显示",
|
||||
Bottom: "靠下显示",
|
||||
Normal: "正常", // 此处还供 Gorush 的通知优先级功能使用,不应翻译为“正常显示”
|
||||
Bottom: "靠下",
|
||||
None: "不显示",
|
||||
Timezone: "时区",
|
||||
"Search Engine Visibility": "搜索引擎可见性",
|
||||
@@ -373,4 +373,89 @@ export default {
|
||||
"For safety, must use secret key": "出于安全考虑,必须使用加签密钥",
|
||||
WeCom: "企业微信群机器人",
|
||||
"WeCom Bot Key": "企业微信群机器人 Key",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API 接入点",
|
||||
alertaEnvironment: "环境参数",
|
||||
alertaApiKey: "API Key",
|
||||
alertaAlertState: "报警时的严重性",
|
||||
alertaRecoverState: "恢复后的严重性",
|
||||
deleteStatusPageMsg: "您确认要删除此状态页吗?",
|
||||
Proxies: "代理",
|
||||
default: "默认",
|
||||
enabled: "启用",
|
||||
setAsDefault: "设为默认",
|
||||
deleteProxyMsg: "您确认要在所有监控项中删除此代理吗?",
|
||||
proxyDescription: "代理必须配置到至少一个监控项后才会工作。",
|
||||
enableProxyDescription: "此代理必须启用才能对监控项的网络请求起作用。您可以通过修改激活状态,临时在所有监控项中禁用此代理。",
|
||||
setAsDefaultProxyDescription: "此代理会对新创建的监控项默认激活,您仍可以在监控项配置中单独禁用此代理。",
|
||||
"Proxy Protocol": "代理协议",
|
||||
"Proxy Server": "代理服务器",
|
||||
"Server Address": "服务器地址",
|
||||
"Certificate Chain": "证书链",
|
||||
Valid: "有效",
|
||||
Invalid: "无效",
|
||||
AccessKeyId: "AccessKey ID",
|
||||
SecretAccessKey: "AccessKey Secret",
|
||||
/* 以下为阿里云短信服务 API Dysms#SendSms 的参数 */
|
||||
PhoneNumbers: "PhoneNumbers",
|
||||
TemplateCode: "TemplateCode",
|
||||
SignName: "SignName",
|
||||
/* 以上为阿里云短信服务 API Dysms#SendSms 的参数 */
|
||||
"Bark Endpoint": "Bark 接入点",
|
||||
"Device Token": "Apple Device Token",
|
||||
Platform: "平台",
|
||||
iOS: "iOS",
|
||||
Android: "Android",
|
||||
Huawei: "华为",
|
||||
High: "高",
|
||||
Retry: "重试次数",
|
||||
Topic: "Gorush Topic",
|
||||
"Setup Proxy": "设置代理",
|
||||
"Proxy server has authentication": "代理服务器启用了身份验证功能",
|
||||
User: "用户名",
|
||||
Installed: "已安装",
|
||||
"Not installed": "未安装",
|
||||
Running: "运行中",
|
||||
"Not running": "未运行",
|
||||
"Message:": "信息:",
|
||||
wayToGetCloudflaredURL: "(可从 {0} 下载 cloudflared)",
|
||||
cloudflareWebsite: "Cloudflare 网站",
|
||||
"Don't know how to get the token? Please read the guide:": "不知道如何获取 Token?请阅读指南:",
|
||||
"The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "如果您正在通过 Cloudflare Tunnel 访问网站,则停止可能会导致当前连接断开。您确定要停止吗?请输入密码以确认。",
|
||||
"Other Software": "其他软件",
|
||||
"For example: nginx, Apache and Traefik.": "例如:nginx、Apache 和 Traefik。",
|
||||
"Please read": "请阅读",
|
||||
"Remove Token": "移除 Token",
|
||||
Start: "启动",
|
||||
Stop: "停止",
|
||||
"Uptime Kuma": "Uptime Kuma",
|
||||
"Add New Status Page": "添加新的状态页",
|
||||
Slug: "路径",
|
||||
"Accept characters:": "可接受的字符:",
|
||||
"startOrEndWithOnly": "开头和结尾必须为 {0}",
|
||||
"No consecutive dashes": "不能有连续的破折号",
|
||||
Next: "下一步",
|
||||
"The slug is already taken. Please choose another slug.": "该路径已被使用。请选择其他路径。",
|
||||
"No Proxy": "无代理",
|
||||
"HTTP Basic Auth": "HTTP 基础身份验证",
|
||||
"New Status Page": "新的状态页",
|
||||
"Page Not Found": "状态页未找到",
|
||||
"Reverse Proxy": "反向代理",
|
||||
"Subject:": "颁发给:",
|
||||
"Valid To:": "有效期至:",
|
||||
"Days Remaining:": "剩余有效天数:",
|
||||
"Issuer:": "颁发者:",
|
||||
"Fingerprint:": "指纹:",
|
||||
"No status pages": "无状态页",
|
||||
"Domain Name Expiry Notification": "域名到期时通知",
|
||||
"Proxy": "代理",
|
||||
"Date Created": "创建于",
|
||||
onebotHttpAddress: "OneBot HTTP 地址",
|
||||
onebotMessageType: "OneBot 消息类型",
|
||||
onebotGroupMessage: "群聊",
|
||||
onebotPrivateMessage: "私聊",
|
||||
onebotUserOrGroupId: "群组/用户ID",
|
||||
onebotSafetyTips: "出于安全原因,请务必设置AccessToken",
|
||||
};
|
||||
|
@@ -200,4 +200,182 @@ export default {
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
deleteStatusPageMsg: "是否確定刪除這個 Status Page?",
|
||||
"Push URL": "推送網址",
|
||||
needPushEvery: "您應每 {0} 秒呼叫此網址。",
|
||||
pushOptionalParams: "選填參數:{0}",
|
||||
defaultNotificationName: "我的 {notification} 通知 ({number})",
|
||||
here: "此處",
|
||||
Required: "必填",
|
||||
"Bot Token": "機器人權杖",
|
||||
wayToGetTelegramToken: "您可以從 {0} 取得 Token。",
|
||||
"Chat ID": "聊天 ID",
|
||||
supportTelegramChatID: "支援 對話/群組/頻道的聊天 ID",
|
||||
wayToGetTelegramChatID: "傳送訊息給機器人,並前往以下網址以取得您的 chat ID:",
|
||||
"YOUR BOT TOKEN HERE": "在此填入您的機器人權杖",
|
||||
chatIDNotFound: "找不到 Chat ID;請先傳送訊息給機器人",
|
||||
"Post URL": "Post 網址",
|
||||
"Content Type": "Content Type",
|
||||
webhookJsonDesc: "{0} 適合任何現代的 HTTP 伺服器,如 Express.js",
|
||||
webhookFormDataDesc: "{multipart} 適合 PHP。 JSON 必須先經由 {decodeFunction} 剖析。",
|
||||
secureOptionNone: "無 / STARTTLS (25, 587)",
|
||||
secureOptionTLS: "TLS (465)",
|
||||
"Ignore TLS Error": "忽略 TLS 錯誤",
|
||||
"From Email": "寄件人",
|
||||
emailCustomSubject: "自訂主旨",
|
||||
"To Email": "收件人",
|
||||
smtpCC: "CC",
|
||||
smtpBCC: "BCC",
|
||||
"Discord Webhook URL": "Discord Webhook 網址",
|
||||
wayToGetDiscordURL: "您可以前往伺服器設定 -> 整合 -> Webhook -> 新 Webhook 以取得",
|
||||
"Bot Display Name": "機器人顯示名稱",
|
||||
"Prefix Custom Message": "前綴自訂訊息",
|
||||
"Webhook URL": "Webhook 網址",
|
||||
wayToGetTeamsURL: "您可以前往此頁面以了解如何建立 Webhook 網址 {0}。",
|
||||
Number: "號碼",
|
||||
Recipients: "收件人",
|
||||
needSignalAPI: "您需要有 REST API 的 Signal 客戶端。",
|
||||
wayToCheckSignalURL: "您可以前往下列網址以了解如何設定:",
|
||||
signalImportant: "注意: 不得混合收件人的群組和號碼!",
|
||||
"Application Token": "應用程式權杖",
|
||||
"Server URL": "伺服器網址",
|
||||
Priority: "優先度",
|
||||
"Icon Emoji": "Emoji 圖示",
|
||||
"Channel Name": "頻道名稱",
|
||||
"Uptime Kuma URL": "Uptime Kuma 網址",
|
||||
aboutWebhooks: "更多關於 Webhook 的資訊: {0}",
|
||||
aboutChannelName: "如果您不想使用 Webhook 頻道,請在 {0} 頻道名稱欄位填入您想使用的頻道。例如: #其他頻道",
|
||||
aboutKumaURL: "如果您未填入 Uptime Kuma 網址。將預設使用專案 Github 頁面。",
|
||||
emojiCheatSheet: "Emoji 一覽表: {0}",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
GoogleChat: "Google Chat (僅限 Google Workspace)",
|
||||
"User Key": "使用者金鑰",
|
||||
Device: "裝置",
|
||||
"Message Title": "訊息標題",
|
||||
"Notification Sound": "通知音效",
|
||||
"More info on:": "更多資訊: {0}",
|
||||
pushoverDesc1: "緊急優先度 (2) 的重試間隔為 30 秒並且會在 1 小時後過期。",
|
||||
pushoverDesc2: "如果您想要傳送通知到不同裝置,請填寫裝置欄位。",
|
||||
"SMS Type": "簡訊類型",
|
||||
octopushTypePremium: "Premium (快速 - 建議用於警報)",
|
||||
octopushTypeLowCost: "Low Cost (緩慢 - 有時會被營運商阻擋)",
|
||||
checkPrice: "查看 {0} 價格:",
|
||||
apiCredentials: "API 認證",
|
||||
octopushLegacyHint: "您使用的是舊版的 Octopush (2011-2020) 還是新版?",
|
||||
"Check octopush prices": "查看 octopush 價格 {0}。",
|
||||
octopushPhoneNumber: "電話號碼 (intl 格式,例如:+33612345678) ",
|
||||
octopushSMSSender: "簡訊寄件人名稱:3-11位英數字元及空白 (a-zA-Z0-9)",
|
||||
"LunaSea Device ID": "LunaSea 裝置 ID",
|
||||
"Apprise URL": "Apprise 網址",
|
||||
"Example:": "範例:{0}",
|
||||
"Read more:": "深入瞭解:{0}",
|
||||
"Status:": "狀態:{0}",
|
||||
"Read more": "深入瞭解",
|
||||
appriseInstalled: "已安裝 Apprise。",
|
||||
appriseNotInstalled: "尚未安裝 Apprise。{0}",
|
||||
"Access Token": "存取權杖",
|
||||
"Channel access token": "頻道存取權杖",
|
||||
"Line Developers Console": "Line 開發者控制台",
|
||||
lineDevConsoleTo: "Line 開發者控制台 - {0}",
|
||||
"Basic Settings": "基本設定",
|
||||
"User ID": "使用者 ID",
|
||||
"Messaging API": "Messaging API",
|
||||
wayToGetLineChannelToken: "首先,前往 {0},建立 provider 和 channel (Messaging API)。接著您就可以從上面提到的選單項目中取得頻道存取權杖及使用者 ID。",
|
||||
"Icon URL": "圖示網址",
|
||||
aboutIconURL: "您可以在 \"圖示網址\" 中提供圖片網址以覆蓋預設個人檔案圖片。若已設定 Emoji 圖示,將忽略此設定。",
|
||||
aboutMattermostChannelName: "您可以在 \"頻道名稱\" 欄位中填寫頻道名稱以覆蓋 Webhook 的預設頻道。必須在 Mattermost 的 Webhook 設定中啟用。例如:#其他頻道",
|
||||
matrix: "Matrix",
|
||||
promosmsTypeEco: "SMS ECO - 便宜,但是很慢且經常過載。僅限位於波蘭的收件人。",
|
||||
promosmsTypeFlash: "SMS FLASH - 訊息會自動在收件人的裝置上顯示。僅限位於波蘭的收件人。",
|
||||
promosmsTypeFull: "SMS FULL - 高級版,您可以使用您的寄件人名稱 (必須先註冊名稱。對於警報來說十分可靠。",
|
||||
promosmsTypeSpeed: "SMS SPEED - 系統中的最高優先度。快速、可靠,但昂貴 (約 SMS FULL 的兩倍價格)。",
|
||||
promosmsPhoneNumber: "電話號碼 (若收件人位於波蘭則無需輸入區域代碼)",
|
||||
promosmsSMSSender: "簡訊寄件人名稱:預先註冊的名稱或以下的預設名稱:InfoSMS、SMS Info、MaxSMS、INFO、SMS",
|
||||
"Feishu WebHookUrl": "飛書 WebHook 網址",
|
||||
matrixHomeserverURL: "Homeserver 網址 (開頭為 http(s)://,結尾可能帶連接埠)",
|
||||
"Internal Room Id": "Internal Room ID",
|
||||
matrixDesc1: "您可以在 Matrix 客戶端的房間設定中的進階選項找到 internal room ID。應該看起來像 !QMdRCpUIfLwsfjxye6:home.server。",
|
||||
matrixDesc2: "使用您自己的 Matrix 使用者存取權杖將賦予存取您的帳號和您加入的房間的完整權限。建議建立新使用者,並邀請至您想要接收通知的房間中。您可以執行 {0} 以取得存取權杖",
|
||||
Method: "方法",
|
||||
Body: "主體",
|
||||
Headers: "標頭",
|
||||
PushUrl: "Push URL",
|
||||
HeadersInvalidFormat: "要求標頭不是有效的 JSON:",
|
||||
BodyInvalidFormat: "請求主體不是有效的 JSON:",
|
||||
"Monitor History": "監測器歷史紀錄",
|
||||
clearDataOlderThan: "保留 {0} 天內的監測器歷史紀錄。",
|
||||
PasswordsDoNotMatch: "密碼不相符。",
|
||||
records: "記錄",
|
||||
"One record": "一項記錄",
|
||||
"Showing {from} to {to} of {count} records": "正在顯示 {count} 項記錄中的 {from} 至 {to} 項",
|
||||
steamApiKeyDescription: "若要監測 Steam 遊戲伺服器,您將需要 Steam Web-API 金鑰。您可以在此註冊您的 API 金鑰:",
|
||||
"Current User": "目前使用者",
|
||||
recent: "最近",
|
||||
Done: "完成",
|
||||
Info: "資訊",
|
||||
Security: "安全性",
|
||||
"Steam API Key": "Steam API 金鑰",
|
||||
"Shrink Database": "壓縮資料庫",
|
||||
"Pick a RR-Type...": "選擇資源記錄類型...",
|
||||
"Pick Accepted Status Codes...": "選擇可接受的狀態碼...",
|
||||
Default: "預設",
|
||||
"HTTP Options": "HTTP 選項",
|
||||
"Create Incident": "建立事件",
|
||||
Title: "標題",
|
||||
Content: "內容",
|
||||
Style: "樣式",
|
||||
info: "資訊",
|
||||
warning: "警告",
|
||||
danger: "危險",
|
||||
primary: "主要",
|
||||
light: "淺色",
|
||||
dark: "暗色",
|
||||
Post: "發佈",
|
||||
"Please input title and content": "請輸入標題及內容",
|
||||
Created: "建立",
|
||||
"Last Updated": "最後更新",
|
||||
Unpin: "取消釘選",
|
||||
"Switch to Light Theme": "切換至淺色佈景主題",
|
||||
"Switch to Dark Theme": "切換至深色佈景主題",
|
||||
"Show Tags": "顯示標籤",
|
||||
"Hide Tags": "隱藏標籤",
|
||||
Description: "描述",
|
||||
"No monitors available.": "沒有可用的監測器。",
|
||||
"Add one": "新增一個",
|
||||
"No Monitors": "無監測器",
|
||||
"Untitled Group": "未命名群組",
|
||||
Services: "服務",
|
||||
Discard: "捨棄",
|
||||
Cancel: "取消",
|
||||
shrinkDatabaseDescription: "觸發 SQLite 的資料庫清理 (VACUUM)。如果您的資料庫是在 1.10.0 版本後建立,AUTO_VACUUM 已自動啟用,則無需此操作。",
|
||||
serwersms: "SerwerSMS.pl",
|
||||
serwersmsAPIUser: "API 使用者名稱 (包括 webapi_ 前綴)",
|
||||
serwersmsAPIPassword: "API 密碼",
|
||||
serwersmsPhoneNumber: "電話號碼",
|
||||
serwersmsSenderName: "SMS 寄件人名稱 (由客戶入口網站註冊)",
|
||||
stackfield: "Stackfield",
|
||||
smtpDkimSettings: "DKIM 設定",
|
||||
smtpDkimDesc: "請參考 Nodemailer DKIM {0} 使用方式。",
|
||||
documentation: "文件",
|
||||
smtpDkimDomain: "網域名稱",
|
||||
smtpDkimKeySelector: "DKIM 選取器",
|
||||
smtpDkimPrivateKey: "私密金鑰",
|
||||
smtpDkimHashAlgo: "雜湊演算法 (選填)",
|
||||
smtpDkimheaderFieldNames: "要簽署的郵件標頭 (選填)",
|
||||
smtpDkimskipFields: "不簽署的郵件標頭 (選填)",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API Endpoint",
|
||||
alertaEnvironment: "環境",
|
||||
alertaApiKey: "API 金鑰",
|
||||
alertaAlertState: "警示狀態",
|
||||
alertaRecoverState: "恢復狀態",
|
||||
Proxies: "代理伺服器",
|
||||
default: "預設",
|
||||
enabled: "啟用",
|
||||
setAsDefault: "設為預設",
|
||||
deleteProxyMsg: "您確定要為所有監測器刪除此代理伺服器嗎?",
|
||||
proxyDescription: "必須將代理伺服器指派給監測器才能運作。",
|
||||
enableProxyDescription: "此代理伺服器在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用代理伺服器。",
|
||||
setAsDefaultProxyDescription: "預設情況下,新監測器將啟用此代理伺服器。您仍可分別停用各監測器的代理伺服器。",
|
||||
};
|
||||
|
@@ -239,11 +239,13 @@ export default {
|
||||
"rocket.chat": "Rocket.Chat",
|
||||
pushover: "Pushover",
|
||||
pushy: "Pushy",
|
||||
PushByTechulus: "Push by Techulus",
|
||||
octopush: "Octopush",
|
||||
promosms: "PromoSMS",
|
||||
clicksendsms: "ClickSend SMS",
|
||||
lunasea: "LunaSea",
|
||||
apprise: "Apprise (支援 50 種以上的通知服務)",
|
||||
GoogleChat: "Google Chat (僅限 Google Workspace)",
|
||||
pushbullet: "Pushbullet",
|
||||
line: "Line Messenger",
|
||||
mattermost: "Mattermost",
|
||||
@@ -352,5 +354,30 @@ export default {
|
||||
serwersmsAPIPassword: "API 密碼",
|
||||
serwersmsPhoneNumber: "電話號碼",
|
||||
serwersmsSenderName: "SMS 寄件人名稱 (由客戶入口網站註冊)",
|
||||
"stackfield": "Stackfield",
|
||||
stackfield: "Stackfield",
|
||||
smtpDkimSettings: "DKIM 設定",
|
||||
smtpDkimDesc: "請參考 Nodemailer DKIM {0} 使用方式。",
|
||||
documentation: "文件",
|
||||
smtpDkimDomain: "網域名稱",
|
||||
smtpDkimKeySelector: "DKIM 選取器",
|
||||
smtpDkimPrivateKey: "私密金鑰",
|
||||
smtpDkimHashAlgo: "雜湊演算法 (選填)",
|
||||
smtpDkimheaderFieldNames: "要簽署的郵件標頭 (選填)",
|
||||
smtpDkimskipFields: "不簽署的郵件標頭 (選填)",
|
||||
gorush: "Gorush",
|
||||
alerta: "Alerta",
|
||||
alertaApiEndpoint: "API Endpoint",
|
||||
alertaEnvironment: "環境",
|
||||
alertaApiKey: "API 金鑰",
|
||||
alertaAlertState: "警示狀態",
|
||||
alertaRecoverState: "恢復狀態",
|
||||
deleteStatusPageMsg: "您確定要刪除此狀態頁嗎?",
|
||||
Proxies: "代理伺服器",
|
||||
default: "預設",
|
||||
enabled: "啟用",
|
||||
setAsDefault: "設為預設",
|
||||
deleteProxyMsg: "您確定要為所有監測器刪除此代理伺服器嗎?",
|
||||
proxyDescription: "必須將代理伺服器指派給監測器才能運作。",
|
||||
enableProxyDescription: "此代理伺服器在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用代理伺服器。",
|
||||
setAsDefaultProxyDescription: "預設情況下,新監測器將啟用此代理伺服器。您仍可分別停用各監測器的代理伺服器。",
|
||||
};
|
||||
|
@@ -3,5 +3,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {}
|
||||
export default {};
|
||||
</script>
|
||||
|
||||
|
@@ -203,6 +203,7 @@ main {
|
||||
color: white;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { io } from "socket.io-client";
|
||||
import { useToast } from "vue-toastification";
|
||||
import jwt_decode from "jwt-decode";
|
||||
import jwtDecode from "jwt-decode";
|
||||
import Favico from "favico.js";
|
||||
const toast = useToast();
|
||||
|
||||
@@ -40,8 +40,17 @@ export default {
|
||||
notificationList: [],
|
||||
statusPageListLoaded: false,
|
||||
statusPageList: [],
|
||||
proxyList: [],
|
||||
connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...",
|
||||
showReverseProxyGuide: true,
|
||||
cloudflared: {
|
||||
cloudflareTunnelToken: "",
|
||||
installed: null,
|
||||
running: false,
|
||||
message: "",
|
||||
errorMessage: "",
|
||||
currentPassword: "",
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -80,7 +89,7 @@ export default {
|
||||
}
|
||||
|
||||
socket = io(wsHost, {
|
||||
transports: ["websocket"],
|
||||
transports: [ "websocket" ],
|
||||
});
|
||||
|
||||
socket.on("info", (info) => {
|
||||
@@ -99,7 +108,7 @@ export default {
|
||||
|
||||
socket.on("monitorList", (data) => {
|
||||
// Add Helper function
|
||||
Object.entries(data).forEach(([monitorID, monitor]) => {
|
||||
Object.entries(data).forEach(([ monitorID, monitor ]) => {
|
||||
monitor.getUrl = () => {
|
||||
try {
|
||||
return new URL(monitor.url);
|
||||
@@ -120,6 +129,16 @@ export default {
|
||||
this.statusPageList = data;
|
||||
});
|
||||
|
||||
socket.on("proxyList", (data) => {
|
||||
this.proxyList = data.map(item => {
|
||||
item.auth = !!item.auth;
|
||||
item.active = !!item.active;
|
||||
item.default = !!item.default;
|
||||
|
||||
return item;
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("heartbeat", (data) => {
|
||||
if (! (data.monitorID in this.heartbeatList)) {
|
||||
this.heartbeatList[data.monitorID] = [];
|
||||
@@ -231,6 +250,12 @@ export default {
|
||||
this.socket.firstConnect = false;
|
||||
});
|
||||
|
||||
// cloudflared
|
||||
socket.on("cloudflared_installed", (res) => this.cloudflared.installed = res);
|
||||
socket.on("cloudflared_running", (res) => this.cloudflared.running = res);
|
||||
socket.on("cloudflared_message", (res) => this.cloudflared.message = res);
|
||||
socket.on("cloudflared_errorMessage", (res) => this.cloudflared.errorMessage = res);
|
||||
socket.on("cloudflared_token", (res) => this.cloudflared.cloudflareTunnelToken = res);
|
||||
},
|
||||
|
||||
storage() {
|
||||
@@ -241,7 +266,7 @@ export default {
|
||||
const jwtToken = this.$root.storage().token;
|
||||
|
||||
if (jwtToken && jwtToken !== "autoLogin") {
|
||||
return jwt_decode(jwtToken);
|
||||
return jwtDecode(jwtToken);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
@@ -6,6 +6,7 @@ export default {
|
||||
userTheme: localStorage.theme,
|
||||
userHeartbeatBar: localStorage.heartbeatBarTheme,
|
||||
statusPageTheme: "light",
|
||||
forceStatusPageTheme: false,
|
||||
path: "",
|
||||
};
|
||||
},
|
||||
@@ -27,6 +28,10 @@ export default {
|
||||
|
||||
computed: {
|
||||
theme() {
|
||||
// As entry can be status page now, set forceStatusPageTheme to true to use status page theme
|
||||
if (this.forceStatusPageTheme) {
|
||||
return this.statusPageTheme;
|
||||
}
|
||||
|
||||
// Entry no need dark
|
||||
if (this.path === "") {
|
||||
|
@@ -21,7 +21,9 @@
|
||||
<div class="form-text">
|
||||
<ul>
|
||||
<li>{{ $t("Accept characters:") }} <mark>a-z</mark> <mark>0-9</mark> <mark>-</mark></li>
|
||||
<li>{{ $t("Start or end with") }} <mark>a-z</mark> <mark>0-9</mark> only</li>
|
||||
<i18n-t tag="li" keypath="startOrEndWithOnly">
|
||||
<mark>a-z</mark> <mark>0-9</mark>
|
||||
</i18n-t>
|
||||
<li>{{ $t("No consecutive dashes") }} <mark>--</mark></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -25,9 +25,9 @@ export default {
|
||||
MonitorList,
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
return {};
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -118,6 +118,7 @@ export default {
|
||||
return 0;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
this.heartBeatList = result;
|
||||
|
||||
return result;
|
||||
|
@@ -32,6 +32,9 @@
|
||||
<option value="steam">
|
||||
Steam Game Server
|
||||
</option>
|
||||
<option value="mqtt">
|
||||
MQTT
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -67,15 +70,15 @@
|
||||
</div>
|
||||
|
||||
<!-- Hostname -->
|
||||
<!-- TCP Port / Ping / DNS / Steam only -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam'" class="my-3">
|
||||
<!-- TCP Port / Ping / DNS / Steam / MQTT only -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'mqtt'" class="my-3">
|
||||
<label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
|
||||
<input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${ipRegexPattern}|${hostnameRegexPattern}`" required>
|
||||
</div>
|
||||
|
||||
<!-- Port -->
|
||||
<!-- For TCP Port / Steam Type -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'steam'" class="my-3">
|
||||
<!-- For TCP Port / Steam / MQTT Type -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'mqtt'" class="my-3">
|
||||
<label for="port" class="form-label">{{ $t("Port") }}</label>
|
||||
<input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
|
||||
</div>
|
||||
@@ -115,6 +118,36 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- MQTT -->
|
||||
<!-- For MQTT Type -->
|
||||
<template v-if="monitor.type === 'mqtt'">
|
||||
<div class="my-3">
|
||||
<label for="mqttUsername" class="form-label">MQTT {{ $t("Username") }}</label>
|
||||
<input id="mqttUsername" v-model="monitor.mqttUsername" type="text" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="mqttPassword" class="form-label">MQTT {{ $t("Password") }}</label>
|
||||
<input id="mqttPassword" v-model="monitor.mqttPassword" type="password" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="mqttTopic" class="form-label">MQTT {{ $t("Topic") }}</label>
|
||||
<input id="mqttTopic" v-model="monitor.mqttTopic" type="text" class="form-control" required>
|
||||
<div class="form-text">
|
||||
{{ $t("topicExplanation") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="mqttSuccessMessage" class="form-label">MQTT {{ $t("successMessage") }}</label>
|
||||
<input id="mqttSuccessMessage" v-model="monitor.mqttSuccessMessage" type="text" class="form-control">
|
||||
<div class="form-text">
|
||||
{{ $t("successMessageExplanation") }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Interval -->
|
||||
<div class="my-3">
|
||||
<label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [ monitor.interval ]) }})</label>
|
||||
@@ -139,6 +172,15 @@
|
||||
|
||||
<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">
|
||||
<input id="expiry-notification" v-model="monitor.expiryNotification" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="expiry-notification">
|
||||
{{ $t("Domain Name Expiry Notification") }}
|
||||
</label>
|
||||
<div class="form-text">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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="">
|
||||
<label class="form-check-label" for="ignore-tls">
|
||||
@@ -222,6 +264,34 @@
|
||||
{{ $t("Setup Notification") }}
|
||||
</button>
|
||||
|
||||
<!-- Proxies -->
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword'">
|
||||
<h2 class="mt-5 mb-2">{{ $t("Proxy") }}</h2>
|
||||
<p v-if="$root.proxyList.length === 0">
|
||||
{{ $t("Not available, please setup.") }}
|
||||
</p>
|
||||
|
||||
<div v-if="$root.proxyList.length > 0" class="form-check my-3">
|
||||
<input id="proxy-disable" v-model="monitor.proxyId" :value="null" name="proxy" class="form-check-input" type="radio">
|
||||
<label class="form-check-label" for="proxy-disable">{{ $t("No Proxy") }}</label>
|
||||
</div>
|
||||
|
||||
<div v-for="proxy in $root.proxyList" :key="proxy.id" class="form-check my-3">
|
||||
<input :id="`proxy-${proxy.id}`" v-model="monitor.proxyId" :value="proxy.id" name="proxy" class="form-check-input" type="radio">
|
||||
|
||||
<label class="form-check-label" :for="`proxy-${proxy.id}`">
|
||||
{{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
|
||||
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
|
||||
</label>
|
||||
|
||||
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("default") }}</span>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary me-2" type="button" @click="$refs.proxyDialog.show()">
|
||||
{{ $t("Setup Proxy") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- HTTP Options -->
|
||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' ">
|
||||
<h2 class="mt-5 mb-2">{{ $t("HTTP Options") }}</h2>
|
||||
@@ -285,12 +355,14 @@
|
||||
</form>
|
||||
|
||||
<NotificationDialog ref="notificationDialog" @added="addedNotification" />
|
||||
<ProxyDialog ref="proxyDialog" @added="addedProxy" />
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NotificationDialog from "../components/NotificationDialog.vue";
|
||||
import ProxyDialog from "../components/ProxyDialog.vue";
|
||||
import TagsManager from "../components/TagsManager.vue";
|
||||
import CopyableInput from "../components/CopyableInput.vue";
|
||||
|
||||
@@ -302,6 +374,7 @@ const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ProxyDialog,
|
||||
CopyableInput,
|
||||
NotificationDialog,
|
||||
TagsManager,
|
||||
@@ -353,21 +426,32 @@ export default {
|
||||
},
|
||||
|
||||
bodyPlaceholder() {
|
||||
return this.$t("Example:", [`
|
||||
return this.$t("Example:", [ `
|
||||
{
|
||||
"key": "value"
|
||||
}`]);
|
||||
}` ]);
|
||||
},
|
||||
|
||||
headersPlaceholder() {
|
||||
return this.$t("Example:", [`
|
||||
return this.$t("Example:", [ `
|
||||
{
|
||||
"HeaderName": "HeaderValue"
|
||||
}`]);
|
||||
}` ]);
|
||||
}
|
||||
|
||||
},
|
||||
watch: {
|
||||
"$root.proxyList"() {
|
||||
if (this.isAdd) {
|
||||
if (this.$root.proxyList && !this.monitor.proxyId) {
|
||||
const proxy = this.$root.proxyList.find(proxy => proxy.default);
|
||||
|
||||
if (proxy) {
|
||||
this.monitor.proxyId = proxy.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"$route.fullPath"() {
|
||||
this.init();
|
||||
@@ -435,12 +519,26 @@ export default {
|
||||
notificationIDList: {},
|
||||
ignoreTls: false,
|
||||
upsideDown: false,
|
||||
expiryNotification: false,
|
||||
maxredirects: 10,
|
||||
accepted_statuscodes: ["200-299"],
|
||||
accepted_statuscodes: [ "200-299" ],
|
||||
dns_resolve_type: "A",
|
||||
dns_resolve_server: "1.1.1.1",
|
||||
proxyId: null,
|
||||
mqttUsername: "",
|
||||
mqttPassword: "",
|
||||
mqttTopic: "",
|
||||
mqttSuccessMessage: "",
|
||||
};
|
||||
|
||||
if (this.$root.proxyList && !this.monitor.proxyId) {
|
||||
const proxy = this.$root.proxyList.find(proxy => proxy.default);
|
||||
|
||||
if (proxy) {
|
||||
this.monitor.proxyId = proxy.id;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.$root.notificationList.length; i++) {
|
||||
if (this.$root.notificationList[i].isDefault == true) {
|
||||
this.monitor.notificationIDList[this.$root.notificationList[i].id] = true;
|
||||
@@ -532,6 +630,12 @@ export default {
|
||||
addedNotification(id) {
|
||||
this.monitor.notificationIDList[id] = true;
|
||||
},
|
||||
|
||||
// Added a Proxy Event
|
||||
// Enable it if the proxy is added in EditMonitor.vue
|
||||
addedProxy(id) {
|
||||
this.monitor.proxyId = id;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@@ -1,19 +1,45 @@
|
||||
<template>
|
||||
<div></div>
|
||||
<div>
|
||||
<StatusPage v-if="statusPageSlug" :override-slug="statusPageSlug" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import StatusPage from "./StatusPage.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
StatusPage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statusPageSlug: null,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
let entryPage = (await axios.get("/api/entry-page")).data;
|
||||
|
||||
if (entryPage === "statusPage") {
|
||||
this.$router.push("/status");
|
||||
// There are only 2 cases that could come in here.
|
||||
// 1. Matched status Page domain name
|
||||
// 2. Vue Frontend Dev
|
||||
let res = (await axios.get("/api/entry-page")).data;
|
||||
|
||||
if (res.type === "statusPageMatchedDomain") {
|
||||
this.statusPageSlug = res.statusPageSlug;
|
||||
this.$root.forceStatusPageTheme = true;
|
||||
|
||||
} else if (res.type === "entryPage") { // Dev only. For production, the logic is in the server side
|
||||
const entryPage = res.entryPage;
|
||||
|
||||
if (entryPage === "statusPage") {
|
||||
this.$router.push("/status");
|
||||
} else {
|
||||
this.$router.push("/dashboard");
|
||||
}
|
||||
} else {
|
||||
this.$router.push("/dashboard");
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
@@ -11,6 +11,6 @@ export default {
|
||||
components: {
|
||||
MonitorList,
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<div class="shadow-box">
|
||||
<template v-if="$root.statusPageListLoaded">
|
||||
<span v-if="Object.keys($root.statusPageList).length === 0" class="d-flex align-items-center justify-content-center my-3">
|
||||
No status pages
|
||||
{{ $t("No status pages") }}
|
||||
</span>
|
||||
|
||||
<!-- use <a> instead of <router-link>, because the heartbeat won't load. -->
|
||||
@@ -92,7 +92,6 @@ export default {
|
||||
}
|
||||
|
||||
.info {
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
|
@@ -75,12 +75,18 @@ export default {
|
||||
notifications: {
|
||||
title: this.$t("Notifications"),
|
||||
},
|
||||
"reverse-proxy": {
|
||||
title: this.$t("Reverse Proxy"),
|
||||
},
|
||||
"monitor-history": {
|
||||
title: this.$t("Monitor History"),
|
||||
},
|
||||
security: {
|
||||
title: this.$t("Security"),
|
||||
},
|
||||
proxies: {
|
||||
title: this.$t("Proxies"),
|
||||
},
|
||||
backup: {
|
||||
title: this.$t("Backup"),
|
||||
},
|
||||
@@ -115,6 +121,10 @@ export default {
|
||||
this.$root.getSocket().emit("getSettings", (res) => {
|
||||
this.settings = res.data;
|
||||
|
||||
if (this.settings.checkUpdate === undefined) {
|
||||
this.settings.checkUpdate = true;
|
||||
}
|
||||
|
||||
if (this.settings.searchEngineIndex === undefined) {
|
||||
this.settings.searchEngineIndex = false;
|
||||
}
|
||||
@@ -131,10 +141,18 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
saveSettings() {
|
||||
this.$root.getSocket().emit("setSettings", this.settings, (res) => {
|
||||
/**
|
||||
* Save Settings
|
||||
* @param currentPassword (Optional) Only need for disableAuth to true
|
||||
*/
|
||||
saveSettings(callback, currentPassword) {
|
||||
this.$root.getSocket().emit("setSettings", this.settings, currentPassword, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
this.loadSettings();
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
@@ -2,49 +2,80 @@
|
||||
<div v-if="loadedTheme" class="container mt-3">
|
||||
<!-- Sidebar for edit mode -->
|
||||
<div v-if="enableEditMode" class="sidebar">
|
||||
<div class="my-3">
|
||||
<label for="slug" class="form-label">{{ $t("Slug") }}</label>
|
||||
<div class="input-group">
|
||||
<span id="basic-addon3" class="input-group-text">/status/</span>
|
||||
<input id="slug" v-model="config.slug" type="text" class="form-control">
|
||||
<div class="sidebar-body">
|
||||
<div class="my-3">
|
||||
<label for="slug" class="form-label">{{ $t("Slug") }}</label>
|
||||
<div class="input-group">
|
||||
<span id="basic-addon3" class="input-group-text">/status/</span>
|
||||
<input id="slug" v-model="config.slug" type="text" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="title" class="form-label">{{ $t("Title") }}</label>
|
||||
<input id="title" v-model="config.title" type="text" class="form-control">
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<label for="title" class="form-label">{{ $t("Title") }}</label>
|
||||
<input id="title" v-model="config.title" type="text" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="description" class="form-label">{{ $t("Description") }}</label>
|
||||
<textarea id="description" v-model="config.description" class="form-control"></textarea>
|
||||
</div>
|
||||
<!-- Description -->
|
||||
<div class="my-3">
|
||||
<label for="description" class="form-label">{{ $t("Description") }}</label>
|
||||
<textarea id="description" v-model="config.description" class="form-control"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="my-3 form-check form-switch">
|
||||
<input id="switch-theme" v-model="config.theme" class="form-check-input" type="checkbox" true-value="dark" false-value="light">
|
||||
<label class="form-check-label" for="switch-theme">{{ $t("Switch to Dark Theme") }}</label>
|
||||
</div>
|
||||
<!-- Footer Text -->
|
||||
<div class="my-3">
|
||||
<label for="footer-text" class="form-label">{{ $t("Footer Text") }}</label>
|
||||
<textarea id="footer-text" v-model="config.footerText" class="form-control"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="my-3 form-check form-switch">
|
||||
<input id="showTags" v-model="config.showTags" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="showTags">{{ $t("Show Tags") }}</label>
|
||||
</div>
|
||||
<div class="my-3 form-check form-switch">
|
||||
<input id="switch-theme" v-model="config.theme" class="form-check-input" type="checkbox" true-value="dark" false-value="light">
|
||||
<label class="form-check-label" for="switch-theme">{{ $t("Switch to Dark Theme") }}</label>
|
||||
</div>
|
||||
|
||||
<div v-if="false" class="my-3">
|
||||
<label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label>
|
||||
<input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
|
||||
</div>
|
||||
<div class="my-3 form-check form-switch">
|
||||
<input id="showTags" v-model="config.showTags" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="showTags">{{ $t("Show Tags") }}</label>
|
||||
</div>
|
||||
|
||||
<div v-if="false" class="my-3">
|
||||
<label for="cname" class="form-label">Domain Names <sup>Coming Soon</sup></label>
|
||||
<textarea id="cname" v-model="config.domanNames" rows="3" disabled class="form-control" :placeholder="domainNamesPlaceholder"></textarea>
|
||||
</div>
|
||||
<!-- Show Powered By -->
|
||||
<div class="my-3 form-check form-switch">
|
||||
<input id="show-powered-by" v-model="config.showPoweredBy" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="show-powered-by">{{ $t("Show Powered By") }}</label>
|
||||
</div>
|
||||
|
||||
<div class="danger-zone">
|
||||
<button class="btn btn-danger me-2" @click="deleteDialog">
|
||||
<font-awesome-icon icon="trash" />
|
||||
{{ $t("Delete") }}
|
||||
</button>
|
||||
<div v-if="false" class="my-3">
|
||||
<label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label>
|
||||
<input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
|
||||
</div>
|
||||
|
||||
<!-- Domain Name List -->
|
||||
<div class="my-3">
|
||||
<label class="form-label">
|
||||
Domain Names
|
||||
<font-awesome-icon icon="plus-circle" class="btn-add-domain action text-primary" @click="addDomainField" />
|
||||
</label>
|
||||
|
||||
<ul class="list-group domain-name-list">
|
||||
<li v-for="(domain, index) in config.domainNameList" :key="index" class="list-group-item">
|
||||
<input v-model="config.domainNameList[index]" type="text" class="no-bg domain-input" placeholder="example.com" />
|
||||
<font-awesome-icon icon="times" class="action remove ms-2 me-3 text-danger" @click="removeDomain(index)" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<div class="my-3">
|
||||
<div class="mb-1">{{ $t("Custom CSS") }}</div>
|
||||
<prism-editor v-model="config.customCSS" class="css-editor" :highlight="highlighter" line-numbers></prism-editor>
|
||||
</div>
|
||||
|
||||
<div class="danger-zone">
|
||||
<button class="btn btn-danger me-2" @click="deleteDialog">
|
||||
<font-awesome-icon icon="trash" />
|
||||
{{ $t("Delete") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar Footer -->
|
||||
@@ -55,7 +86,7 @@
|
||||
</button>
|
||||
|
||||
<button class="btn btn-danger me-2" @click="discard">
|
||||
<font-awesome-icon icon="save" />
|
||||
<font-awesome-icon icon="undo" />
|
||||
{{ $t("Discard") }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -67,7 +98,7 @@
|
||||
<h1 class="mb-4 title-flex">
|
||||
<!-- Logo -->
|
||||
<span class="logo-wrapper" @click="showImageCropUploadMethod">
|
||||
<img :src="logoURL" alt class="logo me-2" :class="logoClass" />
|
||||
<img :src="logoURL" alt class="logo me-2" :class="logoClass" @load="statusPageLogoLoaded" />
|
||||
<font-awesome-icon v-if="enableEditMode" class="icon-upload" icon="upload" />
|
||||
</span>
|
||||
|
||||
@@ -120,7 +151,7 @@
|
||||
|
||||
<!-- Incident Date -->
|
||||
<div class="date mt-3">
|
||||
{{ $t("Created") }}: {{ $root.datetime(incident.createdDate) }} ({{ dateFromNow(incident.createdDate) }})<br />
|
||||
{{ $t("Date Created") }}: {{ $root.datetime(incident.createdDate) }} ({{ dateFromNow(incident.createdDate) }})<br />
|
||||
<span v-if="incident.lastUpdatedDate">
|
||||
{{ $t("Last Updated") }}: {{ $root.datetime(incident.lastUpdatedDate) }} ({{ dateFromNow(incident.lastUpdatedDate) }})
|
||||
</span>
|
||||
@@ -227,13 +258,24 @@
|
||||
</div>
|
||||
|
||||
<footer class="mt-5 mb-4">
|
||||
{{ $t("Powered by") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
|
||||
<div class="custom-footer-text text-start">
|
||||
<strong v-if="enableEditMode">{{ $t("Custom Footer") }}:</strong>
|
||||
</div>
|
||||
<Editable v-model="config.footerText" tag="div" :contenteditable="enableEditMode" :noNL="false" class="alert-heading p-2" />
|
||||
|
||||
<p v-if="config.showPoweredBy">
|
||||
{{ $t("Powered by") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<Confirm ref="confirmDelete" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="deleteStatusPage">
|
||||
{{ $t("deleteStatusPageMsg") }}
|
||||
</Confirm>
|
||||
|
||||
<component is="style" v-if="config.customCSS" type="text/css">
|
||||
{{ config.customCSS }}
|
||||
</component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -247,11 +289,20 @@ import dayjs from "dayjs";
|
||||
import Favico from "favico.js";
|
||||
import { getResBaseURL } from "../util-frontend";
|
||||
import Confirm from "../components/Confirm.vue";
|
||||
// import Prism Editor
|
||||
import { PrismEditor } from "vue-prism-editor";
|
||||
import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere
|
||||
|
||||
// import highlighting library (you can use any library you want just return html string)
|
||||
import { highlight, languages } from "prismjs/components/prism-core";
|
||||
import "prismjs/components/prism-css";
|
||||
import "prismjs/themes/prism-tomorrow.css"; // import syntax highlighting styles
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const leavePageMsg = "Do you really want to leave? you have unsaved changes!";
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let feedInterval;
|
||||
|
||||
const favicon = new Favico({
|
||||
@@ -259,10 +310,12 @@ const favicon = new Favico({
|
||||
});
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
PublicGroupList,
|
||||
ImageCropUpload,
|
||||
Confirm,
|
||||
PrismEditor,
|
||||
},
|
||||
|
||||
// Leave Page for vue route change
|
||||
@@ -278,6 +331,14 @@ export default {
|
||||
next();
|
||||
},
|
||||
|
||||
props: {
|
||||
overrideSlug: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
slug: null,
|
||||
@@ -294,7 +355,6 @@ export default {
|
||||
loadedData: false,
|
||||
baseURL: "",
|
||||
clickedEditButton: false,
|
||||
domainNamesPlaceholder: "domain1.com\ndomain2.com\n..."
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -389,6 +449,29 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
|
||||
/**
|
||||
* If connected to the socket and logged in, request private data of this statusPage
|
||||
* @param connected
|
||||
*/
|
||||
"$root.loggedIn"(loggedIn) {
|
||||
if (loggedIn) {
|
||||
this.$root.getSocket().emit("getStatusPage", this.slug, (res) => {
|
||||
if (res.ok) {
|
||||
this.config = res.config;
|
||||
|
||||
if (!this.config.customCSS) {
|
||||
this.config.customCSS = "body {\n" +
|
||||
" \n" +
|
||||
"}\n";
|
||||
}
|
||||
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Selected a monitor and add to the list.
|
||||
*/
|
||||
@@ -449,7 +532,7 @@ export default {
|
||||
this.baseURL = getResBaseURL();
|
||||
},
|
||||
async mounted() {
|
||||
this.slug = this.$route.params.slug;
|
||||
this.slug = this.overrideSlug || this.$route.params.slug;
|
||||
|
||||
if (!this.slug) {
|
||||
this.slug = "default";
|
||||
@@ -458,6 +541,10 @@ export default {
|
||||
axios.get("/api/status-page/" + this.slug).then((res) => {
|
||||
this.config = res.data.config;
|
||||
|
||||
if (!this.config.domainNameList) {
|
||||
this.config.domainNameList = [];
|
||||
}
|
||||
|
||||
if (this.config.icon) {
|
||||
this.imgDataUrl = this.config.icon;
|
||||
}
|
||||
@@ -480,6 +567,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
|
||||
highlighter(code) {
|
||||
return highlight(code, languages.css);
|
||||
},
|
||||
|
||||
updateHeartbeatList() {
|
||||
// If editMode, it will use the data from websocket.
|
||||
if (! this.editMode) {
|
||||
@@ -575,6 +666,10 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
addDomainField() {
|
||||
this.config.domainNameList.push("");
|
||||
},
|
||||
|
||||
discard() {
|
||||
location.href = "/status/" + this.slug;
|
||||
},
|
||||
@@ -592,6 +687,11 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
statusPageLogoLoaded(eventPayload) {
|
||||
// Remark: may not work in dev, due to cros
|
||||
favicon.image(eventPayload.target);
|
||||
},
|
||||
|
||||
createIncident() {
|
||||
this.enableEditIncidentMode = true;
|
||||
|
||||
@@ -652,6 +752,10 @@ export default {
|
||||
return dayjs.utc(date).fromNow();
|
||||
},
|
||||
|
||||
removeDomain(index) {
|
||||
this.config.domainNameList.splice(index, 1);
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -700,9 +804,7 @@ h1 {
|
||||
top: 0;
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
padding: 15px 15px 68px 15px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
border-right: 1px solid #ededed;
|
||||
|
||||
.danger-zone {
|
||||
@@ -710,13 +812,25 @@ h1 {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.sidebar-body {
|
||||
padding: 0 10px 10px 10px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: calc(100% - 70px);
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 15px;
|
||||
position: absolute;
|
||||
border-top: 1px solid #ededed;
|
||||
border-right: 1px solid #ededed;
|
||||
padding: 10px;
|
||||
width: 300px;
|
||||
height: 70px;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,7 +887,7 @@ footer {
|
||||
|
||||
.incident {
|
||||
.content {
|
||||
&[contenteditable=true] {
|
||||
&[contenteditable="true"] {
|
||||
min-height: 60px;
|
||||
}
|
||||
}
|
||||
@@ -803,9 +917,45 @@ footer {
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
border-right-color: $dark-border-color;
|
||||
border-top-color: $dark-border-color;
|
||||
background-color: $dark-header-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.domain-name-list {
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 0 10px 10px;
|
||||
|
||||
.domain-input {
|
||||
flex-grow: 1;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: $dark-font-color;
|
||||
outline: none;
|
||||
|
||||
&::placeholder {
|
||||
color: #1d2634;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* required class */
|
||||
.css-editor {
|
||||
/* we dont use `language-` classes anymore so thats why we need to add background and text color manually */
|
||||
|
||||
border-radius: 1rem;
|
||||
padding: 10px 5px;
|
||||
border: 1px solid #ced4da;
|
||||
|
||||
.dark & {
|
||||
background: $dark-bg;
|
||||
border: 1px solid $dark-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -14,8 +14,10 @@ import Entry from "./pages/Entry.vue";
|
||||
import Appearance from "./components/settings/Appearance.vue";
|
||||
import General from "./components/settings/General.vue";
|
||||
import Notifications from "./components/settings/Notifications.vue";
|
||||
import ReverseProxy from "./components/settings/ReverseProxy.vue";
|
||||
import MonitorHistory from "./components/settings/MonitorHistory.vue";
|
||||
import Security from "./components/settings/Security.vue";
|
||||
import Proxies from "./components/settings/Proxies.vue";
|
||||
import Backup from "./components/settings/Backup.vue";
|
||||
import About from "./components/settings/About.vue";
|
||||
import ManageStatusPage from "./pages/ManageStatusPage.vue";
|
||||
@@ -83,6 +85,10 @@ const routes = [
|
||||
path: "notifications",
|
||||
component: Notifications,
|
||||
},
|
||||
{
|
||||
path: "reverse-proxy",
|
||||
component: ReverseProxy,
|
||||
},
|
||||
{
|
||||
path: "monitor-history",
|
||||
component: MonitorHistory,
|
||||
@@ -91,6 +97,10 @@ const routes = [
|
||||
path: "security",
|
||||
component: Security,
|
||||
},
|
||||
{
|
||||
path: "proxies",
|
||||
component: Proxies,
|
||||
},
|
||||
{
|
||||
path: "backup",
|
||||
component: Backup,
|
||||
|
@@ -7,6 +7,12 @@ import { localeDirection, currentLocale } from "./i18n";
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
/**
|
||||
* Returns the offset from UTC in hours for the current locale.
|
||||
* @returns {number} The offset from UTC in hours.
|
||||
*
|
||||
* Generated by Trelent
|
||||
*/
|
||||
function getTimezoneOffset(timeZone) {
|
||||
const now = new Date();
|
||||
const tzString = now.toLocaleString("en-US", {
|
||||
@@ -18,6 +24,13 @@ function getTimezoneOffset(timeZone) {
|
||||
return -offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of timezones sorted by their offset from UTC.
|
||||
* @param {Array} timezones - An array of timezone objects.
|
||||
* @returns {Array} A list of the given timezones sorted by their offset from UTC.
|
||||
*
|
||||
* Generated by Trelent
|
||||
*/
|
||||
export function timezoneList() {
|
||||
let result = [];
|
||||
|
||||
|
88
src/util.js
88
src/util.js
@@ -7,7 +7,7 @@
|
||||
// Backend uses the compiled file util.js
|
||||
// Frontend uses util.ts
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0;
|
||||
exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0;
|
||||
const _dayjs = require("dayjs");
|
||||
const dayjs = _dayjs;
|
||||
exports.isDev = process.env.NODE_ENV === "development";
|
||||
@@ -44,12 +44,92 @@ function ucfirst(str) {
|
||||
return firstLetter.toUpperCase() + str.substr(1);
|
||||
}
|
||||
exports.ucfirst = ucfirst;
|
||||
/**
|
||||
* @deprecated Use log.debug
|
||||
* @since https://github.com/louislam/uptime-kuma/pull/910
|
||||
* @param msg
|
||||
*/
|
||||
function debug(msg) {
|
||||
if (exports.isDev) {
|
||||
console.log(msg);
|
||||
}
|
||||
exports.log.log("", msg, "debug");
|
||||
}
|
||||
exports.debug = debug;
|
||||
class Logger {
|
||||
constructor() {
|
||||
/**
|
||||
* UPTIME_KUMA_HIDE_LOG=debug_monitor,info_monitor
|
||||
*
|
||||
* Example:
|
||||
* [
|
||||
* "debug_monitor", // Hide all logs that level is debug and the module is monitor
|
||||
* "info_monitor",
|
||||
* ]
|
||||
*/
|
||||
this.hideLog = {
|
||||
info: [],
|
||||
warn: [],
|
||||
error: [],
|
||||
debug: [],
|
||||
};
|
||||
if (typeof process !== "undefined" && process.env.UPTIME_KUMA_HIDE_LOG) {
|
||||
let list = process.env.UPTIME_KUMA_HIDE_LOG.split(",").map(v => v.toLowerCase());
|
||||
for (let pair of list) {
|
||||
// split first "_" only
|
||||
let values = pair.split(/_(.*)/s);
|
||||
if (values.length >= 2) {
|
||||
this.hideLog[values[0]].push(values[1]);
|
||||
}
|
||||
}
|
||||
this.debug("server", "UPTIME_KUMA_HIDE_LOG is set");
|
||||
this.debug("server", this.hideLog);
|
||||
}
|
||||
}
|
||||
log(module, msg, level) {
|
||||
if (this.hideLog[level] && this.hideLog[level].includes(module)) {
|
||||
return;
|
||||
}
|
||||
module = module.toUpperCase();
|
||||
level = level.toUpperCase();
|
||||
const now = new Date().toISOString();
|
||||
const formattedMessage = (typeof msg === "string") ? `${now} [${module}] ${level}: ${msg}` : msg;
|
||||
if (level === "INFO") {
|
||||
console.info(formattedMessage);
|
||||
}
|
||||
else if (level === "WARN") {
|
||||
console.warn(formattedMessage);
|
||||
}
|
||||
else if (level === "ERROR") {
|
||||
console.error(formattedMessage);
|
||||
}
|
||||
else if (level === "DEBUG") {
|
||||
if (exports.isDev) {
|
||||
console.debug(formattedMessage);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log(formattedMessage);
|
||||
}
|
||||
}
|
||||
info(module, msg) {
|
||||
this.log(module, msg, "info");
|
||||
}
|
||||
warn(module, msg) {
|
||||
this.log(module, msg, "warn");
|
||||
}
|
||||
error(module, msg) {
|
||||
this.log(module, msg, "error");
|
||||
}
|
||||
debug(module, msg) {
|
||||
this.log(module, msg, "debug");
|
||||
}
|
||||
exception(module, exception, msg) {
|
||||
let finalMessage = exception;
|
||||
if (msg) {
|
||||
finalMessage = `${msg}: ${exception}`;
|
||||
}
|
||||
this.log(module, finalMessage, "error");
|
||||
}
|
||||
}
|
||||
exports.log = new Logger();
|
||||
function polyfill() {
|
||||
/**
|
||||
* String.prototype.replaceAll() polyfill
|
||||
|
97
src/util.ts
97
src/util.ts
@@ -49,12 +49,105 @@ export function ucfirst(str: string) {
|
||||
return firstLetter.toUpperCase() + str.substr(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use log.debug
|
||||
* @since https://github.com/louislam/uptime-kuma/pull/910
|
||||
* @param msg
|
||||
*/
|
||||
export function debug(msg: any) {
|
||||
if (isDev) {
|
||||
console.log(msg);
|
||||
log.log("", msg, "debug");
|
||||
}
|
||||
|
||||
class Logger {
|
||||
|
||||
/**
|
||||
* UPTIME_KUMA_HIDE_LOG=debug_monitor,info_monitor
|
||||
*
|
||||
* Example:
|
||||
* [
|
||||
* "debug_monitor", // Hide all logs that level is debug and the module is monitor
|
||||
* "info_monitor",
|
||||
* ]
|
||||
*/
|
||||
hideLog : any = {
|
||||
info: [],
|
||||
warn: [],
|
||||
error: [],
|
||||
debug: [],
|
||||
};
|
||||
|
||||
constructor() {
|
||||
if (typeof process !== "undefined" && process.env.UPTIME_KUMA_HIDE_LOG) {
|
||||
let list = process.env.UPTIME_KUMA_HIDE_LOG.split(",").map(v => v.toLowerCase());
|
||||
|
||||
for (let pair of list) {
|
||||
// split first "_" only
|
||||
let values = pair.split(/_(.*)/s);
|
||||
|
||||
if (values.length >= 2) {
|
||||
this.hideLog[values[0]].push(values[1]);
|
||||
}
|
||||
}
|
||||
|
||||
this.debug("server", "UPTIME_KUMA_HIDE_LOG is set");
|
||||
this.debug("server", this.hideLog);
|
||||
}
|
||||
}
|
||||
|
||||
log(module: string, msg: any, level: string) {
|
||||
if (this.hideLog[level] && this.hideLog[level].includes(module)) {
|
||||
return;
|
||||
}
|
||||
|
||||
module = module.toUpperCase();
|
||||
level = level.toUpperCase();
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const formattedMessage = (typeof msg === "string") ? `${now} [${module}] ${level}: ${msg}` : msg;
|
||||
|
||||
if (level === "INFO") {
|
||||
console.info(formattedMessage);
|
||||
} else if (level === "WARN") {
|
||||
console.warn(formattedMessage);
|
||||
} else if (level === "ERROR") {
|
||||
console.error(formattedMessage);
|
||||
} else if (level === "DEBUG") {
|
||||
if (isDev) {
|
||||
console.debug(formattedMessage);
|
||||
}
|
||||
} else {
|
||||
console.log(formattedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
info(module: string, msg: any) {
|
||||
this.log(module, msg, "info");
|
||||
}
|
||||
|
||||
warn(module: string, msg: any) {
|
||||
this.log(module, msg, "warn");
|
||||
}
|
||||
|
||||
error(module: string, msg: any) {
|
||||
this.log(module, msg, "error");
|
||||
}
|
||||
|
||||
debug(module: string, msg: any) {
|
||||
this.log(module, msg, "debug");
|
||||
}
|
||||
|
||||
exception(module: string, exception: any, msg: any) {
|
||||
let finalMessage = exception
|
||||
|
||||
if (msg) {
|
||||
finalMessage = `${msg}: ${exception}`
|
||||
}
|
||||
|
||||
this.log(module, finalMessage , "error");
|
||||
}
|
||||
}
|
||||
|
||||
export const log = new Logger();
|
||||
|
||||
declare global { interface String { replaceAll(str: string, newStr: string): string; } }
|
||||
|
||||
|
Reference in New Issue
Block a user