mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-03-04 08:25:57 +00:00
Merge branch 'master' of git@github.com:lucasnz/uptime-kuma.git into ip-version
This commit is contained in:
commit
d79154ae1f
25 changed files with 48 additions and 544 deletions
211
server/server.js
211
server/server.js
|
@ -65,8 +65,6 @@ log.debug("server", "Importing http-graceful-shutdown");
|
|||
const gracefulShutdown = require("http-graceful-shutdown");
|
||||
log.debug("server", "Importing prometheus-api-metrics");
|
||||
const prometheusAPIMetrics = require("prometheus-api-metrics");
|
||||
log.debug("server", "Importing compare-versions");
|
||||
const compareVersions = require("compare-versions");
|
||||
const { passwordStrength } = require("check-password-strength");
|
||||
|
||||
log.debug("server", "Importing 2FA Modules");
|
||||
|
@ -91,9 +89,6 @@ log.debug("server", "Importing Notification");
|
|||
const { Notification } = require("./notification");
|
||||
Notification.init();
|
||||
|
||||
log.debug("server", "Importing Proxy");
|
||||
const { Proxy } = require("./proxy");
|
||||
|
||||
log.debug("server", "Importing Database");
|
||||
const Database = require("./database");
|
||||
|
||||
|
@ -1435,212 +1430,6 @@ let needSetup = false;
|
|||
}
|
||||
});
|
||||
|
||||
socket.on("uploadBackup", async (uploadedJSON, importHandle, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
let backupData = JSON.parse(uploadedJSON);
|
||||
|
||||
log.info("manage", `Importing Backup, User ID: ${socket.userID}, Version: ${backupData.version}`);
|
||||
|
||||
let notificationListData = backupData.notificationList;
|
||||
let proxyListData = backupData.proxyList;
|
||||
let monitorListData = backupData.monitorList;
|
||||
|
||||
let version17x = compareVersions.compare(backupData.version, "1.7.0", ">=");
|
||||
|
||||
// If the import option is "overwrite" it'll clear most of the tables, except "settings" and "user"
|
||||
if (importHandle === "overwrite") {
|
||||
// Stops every monitor first, so it doesn't execute any heartbeat while importing
|
||||
for (let id in server.monitorList) {
|
||||
let monitor = server.monitorList[id];
|
||||
await monitor.stop();
|
||||
}
|
||||
await R.exec("DELETE FROM heartbeat");
|
||||
await R.exec("DELETE FROM monitor_notification");
|
||||
await R.exec("DELETE FROM monitor_tls_info");
|
||||
await R.exec("DELETE FROM notification");
|
||||
await R.exec("DELETE FROM monitor_tag");
|
||||
await R.exec("DELETE FROM tag");
|
||||
await R.exec("DELETE FROM monitor");
|
||||
await R.exec("DELETE FROM proxy");
|
||||
}
|
||||
|
||||
// Only starts importing if the backup file contains at least one notification
|
||||
if (notificationListData.length >= 1) {
|
||||
// Get every existing notification name and puts them in one simple string
|
||||
let notificationNameList = await R.getAll("SELECT name FROM notification");
|
||||
let notificationNameListString = JSON.stringify(notificationNameList);
|
||||
|
||||
for (let i = 0; i < notificationListData.length; i++) {
|
||||
// Only starts importing the notification if the import option is "overwrite", "keep" or "skip" but the notification doesn't exists
|
||||
if ((importHandle === "skip" && notificationNameListString.includes(notificationListData[i].name) === false) || importHandle === "keep" || importHandle === "overwrite") {
|
||||
|
||||
let notification = JSON.parse(notificationListData[i].config);
|
||||
await Notification.save(notification, null, socket.userID);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only starts importing if the backup file contains at least one proxy
|
||||
if (proxyListData && proxyListData.length >= 1) {
|
||||
const proxies = await R.findAll("proxy");
|
||||
|
||||
// Loop over proxy list and save proxies
|
||||
for (const proxy of proxyListData) {
|
||||
const exists = proxies.find(item => item.id === proxy.id);
|
||||
|
||||
// Do not process when proxy already exists in import handle is skip and keep
|
||||
if ([ "skip", "keep" ].includes(importHandle) && !exists) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save proxy as new entry if exists update exists one
|
||||
await Proxy.save(proxy, exists ? proxy.id : undefined, proxy.userId);
|
||||
}
|
||||
}
|
||||
|
||||
// Only starts importing if the backup file contains at least one monitor
|
||||
if (monitorListData.length >= 1) {
|
||||
// Get every existing monitor name and puts them in one simple string
|
||||
let monitorNameList = await R.getAll("SELECT name FROM monitor");
|
||||
let monitorNameListString = JSON.stringify(monitorNameList);
|
||||
|
||||
for (let i = 0; i < monitorListData.length; i++) {
|
||||
// Only starts importing the monitor if the import option is "overwrite", "keep" or "skip" but the notification doesn't exists
|
||||
if ((importHandle === "skip" && monitorNameListString.includes(monitorListData[i].name) === false) || importHandle === "keep" || importHandle === "overwrite") {
|
||||
|
||||
// Define in here every new variable for monitors which where implemented after the first version of the Import/Export function (1.6.0)
|
||||
// --- Start ---
|
||||
|
||||
// Define default values
|
||||
let retryInterval = 0;
|
||||
let timeout = monitorListData[i].timeout || (monitorListData[i].interval * 0.8); // fallback to old value
|
||||
|
||||
/*
|
||||
Only replace the default value with the backup file data for the specific version, where it appears the first time
|
||||
More information about that where "let version" will be defined
|
||||
*/
|
||||
if (version17x) {
|
||||
retryInterval = monitorListData[i].retryInterval;
|
||||
}
|
||||
|
||||
// --- End ---
|
||||
|
||||
let monitor = {
|
||||
// Define the new variable from earlier here
|
||||
name: monitorListData[i].name,
|
||||
description: monitorListData[i].description,
|
||||
type: monitorListData[i].type,
|
||||
url: monitorListData[i].url,
|
||||
method: monitorListData[i].method || "GET",
|
||||
body: monitorListData[i].body,
|
||||
headers: monitorListData[i].headers,
|
||||
authMethod: monitorListData[i].authMethod,
|
||||
basic_auth_user: monitorListData[i].basic_auth_user,
|
||||
basic_auth_pass: monitorListData[i].basic_auth_pass,
|
||||
authWorkstation: monitorListData[i].authWorkstation,
|
||||
authDomain: monitorListData[i].authDomain,
|
||||
timeout,
|
||||
interval: monitorListData[i].interval,
|
||||
retryInterval: retryInterval,
|
||||
resendInterval: monitorListData[i].resendInterval || 0,
|
||||
hostname: monitorListData[i].hostname,
|
||||
maxretries: monitorListData[i].maxretries,
|
||||
port: monitorListData[i].port,
|
||||
keyword: monitorListData[i].keyword,
|
||||
invertKeyword: monitorListData[i].invertKeyword,
|
||||
ignoreTls: monitorListData[i].ignoreTls,
|
||||
upsideDown: monitorListData[i].upsideDown,
|
||||
maxredirects: monitorListData[i].maxredirects,
|
||||
accepted_statuscodes: monitorListData[i].accepted_statuscodes,
|
||||
dns_resolve_type: monitorListData[i].dns_resolve_type,
|
||||
dns_resolve_server: monitorListData[i].dns_resolve_server,
|
||||
notificationIDList: monitorListData[i].notificationIDList,
|
||||
proxy_id: monitorListData[i].proxy_id || null,
|
||||
};
|
||||
|
||||
if (monitorListData[i].pushToken) {
|
||||
monitor.pushToken = monitorListData[i].pushToken;
|
||||
}
|
||||
|
||||
let bean = R.dispense("monitor");
|
||||
|
||||
let notificationIDList = monitor.notificationIDList;
|
||||
delete monitor.notificationIDList;
|
||||
|
||||
monitor.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
|
||||
delete monitor.accepted_statuscodes;
|
||||
|
||||
bean.import(monitor);
|
||||
bean.user_id = socket.userID;
|
||||
await R.store(bean);
|
||||
|
||||
// Only for backup files with the version 1.7.0 or higher, since there was the tag feature implemented
|
||||
if (version17x) {
|
||||
// Only import if the specific monitor has tags assigned
|
||||
for (const oldTag of monitorListData[i].tags) {
|
||||
|
||||
// Check if tag already exists and get data ->
|
||||
let tag = await R.findOne("tag", " name = ?", [
|
||||
oldTag.name,
|
||||
]);
|
||||
|
||||
let tagId;
|
||||
if (!tag) {
|
||||
// -> If it doesn't exist, create new tag from backup file
|
||||
let beanTag = R.dispense("tag");
|
||||
beanTag.name = oldTag.name;
|
||||
beanTag.color = oldTag.color;
|
||||
await R.store(beanTag);
|
||||
|
||||
tagId = beanTag.id;
|
||||
} else {
|
||||
// -> If it already exist, set tagId to value from database
|
||||
tagId = tag.id;
|
||||
}
|
||||
|
||||
// Assign the new created tag to the monitor
|
||||
await R.exec("INSERT INTO monitor_tag (tag_id, monitor_id, value) VALUES (?, ?, ?)", [
|
||||
tagId,
|
||||
bean.id,
|
||||
oldTag.value,
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
await updateMonitorNotification(bean.id, notificationIDList);
|
||||
|
||||
// If monitor was active start it immediately, otherwise pause it
|
||||
if (monitorListData[i].active === 1) {
|
||||
await startMonitor(socket.userID, bean.id);
|
||||
} else {
|
||||
await pauseMonitor(socket.userID, bean.id);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
await sendNotificationList(socket);
|
||||
await server.sendMonitorList(socket);
|
||||
}
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "successBackupRestored",
|
||||
msgi18n: true,
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("clearEvents", async (monitorID, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
|
|
@ -92,11 +92,9 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { Modal } from "bootstrap";
|
||||
import { useToast } from "vue-toastification";
|
||||
import dayjs from "dayjs";
|
||||
import Datepicker from "@vuepic/vue-datepicker";
|
||||
import CopyableInput from "./CopyableInput.vue";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -158,7 +156,7 @@ export default {
|
|||
this.keymodal.show();
|
||||
this.clearForm();
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -62,8 +62,6 @@
|
|||
<script lang="ts">
|
||||
import { Modal } from "bootstrap";
|
||||
import Confirm from "./Confirm.vue";
|
||||
import { useToast } from "vue-toastification";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -120,7 +118,7 @@ export default {
|
|||
}
|
||||
|
||||
if (!found) {
|
||||
toast.error("Docker Host not found!");
|
||||
this.$root.toastError("Docker Host not found!");
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
</button>
|
||||
|
||||
<div v-if="res && !res.ok" class="alert alert-danger mt-3" role="alert">
|
||||
{{ res.msg }}
|
||||
{{ $t(res.msg) }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -21,11 +21,8 @@ import { BarController, BarElement, Chart, Filler, LinearScale, LineController,
|
|||
import "chartjs-adapter-dayjs-4";
|
||||
import dayjs from "dayjs";
|
||||
import { Line } from "vue-chartjs";
|
||||
import { useToast } from "vue-toastification";
|
||||
import { DOWN, PENDING, MAINTENANCE, log } from "../util.ts";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
Chart.register(LineController, BarController, LineElement, PointElement, TimeScale, BarElement, LinearScale, Tooltip, Filler);
|
||||
|
||||
export default {
|
||||
|
@ -231,7 +228,7 @@ export default {
|
|||
|
||||
this.$root.getMonitorBeats(this.monitorId, newPeriod, (res) => {
|
||||
if (!res.ok) {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
} else {
|
||||
this.heartbeatList = res.data;
|
||||
this.$root.storage()[`chart-period-${this.monitorId}`] = newPeriod;
|
||||
|
|
|
@ -123,9 +123,7 @@ import Confirm from "./Confirm.vue";
|
|||
import Tag from "./Tag.vue";
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import { colorOptions } from "../util-frontend";
|
||||
import { useToast } from "vue-toastification";
|
||||
import { getMonitorRelativeURL } from "../util.ts";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -320,7 +318,7 @@ export default {
|
|||
for (let addId of this.addingMonitor) {
|
||||
await this.addMonitorTagAsync(this.tag.id, addId, "").then((res) => {
|
||||
if (!res.ok) {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
editResult = false;
|
||||
}
|
||||
});
|
||||
|
@ -330,7 +328,7 @@ export default {
|
|||
this.monitors.find(monitor => monitor.id === removeId)?.tags.forEach(async (monitorTag) => {
|
||||
await this.deleteMonitorTagAsync(this.tag.id, removeId, monitorTag.value).then((res) => {
|
||||
if (!res.ok) {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
editResult = false;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -129,10 +129,8 @@
|
|||
<script>
|
||||
import { Modal } from "bootstrap";
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import { useToast } from "vue-toastification";
|
||||
import { colorOptions } from "../util-frontend";
|
||||
import Tag from "../components/Tag.vue";
|
||||
const toast = useToast();
|
||||
|
||||
/**
|
||||
* @typedef Tag
|
||||
|
@ -262,7 +260,7 @@ export default {
|
|||
if (res.ok) {
|
||||
this.existingTags = res.tags;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -399,7 +397,7 @@ export default {
|
|||
let newTagResult;
|
||||
await this.addTagAsync(newTag).then((res) => {
|
||||
if (!res.ok) {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
newTagResult = false;
|
||||
}
|
||||
newTagResult = res.tag;
|
||||
|
@ -424,7 +422,7 @@ export default {
|
|||
// Assign tag to monitor
|
||||
await this.addMonitorTagAsync(tagId, monitorId, newTag.value).then((res) => {
|
||||
if (!res.ok) {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
newMonitorTagResult = false;
|
||||
}
|
||||
newMonitorTagResult = true;
|
||||
|
@ -440,7 +438,7 @@ export default {
|
|||
let deleteMonitorTagResult;
|
||||
await this.deleteMonitorTagAsync(deleteTag.tag_id, deleteTag.monitor_id, deleteTag.value).then((res) => {
|
||||
if (!res.ok) {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
deleteMonitorTagResult = false;
|
||||
}
|
||||
deleteMonitorTagResult = true;
|
||||
|
|
|
@ -76,8 +76,6 @@
|
|||
import { Modal } from "bootstrap";
|
||||
import Confirm from "./Confirm.vue";
|
||||
import VueQrcode from "vue-qrcode";
|
||||
import { useToast } from "vue-toastification";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -138,7 +136,7 @@ export default {
|
|||
if (res.ok) {
|
||||
this.uri = res.uri;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -159,7 +157,7 @@ export default {
|
|||
this.currentPassword = "";
|
||||
this.modal.hide();
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -180,7 +178,7 @@ export default {
|
|||
this.currentPassword = "";
|
||||
this.modal.hide();
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -194,7 +192,7 @@ export default {
|
|||
if (res.ok) {
|
||||
this.tokenValid = res.valid;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -208,7 +206,7 @@ export default {
|
|||
if (res.ok) {
|
||||
this.twoFAStatus = res.status;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -58,8 +58,6 @@
|
|||
<script>
|
||||
import HiddenInput from "../HiddenInput.vue";
|
||||
import axios from "axios";
|
||||
import { useToast } from "vue-toastification";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -110,7 +108,7 @@ export default {
|
|||
}
|
||||
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
this.$root.toastError(error.message);
|
||||
}
|
||||
|
||||
},
|
||||
|
|
|
@ -72,8 +72,6 @@
|
|||
<script>
|
||||
import APIKeyDialog from "../../components/APIKeyDialog.vue";
|
||||
import Confirm from "../Confirm.vue";
|
||||
import { useToast } from "vue-toastification";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -109,11 +107,7 @@ export default {
|
|||
*/
|
||||
deleteKey() {
|
||||
this.$root.deleteAPIKey(this.selectedKeyID, (res) => {
|
||||
if (res.ok) {
|
||||
toast.success(res.msg);
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
this.$root.toastRes(res);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -1,232 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="my-4">
|
||||
<div class="alert alert-warning" role="alert" style="border-radius: 15px;">
|
||||
{{ $t("backupOutdatedWarning") }}<br />
|
||||
<br />
|
||||
{{ $t("backupRecommend") }}
|
||||
</div>
|
||||
|
||||
<h4 class="mt-4 mb-2">{{ $t("Export Backup") }}</h4>
|
||||
|
||||
<p>
|
||||
{{ $t("backupDescription") }} <br />
|
||||
({{ $t("backupDescription2") }}) <br />
|
||||
</p>
|
||||
|
||||
<div class="mb-2">
|
||||
<button class="btn btn-primary" @click="downloadBackup">
|
||||
{{ $t("Export") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<strong>{{ $t("backupDescription3") }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<h4 class="mt-4 mb-2">{{ $t("Import Backup") }}</h4>
|
||||
|
||||
<label class="form-label">{{ $t("Options") }}:</label>
|
||||
<br />
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="radioKeep"
|
||||
v-model="importHandle"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="radioImportHandle"
|
||||
value="keep"
|
||||
/>
|
||||
<label class="form-check-label" for="radioKeep">
|
||||
{{ $t("Keep both") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="radioSkip"
|
||||
v-model="importHandle"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="radioImportHandle"
|
||||
value="skip"
|
||||
/>
|
||||
<label class="form-check-label" for="radioSkip">
|
||||
{{ $t("Skip existing") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="radioOverwrite"
|
||||
v-model="importHandle"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="radioImportHandle"
|
||||
value="overwrite"
|
||||
/>
|
||||
<label class="form-check-label" for="radioOverwrite">
|
||||
{{ $t("Overwrite") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-text mb-2">
|
||||
{{ $t("importHandleDescription") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<input
|
||||
id="import-backend"
|
||||
type="file"
|
||||
class="form-control"
|
||||
accept="application/json"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-2 justify-content-end">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-primary"
|
||||
:disabled="processing"
|
||||
@click="confirmImport"
|
||||
>
|
||||
<div
|
||||
v-if="processing"
|
||||
class="spinner-border spinner-border-sm me-1"
|
||||
></div>
|
||||
{{ $t("Import") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="importAlert"
|
||||
class="alert alert-danger mt-3"
|
||||
style="padding: 6px 16px;"
|
||||
>
|
||||
{{ importAlert }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Confirm
|
||||
ref="confirmImport"
|
||||
btn-style="btn-danger"
|
||||
:yes-text="$t('Yes')"
|
||||
:no-text="$t('No')"
|
||||
@yes="importBackup"
|
||||
>
|
||||
{{ $t("confirmImportMsg") }}
|
||||
</Confirm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Confirm from "../../components/Confirm.vue";
|
||||
import dayjs from "dayjs";
|
||||
import { useToast } from "vue-toastification";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Confirm,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
processing: false,
|
||||
importHandle: "skip",
|
||||
importAlert: null,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Show the confimation dialog confirming the configuration
|
||||
* be imported
|
||||
* @returns {void}
|
||||
*/
|
||||
confirmImport() {
|
||||
this.$refs.confirmImport.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Download a backup of the configuration
|
||||
* @returns {void}
|
||||
*/
|
||||
downloadBackup() {
|
||||
let time = dayjs().format("YYYY_MM_DD-hh_mm_ss");
|
||||
let fileName = `Uptime_Kuma_Backup_${time}.json`;
|
||||
let monitorList = Object.values(this.$root.monitorList);
|
||||
let exportData = {
|
||||
version: this.$root.info.version,
|
||||
notificationList: this.$root.notificationList,
|
||||
monitorList: monitorList,
|
||||
};
|
||||
exportData = JSON.stringify(exportData, null, 4);
|
||||
let downloadItem = document.createElement("a");
|
||||
downloadItem.setAttribute(
|
||||
"href",
|
||||
"data:application/json;charset=utf-8," +
|
||||
encodeURIComponent(exportData)
|
||||
);
|
||||
downloadItem.setAttribute("download", fileName);
|
||||
downloadItem.click();
|
||||
},
|
||||
|
||||
/**
|
||||
* Import the specified backup file
|
||||
* @returns {string|void} Error message
|
||||
*/
|
||||
importBackup() {
|
||||
this.processing = true;
|
||||
let uploadItem = document.getElementById("import-backend").files;
|
||||
|
||||
if (uploadItem.length <= 0) {
|
||||
this.processing = false;
|
||||
return (this.importAlert = this.$t("alertNoFile"));
|
||||
}
|
||||
|
||||
if (uploadItem.item(0).type !== "application/json") {
|
||||
this.processing = false;
|
||||
return (this.importAlert = this.$t("alertWrongFileType"));
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.readAsText(uploadItem.item(0));
|
||||
|
||||
fileReader.onload = (item) => {
|
||||
this.$root.uploadBackup(
|
||||
item.target.result,
|
||||
this.importHandle,
|
||||
(res) => {
|
||||
this.processing = false;
|
||||
|
||||
if (res.ok) {
|
||||
toast.success(res.msg);
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/vars.scss";
|
||||
|
||||
.dark {
|
||||
#import-backend {
|
||||
&::file-selector-button {
|
||||
color: $primary;
|
||||
background-color: $dark-bg;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled):not([readonly])::file-selector-button {
|
||||
color: $dark-font-color2;
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -57,9 +57,6 @@
|
|||
<script>
|
||||
import Confirm from "../../components/Confirm.vue";
|
||||
import { log } from "../../util.ts";
|
||||
import { useToast } from "vue-toastification";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -118,7 +115,7 @@ export default {
|
|||
this.$root.getSocket().emit("shrinkDatabase", (res) => {
|
||||
if (res.ok) {
|
||||
this.loadDatabaseSize();
|
||||
toast.success("Done");
|
||||
this.$root.toastSuccess("Done");
|
||||
} else {
|
||||
log.debug("monitorhistory", res);
|
||||
}
|
||||
|
@ -142,7 +139,7 @@ export default {
|
|||
if (res.ok) {
|
||||
this.$router.go();
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -28,11 +28,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { useToast } from "vue-toastification";
|
||||
import TagEditDialog from "../../components/TagEditDialog.vue";
|
||||
import Tag from "../Tag.vue";
|
||||
import Confirm from "../Confirm.vue";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -86,7 +84,7 @@ export default {
|
|||
if (res.ok) {
|
||||
this.tagsList = res.tags;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -342,7 +342,7 @@ export default {
|
|||
* @returns {void}
|
||||
*/
|
||||
toastSuccess(msg) {
|
||||
toast.success(msg);
|
||||
toast.success(this.$t(msg));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -351,7 +351,7 @@ export default {
|
|||
* @returns {void}
|
||||
*/
|
||||
toastError(msg) {
|
||||
toast.error(msg);
|
||||
toast.error(this.$t(msg));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -66,7 +66,7 @@ export default {
|
|||
} else {
|
||||
|
||||
if (res.msg.includes("UNIQUE constraint")) {
|
||||
this.$root.toastError(this.$t("The slug is already taken. Please choose another slug."));
|
||||
this.$root.toastError("The slug is already taken. Please choose another slug.");
|
||||
} else {
|
||||
this.$root.toastRes(res);
|
||||
}
|
||||
|
|
|
@ -437,7 +437,7 @@ export default {
|
|||
*/
|
||||
testNotification() {
|
||||
this.$root.getSocket().emit("testNotification", this.monitor.id);
|
||||
toast.success("Test notification is requested.");
|
||||
this.$root.toastSuccess("Test notification is requested.");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -498,11 +498,9 @@ export default {
|
|||
*/
|
||||
deleteMonitor() {
|
||||
this.$root.deleteMonitor(this.monitor.id, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
if (res.ok) {
|
||||
toast.success(res.msg);
|
||||
this.$router.push("/dashboard");
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -246,14 +246,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { useToast } from "vue-toastification";
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import Datepicker from "@vuepic/vue-datepicker";
|
||||
import { timezoneList } from "../util-frontend";
|
||||
import cronstrue from "cronstrue/i18n";
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VueMultiselect,
|
||||
|
@ -457,7 +454,7 @@ export default {
|
|||
this.affectedMonitors.push(this.affectedMonitorsOptions.find(item => item.id === monitor.id));
|
||||
});
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -472,11 +469,11 @@ export default {
|
|||
|
||||
this.showOnAllPages = Object.values(res.statusPages).length === this.selectedStatusPagesOptions.length;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -490,7 +487,7 @@ export default {
|
|||
this.processing = true;
|
||||
|
||||
if (this.affectedMonitors.length === 0) {
|
||||
toast.error(this.$t("atLeastOneMonitor"));
|
||||
this.$root.toastError(this.$t("atLeastOneMonitor"));
|
||||
return this.processing = false;
|
||||
}
|
||||
|
||||
|
@ -499,14 +496,14 @@ export default {
|
|||
if (res.ok) {
|
||||
await this.addMonitorMaintenance(res.maintenanceID, async () => {
|
||||
await this.addMaintenanceStatusPage(res.maintenanceID, () => {
|
||||
toast.success(res.msg);
|
||||
this.$root.toastRes(res);
|
||||
this.processing = false;
|
||||
this.$root.getMaintenanceList();
|
||||
this.$router.push("/maintenance");
|
||||
});
|
||||
});
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastRes(res);
|
||||
this.processing = false;
|
||||
}
|
||||
|
||||
|
@ -524,7 +521,7 @@ export default {
|
|||
});
|
||||
} else {
|
||||
this.processing = false;
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -539,7 +536,7 @@ export default {
|
|||
async addMonitorMaintenance(maintenanceID, callback) {
|
||||
await this.$root.addMonitorMaintenance(maintenanceID, this.affectedMonitors, async (res) => {
|
||||
if (!res.ok) {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
} else {
|
||||
this.$root.getMonitorList();
|
||||
}
|
||||
|
@ -557,7 +554,7 @@ export default {
|
|||
async addMaintenanceStatusPage(maintenanceID, callback) {
|
||||
await this.$root.addMaintenanceStatusPage(maintenanceID, (this.showOnAllPages) ? this.selectedStatusPagesOptions : this.selectedStatusPages, async (res) => {
|
||||
if (!res.ok) {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
} else {
|
||||
this.$root.getMaintenanceList();
|
||||
}
|
||||
|
|
|
@ -865,7 +865,7 @@ import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND } from "../u
|
|||
import { hostNameRegexPattern } from "../util-frontend";
|
||||
import { sleep } from "../util";
|
||||
|
||||
const toast = useToast();
|
||||
const toast = useToast;
|
||||
|
||||
const pushTokenLength = 32;
|
||||
|
||||
|
@ -1190,7 +1190,7 @@ message HealthCheckResponse {
|
|||
if (res.ok) {
|
||||
this.gameList = res.gameList;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1351,7 +1351,7 @@ message HealthCheckResponse {
|
|||
this.monitor.timeout = ~~(this.monitor.interval * 8) / 10;
|
||||
}
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1445,7 +1445,7 @@ message HealthCheckResponse {
|
|||
createdNewParent = true;
|
||||
this.monitor.parent = res.monitorID;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
this.processing = false;
|
||||
return;
|
||||
}
|
||||
|
@ -1461,17 +1461,14 @@ message HealthCheckResponse {
|
|||
if (createdNewParent) {
|
||||
this.startParentGroupMonitor();
|
||||
}
|
||||
|
||||
toast.success(res.msg);
|
||||
this.processing = false;
|
||||
this.$root.getMonitorList();
|
||||
this.$router.push("/dashboard/" + res.monitorID);
|
||||
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.processing = false;
|
||||
}
|
||||
|
||||
this.$root.toastRes(res);
|
||||
});
|
||||
} else {
|
||||
await this.$refs.tagsManager.submit(this.monitor.id);
|
||||
|
|
|
@ -101,12 +101,8 @@ export default {
|
|||
*/
|
||||
deleteMaintenance() {
|
||||
this.$root.deleteMaintenance(this.maintenance.id, (res) => {
|
||||
if (res.ok) {
|
||||
toast.success(res.msg);
|
||||
this.$root.toastRes(res);
|
||||
this.$router.push("/maintenance");
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
|
@ -81,8 +81,6 @@ import { getResBaseURL } from "../util-frontend";
|
|||
import { getMaintenanceRelativeURL } from "../util.ts";
|
||||
import Confirm from "../components/Confirm.vue";
|
||||
import MaintenanceTime from "../components/MaintenanceTime.vue";
|
||||
import { useToast } from "vue-toastification";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -159,11 +157,9 @@ export default {
|
|||
*/
|
||||
deleteMaintenance() {
|
||||
this.$root.deleteMaintenance(this.selectedMaintenanceID, (res) => {
|
||||
this.$root.toastRes(res);
|
||||
if (res.ok) {
|
||||
toast.success(res.msg);
|
||||
this.$router.push("/maintenance");
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -113,9 +113,6 @@ export default {
|
|||
proxies: {
|
||||
title: this.$t("Proxies"),
|
||||
},
|
||||
backup: {
|
||||
title: this.$t("Backup"),
|
||||
},
|
||||
about: {
|
||||
title: this.$t("About"),
|
||||
},
|
||||
|
|
|
@ -46,9 +46,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { useToast } from "vue-toastification";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
@ -79,7 +76,7 @@ export default {
|
|||
this.processing = true;
|
||||
|
||||
if (this.password !== this.repeatPassword) {
|
||||
toast.error(this.$t("PasswordsDoNotMatch"));
|
||||
this.$root.toastError("PasswordsDoNotMatch");
|
||||
this.processing = false;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ export default {
|
|||
},
|
||||
|
||||
test() {
|
||||
toast.error("not implemented");
|
||||
this.$root.toastError("not implemented");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -614,7 +614,7 @@ export default {
|
|||
}
|
||||
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -869,7 +869,7 @@ export default {
|
|||
this.enableEditMode = false;
|
||||
location.href = "/manage-status-page";
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -959,7 +959,7 @@ export default {
|
|||
*/
|
||||
postIncident() {
|
||||
if (this.incident.title === "" || this.incident.content === "") {
|
||||
toast.error(this.$t("Please input title and content"));
|
||||
this.$root.toastError("Please input title and content");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -969,7 +969,7 @@ export default {
|
|||
this.enableEditIncidentMode = false;
|
||||
this.incident = res.incident;
|
||||
} else {
|
||||
toast.error(res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -30,7 +30,6 @@ import Tags from "./components/settings/Tags.vue";
|
|||
import MonitorHistory from "./components/settings/MonitorHistory.vue";
|
||||
const Security = () => import("./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";
|
||||
|
||||
const routes = [
|
||||
|
@ -126,10 +125,6 @@ const routes = [
|
|||
path: "proxies",
|
||||
component: Proxies,
|
||||
},
|
||||
{
|
||||
path: "backup",
|
||||
component: Backup,
|
||||
},
|
||||
{
|
||||
path: "about",
|
||||
component: About,
|
||||
|
|
Loading…
Add table
Reference in a new issue