Merge branch 'master' into redirects&status

This commit is contained in:
LouisLam 2021-08-08 14:48:00 +08:00
commit 3005585c0f
17 changed files with 398 additions and 122 deletions

View file

@ -62,9 +62,6 @@ module.exports = {
exceptAfterSingleLine: true, exceptAfterSingleLine: true,
}], }],
"no-unneeded-ternary": "error", "no-unneeded-ternary": "error",
"no-else-return": ["error", {
"allowElseIf": false,
}],
"array-bracket-newline": ["error", "consistent"], "array-bracket-newline": ["error", "consistent"],
"eol-last": ["error", "always"], "eol-last": ["error", "always"],
//'prefer-template': 'error', //'prefer-template': 'error',

View file

@ -132,7 +132,9 @@ class Monitor extends BeanModel {
try { try {
await this.updateTlsInfo(checkCertificate(res)); await this.updateTlsInfo(checkCertificate(res));
} catch (e) { } catch (e) {
console.error(e.message) if (e.message !== "No TLS certificate in response") {
console.error(e.message)
}
} }
} }

View file

@ -84,40 +84,78 @@ class Notification {
} else if (notification.type === "discord") { } else if (notification.type === "discord") {
try { try {
const discordDisplayName = notification.discordUsername || "Uptime Kuma";
// If heartbeatJSON is null, assume we're testing. // If heartbeatJSON is null, assume we're testing.
if (heartbeatJSON == null) { if (heartbeatJSON == null) {
let data = { let discordtestdata = {
username: "Uptime-Kuma", username: discordDisplayName,
content: msg, content: msg,
} }
await axios.post(notification.discordWebhookUrl, data) await axios.post(notification.discordWebhookUrl, discordtestdata)
return okMsg; return okMsg;
} }
// If heartbeatJSON is not null, we go into the normal alerting loop. // If heartbeatJSON is not null, we go into the normal alerting loop.
if (heartbeatJSON["status"] == 0) { if (heartbeatJSON["status"] == 0) {
var alertColor = "16711680"; let discorddowndata = {
username: discordDisplayName,
embeds: [{
title: "❌ One of your services went down. ❌",
color: 16711680,
timestamp: heartbeatJSON["time"],
fields: [
{
name: "Service Name",
value: monitorJSON["name"],
},
{
name: "Service URL",
value: monitorJSON["url"],
},
{
name: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
name: "Error",
value: heartbeatJSON["msg"],
},
],
}],
}
await axios.post(notification.discordWebhookUrl, discorddowndata)
return okMsg;
} else if (heartbeatJSON["status"] == 1) { } else if (heartbeatJSON["status"] == 1) {
var alertColor = "65280"; let discordupdata = {
username: discordDisplayName,
embeds: [{
title: "✅ Your service " + monitorJSON["name"] + " is up! ✅",
color: 65280,
timestamp: heartbeatJSON["time"],
fields: [
{
name: "Service Name",
value: monitorJSON["name"],
},
{
name: "Service URL",
value: "[Visit Service](" + monitorJSON["url"] + ")",
},
{
name: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
name: "Ping",
value: heartbeatJSON["ping"] + "ms",
},
],
}],
}
await axios.post(notification.discordWebhookUrl, discordupdata)
return okMsg;
} }
let data = {
username: "Uptime-Kuma",
embeds: [{
title: "Uptime-Kuma Alert",
color: alertColor,
fields: [
{
name: "Time (UTC)",
value: heartbeatJSON["time"],
},
{
name: "Message",
value: msg,
},
],
}],
}
await axios.post(notification.discordWebhookUrl, data)
return okMsg;
} catch (error) { } catch (error) {
throwGeneralAxiosError(error) throwGeneralAxiosError(error)
} }

View file

@ -5,6 +5,26 @@
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;
} }
.modal {
backdrop-filter: blur(3px);
}
.modal-content {
border-radius: 1rem;
box-shadow: 0 15px 70px rgba(0, 0, 0, .1);
.dark & {
box-shadow: 0 15px 70px rgb(0 0 0);
background-color: $dark-bg;
}
}
.VuePagination__count {
font-size: 13px;
text-align: center;
}
.shadow-box { .shadow-box {
overflow: hidden; overflow: hidden;
box-shadow: 0 15px 70px rgba(0, 0, 0, .1); box-shadow: 0 15px 70px rgba(0, 0, 0, .1);
@ -29,10 +49,87 @@
background-color: $highlight; background-color: $highlight;
border-color: $highlight; border-color: $highlight;
} }
.dark & {
color: $dark-font-color2;
}
} }
.modal-content {
border-radius: 1rem;
backdrop-filter: blur(3px);
}
// Dark Theme override here
.dark {
background-color: #090C10;
color: $dark-font-color;
.shadow-box {
background-color: $dark-bg;
}
.form-check-input {
background-color: $dark-bg2;
}
.form-switch .form-check-input {
background-color: #131a21;
}
a,
.table,
.nav-link {
color: $dark-font-color;
}
.form-control,
.form-control:focus,
.form-select,
.form-select:focus {
color: $dark-font-color;
background-color: $dark-bg2;
}
.form-control, .form-select {
border-color: $dark-border-color;
}
.table-hover > tbody > tr:hover {
--bs-table-accent-bg: #070A10;
color: $dark-font-color;
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: $dark-font-color2;
}
.bg-primary {
color: $dark-font-color2;
}
.btn-secondary {
color: white;
}
.btn-close {
opacity: 1;
}
.modal-header {
border-color: $dark-bg;
}
.modal-footer {
border-color: $dark-bg;
}
// Pagination
.page-item.disabled .page-link {
background-color: $dark-bg;
border-color: $dark-border-color;
}
.page-link {
background-color: $dark-bg;
border-color: $dark-border-color;
color: $dark-font-color;
}
}

View file

@ -6,3 +6,9 @@ $border-radius: 50rem;
$highlight: #7ce8a4; $highlight: #7ce8a4;
$highlight-white: #e7faec; $highlight-white: #e7faec;
$dark-font-color: #b1b8c0;
$dark-font-color2: #020b05;
$dark-bg: #0D1117;
$dark-bg2: #070A10;
$dark-border-color: #1d2634;

View file

@ -133,7 +133,7 @@ export default {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
@import "../assets/vars.scss"; @import "../assets/vars.scss";
.wrap { .wrap {
@ -150,6 +150,10 @@ export default {
&.empty { &.empty {
background-color: aliceblue; background-color: aliceblue;
.dark & {
background-color: #d0d3d5;
}
} }
&.down { &.down {
@ -168,4 +172,10 @@ export default {
} }
} }
.dark {
.hp-bar-big .beat.empty{
background-color: #848484;
}
}
</style> </style>

View file

@ -143,6 +143,11 @@
<div class="mb-3"> <div class="mb-3">
<label for="discord-webhook-url" class="form-label">Discord Webhook URL</label> <label for="discord-webhook-url" class="form-label">Discord Webhook URL</label>
<input id="discord-webhook-url" v-model="notification.discordWebhookUrl" type="text" class="form-control" required autocomplete="false"> <input id="discord-webhook-url" v-model="notification.discordWebhookUrl" type="text" class="form-control" required autocomplete="false">
</div>
<div class="mb-3">
<label for="discord-username" class="form-label">Bot Display Name</label>
<input id="discord-username" v-model="notification.discordUsername" type="text" class="form-control" autocomplete="false" :placeholder="$root.appName">
<div class="form-text"> <div class="form-text">
You can get this by going to Server Settings -> Integrations -> Create Webhook You can get this by going to Server Settings -> Integrations -> Create Webhook
</div> </div>
@ -233,17 +238,17 @@
<template v-if="notification.type === 'pushy'"> <template v-if="notification.type === 'pushy'">
<div class="mb-3"> <div class="mb-3">
<label for="pushy-app-token" class="form-label">API_KEY</label> <label for="pushy-app-token" class="form-label">API_KEY</label>
<input type="text" class="form-control" id="pushy-app-token" required v-model="notification.pushyAPIKey"> <input id="pushy-app-token" v-model="notification.pushyAPIKey" type="text" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="pushy-user-key" class="form-label">USER_TOKEN</label> <label for="pushy-user-key" class="form-label">USER_TOKEN</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<input type="text" class="form-control" id="pushy-user-key" required v-model="notification.pushyToken"> <input id="pushy-user-key" v-model="notification.pushyToken" type="text" class="form-control" required>
</div> </div>
</div> </div>
<p style="margin-top: 8px;"> <p style="margin-top: 8px;">
More info on: <a href="https://pushy.me/docs/api/send-notifications" target="_blank">https://pushy.me/docs/api/send-notifications</a> More info on: <a href="https://pushy.me/docs/api/send-notifications" target="_blank">https://pushy.me/docs/api/send-notifications</a>
</p> </p>
</template> </template>
@ -320,7 +325,7 @@
<p> <p>
Status: Status:
<span v-if="appriseInstalled" class="text-primary">Apprise is installed</span> <span v-if="appriseInstalled" class="text-primary">Apprise is installed</span>
<span v-else class="text-danger">Apprise is not installed. <a href="https://github.com/caronc/apprise">Read more</a></span> <span v-else class="text-danger">Apprise is not installed. <a href="https://github.com/caronc/apprise" target="_blank">Read more</a></span>
</p> </p>
</div> </div>
</template> </template>
@ -334,7 +339,6 @@
</div> </div>
</div> </div>
</template> </template>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button v-if="id" type="button" class="btn btn-danger" :disabled="processing" @click="deleteConfirm"> <button v-if="id" type="button" class="btn btn-danger" :disabled="processing" @click="deleteConfirm">
@ -508,3 +512,13 @@ export default {
}, },
} }
</script> </script>
<style lang="scss" scoped>
@import "../assets/vars.scss";
.dark {
.modal-dialog .form-text, .modal-dialog p {
color: $dark-font-color;
}
}
</style>

View file

@ -1,76 +1,78 @@
<template> <template>
<div v-if="! $root.socket.connected && ! $root.socket.firstConnect" class="lost-connection"> <div :class="$root.theme">
<div class="container-fluid"> <div v-if="! $root.socket.connected && ! $root.socket.firstConnect" class="lost-connection">
{{ $root.connectionErrorMsg }} <div class="container-fluid">
{{ $root.connectionErrorMsg }}
</div>
</div> </div>
<!-- Desktop header -->
<header v-if="! $root.isMobile" class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom">
<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" alt="Logo" />
<span class="fs-4 title">Uptime Kuma</span>
</router-link>
<ul class="nav nav-pills">
<li class="nav-item">
<router-link to="/dashboard" class="nav-link">
<font-awesome-icon icon="tachometer-alt" /> Dashboard
</router-link>
</li>
<li class="nav-item">
<router-link to="/settings" class="nav-link">
<font-awesome-icon icon="cog" /> Settings
</router-link>
</li>
</ul>
</header>
<!-- Mobile header -->
<header v-else class="d-flex flex-wrap justify-content-center pt-2 pb-2 mb-3">
<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" />
<span class="fs-4 title ms-2">Uptime Kuma</span>
</router-link>
</header>
<main>
<!-- Add :key to disable vue router re-use the same component -->
<router-view v-if="$root.loggedIn" :key="$route.fullPath" />
<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" rel="noopener">Check Update On GitHub</a>
</div>
</footer>
<!-- Mobile Only -->
<div v-if="$root.isMobile" style="width: 100%;height: 60px;" />
<nav v-if="$root.isMobile" class="bottom-nav">
<router-link to="/dashboard" class="nav-link" @click="$root.cancelActiveList">
<div><font-awesome-icon icon="tachometer-alt" /></div>
Dashboard
</router-link>
<a href="#" :class=" { 'router-link-exact-active' : $root.showListMobile } " @click="$root.showListMobile = ! $root.showListMobile">
<div><font-awesome-icon icon="list" /></div>
List
</a>
<router-link to="/add" class="nav-link" @click="$root.cancelActiveList">
<div><font-awesome-icon icon="plus" /></div>
Add
</router-link>
<router-link to="/settings" class="nav-link" @click="$root.cancelActiveList">
<div><font-awesome-icon icon="cog" /></div>
Settings
</router-link>
</nav>
</div> </div>
<!-- Desktop header -->
<header v-if="! $root.isMobile" class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom">
<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" alt="Logo" />
<span class="fs-4 title">Uptime Kuma</span>
</router-link>
<ul class="nav nav-pills">
<li class="nav-item">
<router-link to="/dashboard" class="nav-link">
<font-awesome-icon icon="tachometer-alt" /> Dashboard
</router-link>
</li>
<li class="nav-item">
<router-link to="/settings" class="nav-link">
<font-awesome-icon icon="cog" /> Settings
</router-link>
</li>
</ul>
</header>
<!-- Mobile header -->
<header v-else class="d-flex flex-wrap justify-content-center mt-3 mb-3">
<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" />
<span class="fs-4 title ms-2">Uptime Kuma</span>
</router-link>
</header>
<main>
<!-- Add :key to disable vue router re-use the same component -->
<router-view v-if="$root.loggedIn" :key="$route.fullPath" />
<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" rel="noopener">Check Update On GitHub</a>
</div>
</footer>
<!-- Mobile Only -->
<div v-if="$root.isMobile" style="width: 100%;height: 60px;" />
<nav v-if="$root.isMobile" class="bottom-nav">
<router-link to="/dashboard" class="nav-link" @click="$root.cancelActiveList">
<div><font-awesome-icon icon="tachometer-alt" /></div>
Dashboard
</router-link>
<a href="#" :class=" { 'router-link-exact-active' : $root.showListMobile } " @click="$root.showListMobile = ! $root.showListMobile">
<div><font-awesome-icon icon="list" /></div>
List
</a>
<router-link to="/add" class="nav-link" @click="$root.cancelActiveList">
<div><font-awesome-icon icon="plus" /></div>
Add
</router-link>
<router-link to="/settings" class="nav-link" @click="$root.cancelActiveList">
<div><font-awesome-icon icon="cog" /></div>
Settings
</router-link>
</nav>
</template> </template>
<script> <script>
@ -103,7 +105,7 @@ export default {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
@import "../assets/vars.scss"; @import "../assets/vars.scss";
.bottom-nav { .bottom-nav {
@ -159,9 +161,24 @@ footer {
color: #AAA; color: #AAA;
font-size: 13px; font-size: 13px;
margin-top: 10px; margin-top: 10px;
margin-bottom: 30px; padding-bottom: 30px;
margin-left: 10px; margin-left: 10px;
text-align: center; text-align: center;
} }
.dark {
header {
background-color: #161B22;
border-bottom-color: #161B22 !important;
span {
color: #F0F6FC;
}
}
.bottom-nav {
background-color: $dark-bg;
}
}
</style> </style>

View file

@ -9,12 +9,14 @@ import { FontAwesomeIcon } from "./icon.js";
import EmptyLayout from "./layouts/EmptyLayout.vue"; import EmptyLayout from "./layouts/EmptyLayout.vue";
import Layout from "./layouts/Layout.vue"; import Layout from "./layouts/Layout.vue";
import socket from "./mixins/socket"; import socket from "./mixins/socket";
import theme from "./mixins/theme";
import Dashboard from "./pages/Dashboard.vue"; import Dashboard from "./pages/Dashboard.vue";
import DashboardHome from "./pages/DashboardHome.vue"; import DashboardHome from "./pages/DashboardHome.vue";
import Details from "./pages/Details.vue"; import Details from "./pages/Details.vue";
import EditMonitor from "./pages/EditMonitor.vue"; import EditMonitor from "./pages/EditMonitor.vue";
import Settings from "./pages/Settings.vue"; import Settings from "./pages/Settings.vue";
import Setup from "./pages/Setup.vue"; import Setup from "./pages/Setup.vue";
import { appName } from "./util.ts";
const routes = [ const routes = [
{ {
@ -76,7 +78,13 @@ const router = createRouter({
const app = createApp({ const app = createApp({
mixins: [ mixins: [
socket, socket,
theme
], ],
data() {
return {
appName: appName
}
},
render: () => h(App), render: () => h(App),
}) })

View file

@ -29,7 +29,7 @@ export default {
notificationList: [], notificationList: [],
windowWidth: window.innerWidth, windowWidth: window.innerWidth,
showListMobile: false, showListMobile: false,
connectionErrorMsg: "Cannot connect to the socket server. Reconnecting..." connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...",
} }
}, },

39
src/mixins/theme.js Normal file
View file

@ -0,0 +1,39 @@
export default {
data() {
return {
system: (window.matchMedia("(prefers-color-scheme: dark)")) ? "dark" : "light",
userTheme: localStorage.theme,
};
},
mounted() {
// Default Light
if (! this.userTheme) {
this.userTheme = "light";
}
document.body.classList.add(this.theme);
},
computed: {
theme() {
if (this.userTheme === "auto") {
return this.system;
}
return this.userTheme;
}
},
watch: {
userTheme(to, from) {
localStorage.theme = to;
},
theme(to, from) {
document.body.classList.remove(from);
document.body.classList.add(this.theme);
}
}
}

View file

@ -89,7 +89,7 @@ export default {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
@import "../assets/vars.scss"; @import "../assets/vars.scss";
.container-fluid { .container-fluid {
@ -133,4 +133,18 @@ export default {
padding-right: 5px !important; padding-right: 5px !important;
} }
.dark {
.list {
.item {
&:hover {
background-color: $dark-bg2;
}
&.active {
background-color: $dark-bg2;
}
}
}
}
</style> </style>

View file

@ -169,7 +169,7 @@ export default {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
@import "../assets/vars"; @import "../assets/vars";
.num { .num {

View file

@ -6,7 +6,7 @@
<span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span> <span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
<span v-if="monitor.type === 'keyword'"> <span v-if="monitor.type === 'keyword'">
<br> <br>
<span>Keyword:</span> <span style="color: black">{{ monitor.keyword }}</span> <span>Keyword:</span> <span class="keyword">{{ monitor.keyword }}</span>
</span> </span>
</p> </p>
@ -352,4 +352,14 @@ table {
margin: 20px 0; margin: 20px 0;
} }
} }
.keyword {
color: black;
}
.dark {
.keyword {
color: $dark-font-color;
}
}
</style> </style>

View file

@ -20,6 +20,19 @@
</select> </select>
</div> </div>
<div class="mb-3">
<div class="btn-group" role="group" aria-label="Basic checkbox toggle button group">
<input id="btncheck1" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="light">
<label class="btn btn-outline-primary" for="btncheck1">Light</label>
<input id="btncheck2" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="dark">
<label class="btn btn-outline-primary" for="btncheck2">Dark</label>
<input id="btncheck3" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="auto">
<label class="btn btn-outline-primary" for="btncheck3">Auto</label>
</div>
</div>
<div> <div>
<button class="btn btn-primary" type="submit"> <button class="btn btn-primary" type="submit">
Save Save
@ -67,7 +80,7 @@
</template> </template>
</div> </div>
<div class="col-md-6"> <div class="notification-list col-md-6">
<div v-if="$root.isMobile" class="mt-3" /> <div v-if="$root.isMobile" class="mt-3" />
<h2>Notifications</h2> <h2>Notifications</h2>
@ -201,8 +214,17 @@ export default {
} }
</script> </script>
<style scoped> <style lang="scss" scoped>
.shadow-box { @import "../assets/vars.scss";
padding: 20px;
.shadow-box {
padding: 20px;
}
.dark {
.list-group-item {
background-color: $dark-bg2;
color: $dark-font-color;
} }
}
</style> </style>

View file

@ -1,6 +1,7 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.PENDING = exports.UP = exports.DOWN = void 0; exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.PENDING = exports.UP = exports.DOWN = exports.appName = void 0;
exports.appName = "Uptime Kuma";
exports.DOWN = 0; exports.DOWN = 0;
exports.UP = 1; exports.UP = 1;
exports.PENDING = 2; exports.PENDING = 2;
@ -28,7 +29,7 @@ function ucfirst(str) {
exports.ucfirst = ucfirst; exports.ucfirst = ucfirst;
function debug(msg) { function debug(msg) {
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {
console.log(msg); console.debug(msg);
} }
} }
exports.debug = debug; exports.debug = debug;

View file

@ -3,6 +3,7 @@
// Frontend uses util.ts // Frontend uses util.ts
// Need to run "tsc" to compile if there are any changes. // Need to run "tsc" to compile if there are any changes.
export const appName = "Uptime Kuma";
export const DOWN = 0; export const DOWN = 0;
export const UP = 1; export const UP = 1;
export const PENDING = 2; export const PENDING = 2;
@ -38,6 +39,6 @@ export function ucfirst(str) {
export function debug(msg) { export function debug(msg) {
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {
console.log(msg) console.debug(msg);
} }
} }