mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-12-22 05:55:16 -05:00
Merge branch 'master' into simple_pagination
This commit is contained in:
commit
16f363ac38
@ -3,3 +3,11 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/data/kuma.db
|
/data/kuma.db
|
||||||
/.do
|
/.do
|
||||||
|
**/.dockerignore
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
.editorconfig
|
||||||
|
@ -17,11 +17,14 @@ RUN apk add --no-cache --virtual .build-deps make g++ python3 python3-dev && \
|
|||||||
# Compilation Fail 3 => Google Search "alpine opensslv.h" => Add openssl-dev
|
# Compilation Fail 3 => Google Search "alpine opensslv.h" => Add openssl-dev
|
||||||
# Compilation Fail 4 => Google Search "alpine opensslv.h" again => Change to libressl-dev musl-dev
|
# Compilation Fail 4 => Google Search "alpine opensslv.h" again => Change to libressl-dev musl-dev
|
||||||
# Compilation Fail 5 => Google Search "ERROR: libressl3.3-libtls-3.3.3-r0: trying to overwrite usr/lib/libtls.so.20 owned by libretls-3.3.3-r0." again => Change back to openssl-dev with musl-dev
|
# Compilation Fail 5 => Google Search "ERROR: libressl3.3-libtls-3.3.3-r0: trying to overwrite usr/lib/libtls.so.20 owned by libretls-3.3.3-r0." again => Change back to openssl-dev with musl-dev
|
||||||
|
# Runtime Error => ModuleNotFoundError: No module named 'six' => pip3 install six
|
||||||
|
# Runtime Error 2 => ModuleNotFoundError: No module named 'six' => apk add py3-six
|
||||||
ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1
|
ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1
|
||||||
RUN apk add --no-cache python3
|
RUN apk add --no-cache python3 py3-pip py3-six cargo
|
||||||
RUN apk add --no-cache --virtual .build-deps libffi-dev musl-dev openssl-dev cargo py3-pip python3-dev && \
|
RUN apk add --no-cache --virtual .build-deps libffi-dev musl-dev openssl-dev python3-dev && \
|
||||||
pip3 install apprise && \
|
pip3 install apprise && \
|
||||||
apk del .build-deps
|
apk del .build-deps
|
||||||
|
RUN apprise --version
|
||||||
|
|
||||||
# New things add here
|
# New things add here
|
||||||
|
|
||||||
@ -31,7 +34,7 @@ RUN npm run build
|
|||||||
|
|
||||||
EXPOSE 3001
|
EXPOSE 3001
|
||||||
VOLUME ["/app/data"]
|
VOLUME ["/app/data"]
|
||||||
HEALTHCHECK --interval=5s --timeout=3s --start-period=30s CMD node extra/healthcheck.js
|
HEALTHCHECK --interval=60s --timeout=30s --start-period=300s CMD node extra/healthcheck.js
|
||||||
CMD ["npm", "run", "start-server"]
|
CMD ["npm", "run", "start-server"]
|
||||||
|
|
||||||
FROM release AS nightly
|
FROM release AS nightly
|
||||||
|
7
package-lock.json
generated
7
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "uptime-kuma",
|
"name": "uptime-kuma",
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -655,6 +655,11 @@
|
|||||||
"delayed-stream": "~1.0.0"
|
"delayed-stream": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"command-exists": {
|
||||||
|
"version": "1.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
|
||||||
|
"integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="
|
||||||
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "uptime-kuma",
|
"name": "uptime-kuma",
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/louislam/uptime-kuma.git"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
"start-server": "node server/server.js",
|
"start-server": "node server/server.js",
|
||||||
@ -19,6 +24,7 @@
|
|||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"bootstrap": "^5.0.0",
|
"bootstrap": "^5.0.0",
|
||||||
|
"command-exists": "^1.2.9",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.4",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
|
@ -2,9 +2,16 @@ const axios = require("axios");
|
|||||||
const {R} = require("redbean-node");
|
const {R} = require("redbean-node");
|
||||||
const FormData = require('form-data');
|
const FormData = require('form-data');
|
||||||
const nodemailer = require("nodemailer");
|
const nodemailer = require("nodemailer");
|
||||||
|
const child_process = require("child_process");
|
||||||
|
|
||||||
class Notification {
|
class Notification {
|
||||||
static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
||||||
|
|
||||||
|
let res = {
|
||||||
|
ok: true,
|
||||||
|
msg: "Sent Successfully"
|
||||||
|
}
|
||||||
|
|
||||||
if (notification.type === "telegram") {
|
if (notification.type === "telegram") {
|
||||||
try {
|
try {
|
||||||
await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {
|
await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {
|
||||||
@ -185,7 +192,7 @@ class Notification {
|
|||||||
var pushoverlink = 'https://api.pushover.net/1/messages.json'
|
var pushoverlink = 'https://api.pushover.net/1/messages.json'
|
||||||
try {
|
try {
|
||||||
if (heartbeatJSON == null) {
|
if (heartbeatJSON == null) {
|
||||||
let data = {'message': "<b>Uptime Kuma Pushover testing successful.</b>",
|
let data = {'message': "<b>Uptime Kuma Pushover testing successful.</b>",
|
||||||
'user': notification.pushoveruserkey, 'token': notification.pushoverapptoken, 'sound':notification.pushoversounds,
|
'user': notification.pushoveruserkey, 'token': notification.pushoverapptoken, 'sound':notification.pushoversounds,
|
||||||
'priority': notification.pushoverpriority, 'title':notification.pushovertitle, 'retry': "30", 'expire':"3600", 'html': 1}
|
'priority': notification.pushoverpriority, 'title':notification.pushovertitle, 'retry': "30", 'expire':"3600", 'html': 1}
|
||||||
let res = await axios.post(pushoverlink, data)
|
let res = await axios.post(pushoverlink, data)
|
||||||
@ -210,6 +217,10 @@ class Notification {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (notification.type === "apprise") {
|
||||||
|
|
||||||
|
return Notification.apprise(notification, msg)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Notification type is not supported")
|
throw new Error("Notification type is not supported")
|
||||||
}
|
}
|
||||||
@ -274,16 +285,26 @@ class Notification {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async discord(notification, msg) {
|
static async apprise(notification, msg) {
|
||||||
const client = new Discord.Client();
|
let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL])
|
||||||
await client.login(notification.discordToken)
|
let output = s.stdout.toString();
|
||||||
|
|
||||||
const channel = await client.channels.fetch(notification.discordChannelID);
|
console.log(output)
|
||||||
await channel.send(msg);
|
|
||||||
|
|
||||||
client.destroy()
|
if (output) {
|
||||||
|
return {
|
||||||
|
ok: ! output.includes("ERROR"),
|
||||||
|
msg: output
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
static checkApprise() {
|
||||||
|
let commandExistsSync = require('command-exists').sync;
|
||||||
|
let exists = commandExistsSync('apprise');
|
||||||
|
return exists;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,12 +35,15 @@ let needSetup = false;
|
|||||||
(async () => {
|
(async () => {
|
||||||
await initDatabase();
|
await initDatabase();
|
||||||
|
|
||||||
|
console.log("Adding route")
|
||||||
app.use('/', express.static("dist"));
|
app.use('/', express.static("dist"));
|
||||||
|
|
||||||
app.get('*', function(request, response, next) {
|
app.get('*', function(request, response, next) {
|
||||||
response.sendFile(process.cwd() + '/dist/index.html');
|
response.sendFile(process.cwd() + '/dist/index.html');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
console.log("Adding socket handler")
|
||||||
io.on('connection', async (socket) => {
|
io.on('connection', async (socket) => {
|
||||||
|
|
||||||
socket.emit("info", {
|
socket.emit("info", {
|
||||||
@ -437,12 +440,9 @@ let needSetup = false;
|
|||||||
try {
|
try {
|
||||||
checkLogin(socket)
|
checkLogin(socket)
|
||||||
|
|
||||||
await Notification.send(notification, notification.name + " Testing")
|
let res = await Notification.send(notification, notification.name + " Testing")
|
||||||
|
|
||||||
callback({
|
callback(res);
|
||||||
ok: true,
|
|
||||||
msg: "Sent Successfully"
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callback({
|
callback({
|
||||||
@ -451,11 +451,20 @@ let needSetup = false;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("checkApprise", async (callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket)
|
||||||
|
callback(Notification.checkApprise());
|
||||||
|
} catch (e) {
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("Init")
|
||||||
server.listen(port, hostname, () => {
|
server.listen(port, hostname, () => {
|
||||||
console.log(`Listening on ${hostname}:${port}`);
|
console.log(`Listening on ${hostname}:${port}`);
|
||||||
|
|
||||||
startMonitors();
|
startMonitors();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -551,10 +560,11 @@ async function initDatabase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log("Connecting to Database")
|
console.log("Connecting to Database")
|
||||||
|
|
||||||
R.setup('sqlite', {
|
R.setup('sqlite', {
|
||||||
filename: path
|
filename: path
|
||||||
});
|
});
|
||||||
|
console.log("Connected")
|
||||||
|
|
||||||
R.freeze(true)
|
R.freeze(true)
|
||||||
await R.autoloadModels("./server/model");
|
await R.autoloadModels("./server/model");
|
||||||
|
|
||||||
@ -569,6 +579,7 @@ async function initDatabase() {
|
|||||||
|
|
||||||
jwtSecretBean.value = passwordHash.generate(dayjs() + "")
|
jwtSecretBean.value = passwordHash.generate(dayjs() + "")
|
||||||
await R.store(jwtSecretBean)
|
await R.store(jwtSecretBean)
|
||||||
|
console.log("Stored JWT secret into database")
|
||||||
} else {
|
} else {
|
||||||
console.log("Load JWT secret from database.")
|
console.log("Load JWT secret from database.")
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,14 @@
|
|||||||
<label for="floatingPassword">Password</label>
|
<label for="floatingPassword">Password</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-check mb-3 mt-3" >
|
<div class="form-check mb-3 mt-3 d-flex justify-content-center pe-4">
|
||||||
<label>
|
<div class="form-check">
|
||||||
<input type="checkbox" value="remember-me" class="form-check-input" id="remember" v-model="$root.remember">
|
<input type="checkbox" value="remember-me" class="form-check-input" id="remember" v-model="$root.remember">
|
||||||
|
|
||||||
<label class="form-check-label" for="remember">
|
<label class="form-check-label" for="remember">
|
||||||
Remember me
|
Remember me
|
||||||
</label>
|
</label>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="w-100 btn btn-primary" type="submit" :disabled="processing">Login</button>
|
<button class="w-100 btn btn-primary" type="submit" :disabled="processing">Login</button>
|
||||||
|
|
||||||
|
@ -10,60 +10,61 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="type" class="form-label">Notification Type</label>
|
||||||
|
<select class="form-select" id="type" v-model="notification.type">
|
||||||
|
<option value="telegram">Telegram</option>
|
||||||
|
<option value="webhook">Webhook</option>
|
||||||
|
<option value="smtp">Email (SMTP)</option>
|
||||||
|
<option value="discord">Discord</option>
|
||||||
|
<option value="signal">Signal</option>
|
||||||
|
<option value="gotify">Gotify</option>
|
||||||
|
<option value="slack">Slack</option>
|
||||||
|
<option value="pushover">Pushover</option>
|
||||||
|
<option value="apprise">Apprise (Support 50+ Notification services)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Friendly Name</label>
|
||||||
|
<input type="text" class="form-control" id="name" required v-model="notification.name">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="notification.type === 'telegram'">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="type" class="form-label">Notification Type</label>
|
<label for="telegram-bot-token" class="form-label">Bot Token</label>
|
||||||
<select class="form-select" id="type" v-model="notification.type">
|
<input type="text" class="form-control" id="telegram-bot-token" required v-model="notification.telegramBotToken">
|
||||||
<option value="telegram">Telegram</option>
|
<div class="form-text">You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.</div>
|
||||||
<option value="webhook">Webhook</option>
|
|
||||||
<option value="smtp">Email (SMTP)</option>
|
|
||||||
<option value="discord">Discord</option>
|
|
||||||
<option value="signal">Signal</option>
|
|
||||||
<option value="gotify">Gotify</option>
|
|
||||||
<option value="slack">Slack</option>
|
|
||||||
<option value="pushover">Pushover</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="name" class="form-label">Friendly Name</label>
|
<label for="telegram-chat-id" class="form-label">Chat ID</label>
|
||||||
<input type="text" class="form-control" id="name" required v-model="notification.name">
|
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="text" class="form-control" id="telegram-chat-id" required v-model="notification.telegramChatID">
|
||||||
|
<button class="btn btn-outline-secondary" type="button" @click="autoGetTelegramChatID" v-if="notification.telegramBotToken">Auto Get</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-text">
|
||||||
|
Support Direct Chat / Group / Channel's Chat ID
|
||||||
|
|
||||||
|
<p style="margin-top: 8px;">
|
||||||
|
You can get your chat id by sending message to the bot and go to this url to view the chat_id:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style="margin-top: 8px;">
|
||||||
|
|
||||||
|
<template v-if="notification.telegramBotToken">
|
||||||
|
<a :href="telegramGetUpdatesURL" target="_blank">{{ telegramGetUpdatesURL }}</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
{{ telegramGetUpdatesURL }}
|
||||||
|
</template>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
<template v-if="notification.type === 'telegram'">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="telegram-bot-token" class="form-label">Bot Token</label>
|
|
||||||
<input type="text" class="form-control" id="telegram-bot-token" required v-model="notification.telegramBotToken">
|
|
||||||
<div class="form-text">You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="telegram-chat-id" class="form-label">Chat ID</label>
|
|
||||||
|
|
||||||
<div class="input-group mb-3">
|
|
||||||
<input type="text" class="form-control" id="telegram-chat-id" required v-model="notification.telegramChatID">
|
|
||||||
<button class="btn btn-outline-secondary" type="button" @click="autoGetTelegramChatID" v-if="notification.telegramBotToken">Auto Get</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-text">
|
|
||||||
Support Direct Chat / Group / Channel's Chat ID
|
|
||||||
|
|
||||||
<p style="margin-top: 8px;">
|
|
||||||
You can get your chat id by sending message to the bot and go to this url to view the chat_id:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p style="margin-top: 8px;">
|
|
||||||
|
|
||||||
<template v-if="notification.telegramBotToken">
|
|
||||||
<a :href="telegramGetUpdatesURL" target="_blank">{{ telegramGetUpdatesURL }}</a>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
{{ telegramGetUpdatesURL }}
|
|
||||||
</template>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="notification.type === 'webhook'">
|
<template v-if="notification.type === 'webhook'">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -219,7 +220,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type === 'pushover'">
|
<template v-if="notification.type === 'pushover'">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="pushover-app-token" class="form-label">Application Token<span style="color:red;"><sup>*</sup></span></label>
|
<label for="pushover-app-token" class="form-label">Application Token<span style="color:red;"><sup>*</sup></span></label>
|
||||||
@ -269,6 +270,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="notification.type === 'apprise'">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="gotify-application-token" class="form-label">Apprise URL</label>
|
||||||
|
<input type="text" class="form-control" id="gotify-application-token" required v-model="notification.appriseURL">
|
||||||
|
<div class="form-text">
|
||||||
|
<p>Example: twilio://AccountSid:AuthToken@FromPhoneNo</p>
|
||||||
|
<p>
|
||||||
|
Read more: <a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<p>
|
||||||
|
Status:
|
||||||
|
<span class="text-primary" v-if="appriseInstalled">Apprise is installed</span>
|
||||||
|
<span class="text-danger" v-else>Apprise is not installed. <a href="https://github.com/caronc/apprise">Read more</a></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-danger" @click="deleteConfirm" :disabled="processing" v-if="id">Delete</button>
|
<button type="button" class="btn btn-danger" @click="deleteConfirm" :disabled="processing" v-if="id">Delete</button>
|
||||||
@ -307,17 +331,15 @@ export default {
|
|||||||
type: null,
|
type: null,
|
||||||
gotifyPriority: 8
|
gotifyPriority: 8
|
||||||
},
|
},
|
||||||
|
appriseInstalled: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.modal = new Modal(this.$refs.modal)
|
this.modal = new Modal(this.$refs.modal)
|
||||||
|
|
||||||
// TODO: for edit
|
this.$root.getSocket().emit("checkApprise", (installed) => {
|
||||||
this.$root.getSocket().emit("getSettings", "notification", (data) => {
|
this.appriseInstalled = installed;
|
||||||
// this.notification = data
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user