# Conflicts: # package.jsonpull/1780/head
commit
197d44981f
@ -0,0 +1,18 @@
|
|||||||
|
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
CREATE TABLE docker_host (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
docker_daemon VARCHAR(255),
|
||||||
|
docker_type VARCHAR(255),
|
||||||
|
name VARCHAR(255)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD docker_host INTEGER REFERENCES docker_host(id);
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD docker_container VARCHAR(255);
|
||||||
|
|
||||||
|
COMMIT;
|
@ -0,0 +1,18 @@
|
|||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD radius_username VARCHAR(255);
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD radius_password VARCHAR(255);
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD radius_calling_station_id VARCHAR(50);
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD radius_called_station_id VARCHAR(50);
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD radius_secret VARCHAR(255);
|
||||||
|
|
||||||
|
COMMIT
|
@ -0,0 +1,10 @@
|
|||||||
|
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD resend_interval INTEGER default 0 not null;
|
||||||
|
|
||||||
|
ALTER TABLE heartbeat
|
||||||
|
ADD down_count INTEGER default 0 not null;
|
||||||
|
|
||||||
|
COMMIT;
|
@ -0,0 +1,33 @@
|
|||||||
|
const childProcess = require("child_process");
|
||||||
|
|
||||||
|
if (!process.env.UPTIME_KUMA_GH_REPO) {
|
||||||
|
console.error("Please set a repo to the environment variable 'UPTIME_KUMA_GH_REPO' (e.g. mhkarimi1383:goalert-notification)");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputArray = process.env.UPTIME_KUMA_GH_REPO.split(":");
|
||||||
|
|
||||||
|
if (inputArray.length !== 2) {
|
||||||
|
console.error("Invalid format. Please set a repo to the environment variable 'UPTIME_KUMA_GH_REPO' (e.g. mhkarimi1383:goalert-notification)");
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = inputArray[0];
|
||||||
|
let branch = inputArray[1];
|
||||||
|
|
||||||
|
console.log("Checkout pr");
|
||||||
|
|
||||||
|
// Checkout the pr
|
||||||
|
let result = childProcess.spawnSync("git", [ "remote", "add", name, `https://github.com/${name}/uptime-kuma` ]);
|
||||||
|
|
||||||
|
console.log(result.stdout.toString());
|
||||||
|
console.error(result.stderr.toString());
|
||||||
|
|
||||||
|
result = childProcess.spawnSync("git", [ "fetch", name, branch ]);
|
||||||
|
|
||||||
|
console.log(result.stdout.toString());
|
||||||
|
console.error(result.stderr.toString());
|
||||||
|
|
||||||
|
result = childProcess.spawnSync("git", [ "checkout", branch, "--force" ]);
|
||||||
|
|
||||||
|
console.log(result.stdout.toString());
|
||||||
|
console.error(result.stderr.toString());
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,106 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
const { R } = require("redbean-node");
|
||||||
|
const version = require("../package.json").version;
|
||||||
|
const https = require("https");
|
||||||
|
|
||||||
|
class DockerHost {
|
||||||
|
/**
|
||||||
|
* Save a docker host
|
||||||
|
* @param {Object} dockerHost Docker host to save
|
||||||
|
* @param {?number} dockerHostID ID of the docker host to update
|
||||||
|
* @param {number} userID ID of the user who adds the docker host
|
||||||
|
* @returns {Promise<Bean>}
|
||||||
|
*/
|
||||||
|
static async save(dockerHost, dockerHostID, userID) {
|
||||||
|
let bean;
|
||||||
|
|
||||||
|
if (dockerHostID) {
|
||||||
|
bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [ dockerHostID, userID ]);
|
||||||
|
|
||||||
|
if (!bean) {
|
||||||
|
throw new Error("docker host not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
bean = R.dispense("docker_host");
|
||||||
|
}
|
||||||
|
|
||||||
|
bean.user_id = userID;
|
||||||
|
bean.docker_daemon = dockerHost.dockerDaemon;
|
||||||
|
bean.docker_type = dockerHost.dockerType;
|
||||||
|
bean.name = dockerHost.name;
|
||||||
|
|
||||||
|
await R.store(bean);
|
||||||
|
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a Docker host
|
||||||
|
* @param {number} dockerHostID ID of the Docker host to delete
|
||||||
|
* @param {number} userID ID of the user who created the Docker host
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async delete(dockerHostID, userID) {
|
||||||
|
let bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [ dockerHostID, userID ]);
|
||||||
|
|
||||||
|
if (!bean) {
|
||||||
|
throw new Error("docker host not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removed proxy from monitors if exists
|
||||||
|
await R.exec("UPDATE monitor SET docker_host = null WHERE docker_host = ?", [ dockerHostID ]);
|
||||||
|
|
||||||
|
await R.trash(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the amount of containers on the Docker host
|
||||||
|
* @param {Object} dockerHost Docker host to check for
|
||||||
|
* @returns {number} Total amount of containers on the host
|
||||||
|
*/
|
||||||
|
static async testDockerHost(dockerHost) {
|
||||||
|
const options = {
|
||||||
|
url: "/containers/json?all=true",
|
||||||
|
headers: {
|
||||||
|
"Accept": "*/*",
|
||||||
|
"User-Agent": "Uptime-Kuma/" + version
|
||||||
|
},
|
||||||
|
httpsAgent: new https.Agent({
|
||||||
|
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dockerHost.dockerType === "socket") {
|
||||||
|
options.socketPath = dockerHost.dockerDaemon;
|
||||||
|
} else if (dockerHost.dockerType === "tcp") {
|
||||||
|
options.baseURL = dockerHost.dockerDaemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = await axios.request(options);
|
||||||
|
|
||||||
|
if (Array.isArray(res.data)) {
|
||||||
|
|
||||||
|
if (res.data.length > 1) {
|
||||||
|
|
||||||
|
if ("ImageID" in res.data[0]) {
|
||||||
|
return res.data.length;
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid Docker response, is it Docker really a daemon?");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return res.data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid Docker response, is it Docker really a daemon?");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
DockerHost,
|
||||||
|
};
|
@ -0,0 +1,19 @@
|
|||||||
|
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||||
|
|
||||||
|
class DockerHost extends BeanModel {
|
||||||
|
/**
|
||||||
|
* Returns an object that ready to parse to JSON
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
userID: this.user_id,
|
||||||
|
dockerDaemon: this.docker_daemon,
|
||||||
|
dockerType: this.docker_type,
|
||||||
|
name: this.name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DockerHost;
|
@ -0,0 +1,35 @@
|
|||||||
|
const NotificationProvider = require("./notification-provider");
|
||||||
|
const axios = require("axios");
|
||||||
|
const { UP } = require("../../src/util");
|
||||||
|
|
||||||
|
class GoAlert extends NotificationProvider {
|
||||||
|
|
||||||
|
name = "GoAlert";
|
||||||
|
|
||||||
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
|
let okMsg = "Sent Successfully.";
|
||||||
|
try {
|
||||||
|
let closeAction = "close";
|
||||||
|
let data = {
|
||||||
|
summary: msg,
|
||||||
|
};
|
||||||
|
if (heartbeatJSON != null && heartbeatJSON["status"] === UP) {
|
||||||
|
data["action"] = closeAction;
|
||||||
|
}
|
||||||
|
let headers = {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
};
|
||||||
|
let config = {
|
||||||
|
headers: headers
|
||||||
|
};
|
||||||
|
let resp = await axios.post(`${notification.goAlertBaseURL}/api/v2/generic/incoming?token=${notification.goAlertToken}`, data, config);
|
||||||
|
return okMsg;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
let msg = (error.response.data) ? error.response.data : "Error without response";
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = GoAlert;
|
@ -0,0 +1,38 @@
|
|||||||
|
const NotificationProvider = require("./notification-provider");
|
||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
const defaultNotificationService = "notify";
|
||||||
|
|
||||||
|
class HomeAssistant extends NotificationProvider {
|
||||||
|
name = "HomeAssistant";
|
||||||
|
|
||||||
|
async send(notification, message, monitor = null, heartbeat = null) {
|
||||||
|
const notificationService = notification?.notificationService || defaultNotificationService;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post(
|
||||||
|
`${notification.homeAssistantUrl}/api/services/notify/${notificationService}`,
|
||||||
|
{
|
||||||
|
title: "Uptime Kuma",
|
||||||
|
message,
|
||||||
|
...(notificationService !== "persistent_notification" && { data: {
|
||||||
|
name: monitor?.name,
|
||||||
|
status: heartbeat?.status,
|
||||||
|
} }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${notification.longLivedAccessToken}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return "Sent Successfully.";
|
||||||
|
} catch (error) {
|
||||||
|
this.throwGeneralAxiosError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = HomeAssistant;
|
@ -0,0 +1,43 @@
|
|||||||
|
const NotificationProvider = require("./notification-provider");
|
||||||
|
const axios = require("axios");
|
||||||
|
const qs = require("qs");
|
||||||
|
const { DOWN, UP } = require("../../src/util");
|
||||||
|
|
||||||
|
class LineNotify extends NotificationProvider {
|
||||||
|
|
||||||
|
name = "LineNotify";
|
||||||
|
|
||||||
|
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
|
let okMsg = "Sent Successfully.";
|
||||||
|
try {
|
||||||
|
let lineAPIUrl = "https://notify-api.line.me/api/notify";
|
||||||
|
let config = {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
"Authorization": "Bearer " + notification.lineNotifyAccessToken
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (heartbeatJSON == null) {
|
||||||
|
let testMessage = {
|
||||||
|
"message": msg,
|
||||||
|
};
|
||||||
|
await axios.post(lineAPIUrl, qs.stringify(testMessage), config);
|
||||||
|
} else if (heartbeatJSON["status"] === DOWN) {
|
||||||
|
let downMessage = {
|
||||||
|
"message": "\n[🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
|
||||||
|
};
|
||||||
|
await axios.post(lineAPIUrl, qs.stringify(downMessage), config);
|
||||||
|
} else if (heartbeatJSON["status"] === UP) {
|
||||||
|
let upMessage = {
|
||||||
|
"message": "\n[✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
|
||||||
|
};
|
||||||
|
await axios.post(lineAPIUrl, qs.stringify(upMessage), config);
|
||||||
|
}
|
||||||
|
return okMsg;
|
||||||
|
} catch (error) {
|
||||||
|
this.throwGeneralAxiosError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = LineNotify;
|
@ -0,0 +1,79 @@
|
|||||||
|
const { sendDockerHostList } = require("../client");
|
||||||
|
const { checkLogin } = require("../util-server");
|
||||||
|
const { DockerHost } = require("../docker");
|
||||||
|
const { log } = require("../../src/util");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handlers for docker hosts
|
||||||
|
* @param {Socket} socket Socket.io instance
|
||||||
|
*/
|
||||||
|
module.exports.dockerSocketHandler = (socket) => {
|
||||||
|
socket.on("addDockerHost", async (dockerHost, dockerHostID, callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
let dockerHostBean = await DockerHost.save(dockerHost, dockerHostID, socket.userID);
|
||||||
|
await sendDockerHostList(socket);
|
||||||
|
|
||||||
|
callback({
|
||||||
|
ok: true,
|
||||||
|
msg: "Saved",
|
||||||
|
id: dockerHostBean.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
callback({
|
||||||
|
ok: false,
|
||||||
|
msg: e.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("deleteDockerHost", async (dockerHostID, callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
await DockerHost.delete(dockerHostID, socket.userID);
|
||||||
|
await sendDockerHostList(socket);
|
||||||
|
|
||||||
|
callback({
|
||||||
|
ok: true,
|
||||||
|
msg: "Deleted",
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
callback({
|
||||||
|
ok: false,
|
||||||
|
msg: e.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("testDockerHost", async (dockerHost, callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
let amount = await DockerHost.testDockerHost(dockerHost);
|
||||||
|
let msg;
|
||||||
|
|
||||||
|
if (amount > 1) {
|
||||||
|
msg = "Connected Successfully. Amount of containers: " + amount;
|
||||||
|
} else {
|
||||||
|
msg = "Connected Successfully, but there are no containers?";
|
||||||
|
}
|
||||||
|
|
||||||
|
callback({
|
||||||
|
ok: true,
|
||||||
|
msg,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
log.error("docker", e);
|
||||||
|
|
||||||
|
callback({
|
||||||
|
ok: false,
|
||||||
|
msg: e.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,177 @@
|
|||||||
|
<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 Docker Host") }}
|
||||||
|
</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="docker-name" class="form-label">{{ $t("Friendly Name") }}</label>
|
||||||
|
<input id="docker-name" v-model="dockerHost.name" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="docker-type" class="form-label">{{ $t("Connection Type") }}</label>
|
||||||
|
<select id="docker-type" v-model="dockerHost.dockerType" class="form-select">
|
||||||
|
<option v-for="type in connectionTypes" :key="type" :value="type">{{ $t(type) }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="docker-daemon" class="form-label">{{ $t("Docker Daemon") }}</label>
|
||||||
|
<input id="docker-daemon" v-model="dockerHost.dockerDaemon" type="text" class="form-control" required>
|
||||||
|
|
||||||
|
<div class="form-text">
|
||||||
|
{{ $t("Examples") }}:
|
||||||
|
<ul>
|
||||||
|
<li>/var/run/docker.sock</li>
|
||||||
|
<li>tcp://localhost:2375</li>
|
||||||
|
</ul>
|
||||||
|
</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="button" class="btn btn-warning" :disabled="processing" @click="test">
|
||||||
|
{{ $t("Test") }}
|
||||||
|
</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="deleteDockerHost">
|
||||||
|
{{ $t("deleteDockerHostMsg") }}
|
||||||
|
</Confirm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Modal } from "bootstrap";
|
||||||
|
import Confirm from "./Confirm.vue";
|
||||||
|
import { useToast } from "vue-toastification";
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Confirm,
|
||||||
|
},
|
||||||
|
props: {},
|
||||||
|
emits: [ "added" ],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
model: null,
|
||||||
|
processing: false,
|
||||||
|
id: null,
|
||||||
|
connectionTypes: [ "socket", "tcp" ],
|
||||||
|
dockerHost: {
|
||||||
|
name: "",
|
||||||
|
dockerDaemon: "",
|
||||||
|
dockerType: "",
|
||||||
|
// Do not set default value here, please scroll to show()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.modal = new Modal(this.$refs.modal);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
deleteConfirm() {
|
||||||
|
this.modal.hide();
|
||||||
|
this.$refs.confirmDelete.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
show(dockerHostID) {
|
||||||
|
if (dockerHostID) {
|
||||||
|
let found = false;
|
||||||
|
|
||||||
|
this.id = dockerHostID;
|
||||||
|
|
||||||
|
for (let n of this.$root.dockerHostList) {
|
||||||
|
if (n.id === dockerHostID) {
|
||||||
|
this.dockerHost = n;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
toast.error("Docker Host not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.id = null;
|
||||||
|
this.dockerHost = {
|
||||||
|
name: "",
|
||||||
|
dockerType: "socket",
|
||||||
|
dockerDaemon: "/var/run/docker.sock",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.modal.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
this.processing = true;
|
||||||
|
this.$root.getSocket().emit("addDockerHost", this.dockerHost, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
test() {
|
||||||
|
this.processing = true;
|
||||||
|
this.$root.getSocket().emit("testDockerHost", this.dockerHost, (res) => {
|
||||||
|
this.$root.toastRes(res);
|
||||||
|
this.processing = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteDockerHost() {
|
||||||
|
this.processing = true;
|
||||||
|
this.$root.getSocket().emit("deleteDockerHost", 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>
|
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="goalert-base-url" class="form-label">{{ $t("Base URL") }}</label>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input id="goalert-base-url" v-model="$parent.notification.goAlertBaseURL" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
<i18n-t tag="div" keypath="goAlertInfo" class="form-text">
|
||||||
|
<a href="https://goalert.me" target="_blank">https://goalert.me</a>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="goalert-token" class="form-label">{{ $t("Token") }}</label>
|
||||||
|
<HiddenInput id="goalert-token" v-model="$parent.notification.goAlertToken" autocomplete="one-time-code" :required="true"></HiddenInput>
|
||||||
|
|
||||||
|
<div class="form-text">
|
||||||
|
{{ $t("goAlertIntegrationKeyInfo") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import HiddenInput from "../HiddenInput.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
HiddenInput,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="homeAssistantUrl" class="form-label">{{ $t("Home Assistant URL") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="homeAssistantUrl" v-model="$parent.notification.homeAssistantUrl" type="url" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="longLivedAccessToken" class="form-label">{{ $t("Long-Lived Access Token") }}<span style="color: red;"><sup>*</sup></span></label>
|
||||||
|
<input id="longLivedAccessToken" v-model="$parent.notification.longLivedAccessToken" type="text" class="form-control" required>
|
||||||
|
|
||||||
|
<div class="form-text">
|
||||||
|
<p>{{ $t("Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ") }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="notificationService" class="form-label">{{ $t("Notification Service") }}</label>
|
||||||
|
<input id="notificationService" v-model="$parent.notification.notificationService" type="text" :placeholder="$t('default: notify all devices')" class="form-control">
|
||||||
|
|
||||||
|
<div class="form-text">
|
||||||
|
<p>{{ $t("A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.") }}</p>
|
||||||
|
<p>{{ $t("Automations can optionally be triggered in Home Assistant:") }}</p>
|
||||||
|
<p>
|
||||||
|
{{ $t("Trigger type:") }} <code>Event</code><br />
|
||||||
|
{{ $t("Event type:") }} <code>call_service</code><br />
|
||||||
|
{{ $t("Event data:") }}
|
||||||
|
</p>
|
||||||
|
<pre>domain: notify
|
||||||
|
service: mobile_app_my_phone # change to your device name
|
||||||
|
service_data:
|
||||||
|
title: Uptime Kuma
|
||||||
|
data:
|
||||||
|
status: 0 # 0=down 1=up
|
||||||
|
# name: Optional Uptime Kuma Monitor Name to filter by</pre>
|
||||||
|
<p>
|
||||||
|
{{ $t("Then choose an action, for example switch the scene to where an RGB light is red.") }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="line-notify-access-token" class="form-label">{{ $t("Access Token") }}</label>
|
||||||
|
<input id="line-notify-access-token" v-model="$parent.notification.lineNotifyAccessToken" type="text" class="form-control" :required="true">
|
||||||
|
</div>
|
||||||
|
<i18n-t tag="div" keypath="wayToGetLineNotifyToken" class="form-text" style="margin-top: 8px;">
|
||||||
|
<a href="https://notify-bot.line.me/" target="_blank">https://notify-bot.line.me/</a>
|
||||||
|
</i18n-t>
|
||||||
|
</template>
|
@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="dockerHost-list my-4">
|
||||||
|
<p v-if="$root.dockerHostList.length === 0">
|
||||||
|
{{ $t("Not available, please setup.") }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul class="list-group mb-3" style="border-radius: 1rem;">
|
||||||
|
<li v-for="(dockerHost, index) in $root.dockerHostList" :key="index" class="list-group-item">
|
||||||
|
{{ dockerHost.name }}<br>
|
||||||
|
<a href="#" @click="$refs.dockerHostDialog.show(dockerHost.id)">{{ $t("Edit") }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button class="btn btn-primary me-2" type="button" @click="$refs.dockerHostDialog.show()">
|
||||||
|
{{ $t("Setup Docker Host") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DockerHostDialog ref="dockerHostDialog" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DockerHostDialog from "../../components/DockerHostDialog.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
DockerHostDialog,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
settings() {
|
||||||
|
return this.$parent.$parent.$parent.settings;
|
||||||
|
},
|
||||||
|
saveSettings() {
|
||||||
|
return this.$parent.$parent.$parent.saveSettings;
|
||||||
|
},
|
||||||
|
settingsLoaded() {
|
||||||
|
return this.$parent.$parent.$parent.settingsLoaded;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -0,0 +1,203 @@
|
|||||||
|
export default {
|
||||||
|
languageName: "Português (Portugal)",
|
||||||
|
checkEverySecond: "Verificar a cada {0} segundos.",
|
||||||
|
retryCheckEverySecond: "Tentar novamente a cada {0} segundos.",
|
||||||
|
retriesDescription: "Máximo de tentativas antes que o serviço seja marcado como inativo e uma notificação seja enviada",
|
||||||
|
ignoreTLSError: "Ignorar erros TLS/SSL para sites HTTPS",
|
||||||
|
upsideDownModeDescription: "Inverte o status de cabeça para baixo. Se o serviço estiver acessível, ele está OFFLINE.",
|
||||||
|
maxRedirectDescription: "Número máximo de redirecionamentos a seguir. Define como 0 para desativar redirecionamentos.",
|
||||||
|
acceptedStatusCodesDescription: "Seleciona os códigos de status que são considerados uma resposta bem-sucedida.",
|
||||||
|
passwordNotMatchMsg: "A senha repetida não corresponde.",
|
||||||
|
notificationDescription: "Atribuir uma notificação ao (s) monitor (es) para que funcione.",
|
||||||
|
keywordDescription: "Pesquisa a palavra-chave em HTML simples ou resposta JSON e diferencia maiúsculas de minúsculas",
|
||||||
|
pauseDashboardHome: "Pausa",
|
||||||
|
deleteMonitorMsg: "Tens a certeza de que queres excluir este monitor?",
|
||||||
|
deleteNotificationMsg: "Tens a certeza de que queres excluir esta notificação para todos os monitores?",
|
||||||
|
resolverserverDescription: "A Cloudflare é o servidor padrão, podes alterar o servidor 'resolvedor' a qualquer momento.",
|
||||||
|
rrtypeDescription: "Seleciona o RR-Type que queres monitorizar",
|
||||||
|
pauseMonitorMsg: "Tens a certeza que queres fazer uma pausa?",
|
||||||
|
enableDefaultNotificationDescription: "Para cada monitor novo esta notificação vai estar activa por padrão. Podes também desativar a notificação separadamente para cada monitor.",
|
||||||
|
clearEventsMsg: "Tens a certeza que queres excluir todos os eventos deste monitor?",
|
||||||
|
clearHeartbeatsMsg: "Tens a certeza de que queres excluir todos os heartbeats deste monitor?",
|
||||||
|
confirmClearStatisticsMsg: "Tens a certeza que queres excluir TODAS as estatísticas?",
|
||||||
|
importHandleDescription: "Escolhe 'Ignorar existente' se quiseres ignorar todos os monitores ou notificações com o mesmo nome. 'Substituir' excluirá todos os monitores e notificações existentes.",
|
||||||
|
confirmImportMsg: "Tens a certeza que queres importar o backup? Certifica-te que selecionaste a opção de importação correta.",
|
||||||
|
twoFAVerifyLabel: "Insire o teu token para verificares se o 2FA está a funcionar",
|
||||||
|
tokenValidSettingsMsg: "O token é válido! Agora podes salvar as configurações do 2FA.",
|
||||||
|
confirmEnableTwoFAMsg: "Tens a certeza de que queres habilitar 2FA?",
|
||||||
|
confirmDisableTwoFAMsg: "Tens a certeza de que queres desativar 2FA?",
|
||||||
|
Settings: "Configurações",
|
||||||
|
Dashboard: "Dashboard",
|
||||||
|
"New Update": "Nova Atualização",
|
||||||
|
Language: "Linguagem",
|
||||||
|
Appearance: "Aparência",
|
||||||
|
Theme: "Tema",
|
||||||
|
General: "Geral",
|
||||||
|
Version: "Versão",
|
||||||
|
"Check Update On GitHub": "Verificar atualização no Github",
|
||||||
|
List: "Lista",
|
||||||
|
Add: "Adicionar",
|
||||||
|
"Add New Monitor": "Adicionar novo monitor",
|
||||||
|
"Quick Stats": "Estatísticas rápidas",
|
||||||
|
Up: "On",
|
||||||
|
Down: "Off",
|
||||||
|
Pending: "Pendente",
|
||||||
|
Unknown: "Desconhecido",
|
||||||
|
Pause: "Pausa",
|
||||||
|
Name: "Nome",
|
||||||
|
Status: "Status",
|
||||||
|
DateTime: "Data hora",
|
||||||
|
Message: "Mensagem",
|
||||||
|
"No important events": "Nenhum evento importante",
|
||||||
|
Resume: "Resumo",
|
||||||
|
Edit: "Editar",
|
||||||
|
Delete: "Apagar",
|
||||||
|
Current: "Atual",
|
||||||
|
Uptime: "Tempo de atividade",
|
||||||
|
"Cert Exp.": "Cert Exp.",
|
||||||
|
day: "dia | dias",
|
||||||
|
"-day": "-dia",
|
||||||
|
hour: "hora",
|
||||||
|
"-hour": "-hora",
|
||||||
|
Response: "Resposta",
|
||||||
|
Ping: "Ping",
|
||||||
|
"Monitor Type": "Tipo de Monitor",
|
||||||
|
Keyword: "Palavra-Chave",
|
||||||
|
"Friendly Name": "Nome Amigável",
|
||||||
|
URL: "URL",
|
||||||
|
Hostname: "Hostname",
|
||||||
|
Port: "Porta",
|
||||||
|
"Heartbeat Interval": "Intervalo de Heartbeats",
|
||||||
|
Retries: "Novas tentativas",
|
||||||
|
"Heartbeat Retry Interval": "Intervalo de repetição de Heartbeats",
|
||||||
|
Advanced: "Avançado",
|
||||||
|
"Upside Down Mode": "Modo de cabeça para baixo",
|
||||||
|
"Max. Redirects": "Redirecionamento Máx.",
|
||||||
|
"Accepted Status Codes": "Status Code Aceitáveis",
|
||||||
|
Save: "Guardar",
|
||||||
|
Notifications: "Notificações",
|
||||||
|
"Not available, please setup.": "Não disponível, por favor configura.",
|
||||||
|
"Setup Notification": "Configurar Notificação",
|
||||||
|
Light: "Claro",
|
||||||
|
Dark: "Escuro",
|
||||||
|
Auto: "Auto",
|
||||||
|
"Theme - Heartbeat Bar": "Tema - Barra de Heartbeat",
|
||||||
|
Normal: "Normal",
|
||||||
|
Bottom: "Inferior",
|
||||||
|
None: "Nenhum",
|
||||||
|
Timezone: "Fuso horário",
|
||||||
|
"Search Engine Visibility": "Visibilidade do mecanismo de pesquisa",
|
||||||
|
"Allow indexing": "Permitir Indexação",
|
||||||
|
"Discourage search engines from indexing site": "Desencorajar que motores de busca indexem o site",
|
||||||
|
"Change Password": "Mudar senha",
|
||||||
|
"Current Password": "Senha atual",
|
||||||
|
"New Password": "Nova Senha",
|
||||||
|
"Repeat New Password": "Repetir Nova Senha",
|
||||||
|
"Update Password": "Atualizar Senha",
|
||||||
|
"Disable Auth": "Desativar Autenticação",
|
||||||
|
"Enable Auth": "Ativar Autenticação",
|
||||||
|
"disableauth.message1": "Tens a certeza que queres <strong>desativar a autenticação</strong>?",
|
||||||
|
"disableauth.message2": "Isso é para <strong>alguém que tem autenticação de terceiros</strong> em frente ao 'UpTime Kuma' como o Cloudflare Access.",
|
||||||
|
"Please use this option carefully!": "Por favor, utiliza esta opção com cuidado.",
|
||||||
|
Logout: "Logout",
|
||||||
|
Leave: "Sair",
|
||||||
|
"I understand, please disable": "Eu entendo, por favor desativa.",
|
||||||
|
Confirm: "Confirmar",
|
||||||
|
Yes: "Sim",
|
||||||
|
No: "Não",
|
||||||
|
Username: "Utilizador",
|
||||||
|
Password: "Senha",
|
||||||
|
"Remember me": "Lembra-me",
|
||||||
|
Login: "Autenticar",
|
||||||
|
"No Monitors, please": "Nenhum monitor, por favor",
|
||||||
|
"add one": "adicionar um",
|
||||||
|
"Notification Type": "Tipo de Notificação",
|
||||||
|
Email: "Email",
|
||||||
|
Test: "Testar",
|
||||||
|
"Certificate Info": "Info. do Certificado ",
|
||||||
|
"Resolver Server": "Resolver Servidor",
|
||||||
|
"Resource Record Type": "Tipo de registro de aplicação",
|
||||||
|
"Last Result": "Último resultado",
|
||||||
|
"Create your admin account": "Cria a tua conta de admin",
|
||||||
|
"Repeat Password": "Repete a senha",
|
||||||
|
"Import Backup": "Importar Backup",
|
||||||
|
"Export Backup": "Exportar Backup",
|
||||||
|
Export: "Exportar",
|
||||||
|
Import: "Importar",
|
||||||
|
respTime: "Tempo de Resp. (ms)",
|
||||||
|
notAvailableShort: "N/A",
|
||||||
|
"Default enabled": "Padrão habilitado",
|
||||||
|
"Apply on all existing monitors": "Aplicar em todos os monitores existentes",
|
||||||
|
Create: "Criar",
|
||||||
|
"Clear Data": "Limpar Dados",
|
||||||
|
Events: "Eventos",
|
||||||
|
Heartbeats: "Heartbeats",
|
||||||
|
"Auto Get": "Obter Automático",
|
||||||
|
backupDescription: "Podes fazer backup de todos os monitores e todas as notificações num arquivo JSON.",
|
||||||
|
backupDescription2: "OBS: Os dados do histórico e do evento não estão incluídos.",
|
||||||
|
backupDescription3: "Dados confidenciais, como tokens de notificação, estão incluídos no arquivo de exportação, mantem-no com cuidado.",
|
||||||
|
alertNoFile: "Seleciona um arquivo para importar.",
|
||||||
|
alertWrongFileType: "Seleciona um arquivo JSON.",
|
||||||
|
"Clear all statistics": "Limpar todas as estatísticas",
|
||||||
|
"Skip existing": "Saltar existente",
|
||||||
|
Overwrite: "Sobrescrever",
|
||||||
|
Options: "Opções",
|
||||||
|
"Keep both": "Manter os dois",
|
||||||
|
"Verify Token": "Verificar Token",
|
||||||
|
"Setup 2FA": "Configurar 2FA",
|
||||||
|
"Enable 2FA": "Ativar 2FA",
|
||||||
|
"Disable 2FA": "Desativar 2FA",
|
||||||
|
"2FA Settings": "Configurações do 2FA ",
|
||||||
|
"Two Factor Authentication": "Autenticação de Dois Fatores",
|
||||||
|
Active: "Ativo",
|
||||||
|
Inactive: "Inativo",
|
||||||
|
Token: "Token",
|
||||||
|
"Show URI": "Mostrar URI",
|
||||||
|
Tags: "Tag",
|
||||||
|
"Add New below or Select...": "Adicionar Novo abaixo ou Selecionar ...",
|
||||||
|
"Tag with this name already exist.": "Já existe uma etiqueta com este nome.",
|
||||||
|
"Tag with this value already exist.": "Já existe uma etiqueta com este valor.",
|
||||||
|
color: "cor",
|
||||||
|
"value (optional)": "valor (opcional)",
|
||||||
|
Gray: "Cinza",
|
||||||
|
Red: "Vermelho",
|
||||||
|
Orange: "Laranja",
|
||||||
|
Green: "Verde",
|
||||||
|
Blue: "Azul",
|
||||||
|
Indigo: "Índigo",
|
||||||
|
Purple: "Roxo",
|
||||||
|
Pink: "Rosa",
|
||||||
|
"Search...": "Pesquisa...",
|
||||||
|
"Avg. Ping": "Ping Médio.",
|
||||||
|
"Avg. Response": "Resposta Média. ",
|
||||||
|
"Status Page": "Página de Status",
|
||||||
|
"Status Pages": "Página de Status",
|
||||||
|
"Entry Page": "Página de entrada",
|
||||||
|
statusPageNothing: "Nada aqui, por favor, adiciona um grupo ou monitor.",
|
||||||
|
"No Services": "Nenhum Serviço",
|
||||||
|
"All Systems Operational": "Todos os Serviços Operacionais",
|
||||||
|
"Partially Degraded Service": "Serviço parcialmente degradados",
|
||||||
|
"Degraded Service": "Serviço Degradado",
|
||||||
|
"Add Group": "Adicionar Grupo",
|
||||||
|
"Add a monitor": "Adicionar um monitor",
|
||||||
|
"Edit Status Page": "Editar Página de Status",
|
||||||
|
"Go to Dashboard": "Ir para o dashboard",
|
||||||
|
telegram: "Telegram",
|
||||||
|
webhook: "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 (Support 50+ Notification services)",
|
||||||
|
pushbullet: "Pushbullet",
|
||||||
|
line: "Line Messenger",
|
||||||
|
mattermost: "Mattermost",
|
||||||
|
};
|
Loading…
Reference in new issue