Merge remote-tracking branch 'upstream/master'

# Conflicts:
#	docker-compose.yml
#	dockerfile
#	package-lock.json
#	package.json
#	server/notification.js
#	server/server.js
#	src/assets/vars.scss
pull/154/head^2
Philipp Dormann 3 years ago
commit 7cabafebaf
No known key found for this signature in database
GPG Key ID: 3BB9ADD52DCA4314

@ -13,3 +13,6 @@ trim_trailing_whitespace = false
[*.yaml]
indent_size = 2
[*.yml]
indent_size = 2

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -1,5 +1,5 @@
const axios = require("axios");
const { R } = require("redbean-node");
const {R} = require("redbean-node");
const FormData = require('form-data');
const nodemailer = require("nodemailer");
@ -52,62 +52,64 @@ class Notification {
} else if (notification.type === "smtp") {
return await Notification.smtp(notification, msg)
} else if (notification.type === "signal") {
try {
let data = {
"message": msg,
"number": notification.signalNumber,
"recipients": notification.signalRecipients.replace(/\s/g, '').split(",")
};
let config = {};
let res = await axios.post(notification.signalURL, data, config)
return true;
} catch (error) {
console.log(error)
return false;
}
} else if (notification.type === "discord") {
try {
// If heartbeatJSON is null, assume we're testing.
if (heartbeatJSON == null) {
let data = {
username: 'Uptime-Kuma',
content: msg
}
let res = await axios.post(notification.discordWebhookUrl, data)
return true;
}
// If heartbeatJSON is not null, we go into the normal alerting loop.
if (heartbeatJSON['status'] == 0) {
var alertColor = "16711680";
} else if (heartbeatJSON['status'] == 1) {
var alertColor = "65280";
}
// If heartbeatJSON is null, assume we're testing.
if(heartbeatJSON == null) {
let data = {
username: 'Uptime-Kuma',
embeds: [{
title: "Uptime-Kuma Alert",
color: alertColor,
fields: [
{
name: "Time (UTC)",
value: heartbeatJSON["time"]
},
{
name: "Message",
value: msg
}
]
}]
username: 'Uptime-Kuma',
content: msg
}
let res = await axios.post(notification.discordWebhookUrl, data)
return true;
} catch (error) {
console.log(error)
return false;
}
// If heartbeatJSON is not null, we go into the normal alerting loop.
if(heartbeatJSON['status'] == 0) {
var alertColor = "16711680";
} else if(heartbeatJSON['status'] == 1) {
var alertColor = "65280";
}
let data = {
username: 'Uptime-Kuma',
embeds: [{
title: "Uptime-Kuma Alert",
color: alertColor,
fields: [
{
name: "Time (UTC)",
value: heartbeatJSON["time"]
},
{
name: "Message",
value: msg
}
]
}]
}
let res = await axios.post(notification.discordWebhookUrl, data)
return true;
} catch(error) {
console.log(error)
return false;
}
return await Notification.discord(notification, msg)
} else if (notification.type === "signal") {
try {
let data = {
"message": msg,
"number": notification.signalNumber,
"recipients": notification.signalRecipients.replace(/\s/g, '').split(",")
};
let config = {};
let res = await axios.post(notification.signalURL, data, config)
return true;
} catch (error) {
console.log(error)
return false;
}
} else {
throw new Error("Notification type is not supported")
}
@ -122,7 +124,7 @@ class Notification {
userID,
])
if (!bean) {
if (! bean) {
throw new Error("notification not found")
}
@ -142,7 +144,7 @@ class Notification {
userID,
])
if (!bean) {
if (! bean) {
throw new Error("notification not found")
}
@ -171,6 +173,18 @@ class Notification {
return true;
}
static async discord(notification, msg) {
const client = new Discord.Client();
await client.login(notification.discordToken)
const channel = await client.channels.fetch(notification.discordChannelID);
await channel.send(msg);
client.destroy()
return true;
}
}
module.exports = {

@ -5,20 +5,18 @@ const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
const dayjs = require("dayjs");
const { R } = require("redbean-node");
const {R} = require("redbean-node");
const passwordHash = require('password-hash');
const jwt = require('jsonwebtoken');
const Monitor = require("./model/monitor");
const fs = require("fs");
const { getSettings } = require("./util-server");
const { Notification } = require("./notification")
const {getSettings} = require("./util-server");
const {Notification} = require("./notification")
const args = require('args-parser')(process.argv);
console.log("args:")
console.log(args)
const version = require('../package.json').version;
const hostname = args.host || "0.0.0.0"
const port = args.port || 50013
const port = args.port || 3001
app.use(express.json())
@ -32,18 +30,16 @@ let needSetup = false;
app.use('/', express.static("dist"));
app.post('/test-webhook', function (request, response, next) {
console.log("Test Webhook (application/json only)")
console.log("Content-Type: " + request.header("Content-Type"))
console.log(request.body)
response.end();
});
app.get('*', function (request, response, next) {
app.get('*', function(request, response, next) {
response.sendFile(process.cwd() + '/dist/index.html');
});
io.on('connection', async (socket) => {
socket.emit("info", {
version,
})
console.log('a user connected');
totalClient++;
@ -194,7 +190,7 @@ let needSetup = false;
try {
checkLogin(socket)
let bean = await R.findOne("monitor", " id = ? ", [monitor.id])
let bean = await R.findOne("monitor", " id = ? ", [ monitor.id ])
if (bean.user_id !== socket.userID) {
throw new Error("Permission denied.")
@ -332,7 +328,7 @@ let needSetup = false;
try {
checkLogin(socket)
if (!password.currentPassword) {
if (! password.currentPassword) {
throw new Error("Invalid new password")
}
@ -471,7 +467,7 @@ async function checkOwner(userID, monitorID) {
userID,
])
if (!row) {
if (! row) {
throw new Error("You do not own this monitor.");
}
}
@ -526,7 +522,7 @@ async function getMonitorJSONList(userID) {
}
function checkLogin(socket) {
if (!socket.userID) {
if (! socket.userID) {
throw new Error("You are not logged in.");
}
}
@ -534,7 +530,7 @@ function checkLogin(socket) {
async function initDatabase() {
const path = './data/kuma.db';
if (!fs.existsSync(path)) {
if (! fs.existsSync(path)) {
console.log("Copy Database")
fs.copyFileSync("./db/kuma.db", path);
}
@ -551,7 +547,7 @@ async function initDatabase() {
"jwtSecret"
]);
if (!jwtSecretBean) {
if (! jwtSecretBean) {
console.log("JWT secret is not found, generate one.")
jwtSecretBean = R.dispense("setting")
jwtSecretBean.key = "jwtSecret"
@ -638,7 +634,7 @@ async function sendHeartbeatList(socket, monitorID) {
let result = [];
for (let bean of list) {
result.unshift(bean.toJSON())
result.unshift(bean.toJSON())
}
socket.emit("heartbeatList", monitorID, result)

@ -1,46 +1,16 @@
@import "vars.scss";
@import "node_modules/bootstrap/scss/bootstrap";
html,
body,
input,
.modal-content {
background: var(--page-background);
color: var(--main-font-color);
}
a,
.table,
.nav-link {
color: var(--main-font-color);
}
.nav-pills .nav-link.active,
.nav-pills .show > .nav-link {
color: #0a0a0a;
}
.nav-link:hover,
.nav-link:focus {
color: #5cdd8b;
}
.form-control,
.form-control:focus,
.form-select,
.form-select:focus {
color: var(--main-font-color);
background-color: var(--background-4);
}
#app {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif,
apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji;
}
.shadow-box {
overflow: hidden;
box-shadow: 0 15px 70px rgba(0, 0, 0, 0.1);
box-shadow: 0 15px 70px rgba(0, 0, 0, .1);
padding: 10px;
border-radius: 10px;
&.big-padding {
padding: 20px;
}
@ -52,14 +22,10 @@ a,
}
.btn-primary {
// color: white;
color: #0a0a0a;
color: white;
&:hover,
&:active,
&:focus,
&.active {
color: #0a0a0a;
&:hover, &:active, &:focus, &.active {
color: white;
background-color: $highlight;
border-color: $highlight;
}
@ -70,8 +36,3 @@ a,
backdrop-filter: blur(3px);
}
@media (prefers-color-scheme: dark) {
a:hover {
color: #7ce8a4;
}
}

@ -159,7 +159,7 @@ export default {
border-radius: 50rem;
&.empty {
background-color: var(--background-ternary);
background-color: aliceblue;
}
&.down {

@ -1,5 +1,5 @@
<template>
<span class="badge rounded-pill" :class="'bg-' + color">{{ text }}</span>
<span class="badge rounded-pill" :class=" 'bg-' + color ">{{ text }}</span>
</template>
<script>
@ -33,10 +33,7 @@ export default {
</script>
<style scoped>
span {
width: 45px;
}
.badge {
color: #0a0a0a;
}
span {
width: 45px;
}
</style>

@ -57,7 +57,5 @@ export default {
</script>
<style scoped>
.badge{
color: #0a0a0a;
}
</style>

@ -8,7 +8,7 @@
<!-- Desktop header -->
<header class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom" v-if="! $root.isMobile">
<router-link to="/dashboard" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-decoration-none">
<router-link to="/dashboard" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
<object class="bi me-2 ms-4" width="40" height="40" data="/icon.svg"></object>
<span class="fs-4 title">Uptime Kuma</span>
</router-link>
@ -21,7 +21,7 @@
<!-- Mobile header -->
<header class="d-flex flex-wrap justify-content-center mt-3 mb-3" v-else>
<router-link to="/dashboard" class="d-flex align-items-center text-decoration-none">
<router-link to="/dashboard" class="d-flex align-items-center text-dark text-decoration-none">
<object class="bi" width="40" height="40" data="/icon.svg"></object>
<span class="fs-4 title ms-2">Uptime Kuma</span>
</router-link>
@ -33,6 +33,14 @@
<Login v-if="! $root.loggedIn && $root.allowLoginDialog" />
</main>
<footer>
<div class="container-fluid">
Uptime Kuma -
Version: {{ $root.info.version }} -
<a href="https://github.com/louislam/uptime-kuma/releases" target="_blank">Check Update On GitHub</a>
</div>
</footer>
<!-- Mobile Only -->
<div style="width: 100%;height: 60px;" v-if="$root.isMobile"></div>
<nav class="bottom-nav" v-if="$root.isMobile">
@ -87,7 +95,7 @@ export default {
height: 60px;
width: 100%;
left: 0;
background-color: var(--background-secondary);
background-color: #fff;
box-shadow: 0 15px 47px 0 rgba(0, 0, 0, 0.05), 0 5px 14px 0 rgba(0, 0, 0, 0.05);
text-align: center;
white-space: nowrap;
@ -130,6 +138,14 @@ export default {
}
main {
}
footer {
color: #AAA;
font-size: 13px;
margin-bottom: 30px;
margin-left: 10px;
}
</style>

@ -9,6 +9,7 @@ export default {
data() {
return {
info: { },
socket: {
token: null,
firstConnect: true,
@ -39,6 +40,10 @@ export default {
transports: ['websocket']
});
socket.on('info', (info) => {
this.info = info;
});
socket.on('setup', (monitorID, data) => {
this.$router.push("/setup")
});
@ -275,6 +280,13 @@ export default {
watch: {
// Reload the SPA if the server version is changed.
"info.version"(to, from) {
if (from && from !== to) {
window.location.reload()
}
},
remember() {
localStorage.remember = (this.remember) ? "1" : "0"
}

@ -129,19 +129,17 @@ export default {
}
&:hover {
color: var(--main-font-color);
background-color: var(--background-4);
background-color: $highlight-white;
}
&.active {
background-color: var(--background-secondary);
background-color: #cdf8f4;
}
}
}
.badge {
min-width: 58px;
color: #0a0a0a;
}
.small-padding {

@ -362,22 +362,29 @@ const aryIannaTimeZones = [
'Pacific/Efate',
'Pacific/Wallis',
'Pacific/Apia',
'Africa/Johannesburg'
'Africa/Johannesburg',
];
export function timezoneList() {
let result = [];
for (let timezone of aryIannaTimeZones) {
let display = dayjs().tz(timezone).format("Z");
try {
let display = dayjs().tz(timezone).format("Z");
result.push({
name: `(UTC${display}) ${timezone}`,
value: timezone,
time: getTimezoneOffset(timezone),
})
} catch (e) {
console.log(e.message);
console.log("Skip this timezone")
}
result.push({
name: `(UTC${display}) ${timezone}`,
value: timezone,
time: getTimezoneOffset(timezone),
})
}
result.sort((a, b) => {

Loading…
Cancel
Save