[Status Page] WIP: Checkpoint

This commit is contained in:
LouisLam 2021-09-11 23:43:07 +08:00
parent 3e25f0e9d9
commit 934685637a
8 changed files with 123 additions and 75 deletions

View File

@ -0,0 +1,6 @@
BEGIN TRANSACTION;
alter table monitor
add public BOOLEAN default 0 not null;
COMMIT;

View File

@ -30,6 +30,7 @@ class Database {
static patchList = { static patchList = {
"patch-setting-value-type.sql": true, "patch-setting-value-type.sql": true,
"patch-improve-performance.sql": true, "patch-improve-performance.sql": true,
"patch-monitor-public.sql": true,
} }
/** /**

View File

@ -165,9 +165,36 @@ let indexHTML = fs.readFileSync("./dist/index.html").toString();
response.json(config); response.json(config);
}); });
// Status Page Polling Data // Status Page - Monitor List
app.get("/api/status-page", async (_request, response) => { app.get("/api/status-page/monitor-list", async (_request, response) => {
allowDevAllOrigin(response); allowDevAllOrigin(response);
const monitorList = {};
let list = await R.find("monitor", " public = 1 ORDER BY weight DESC, name ", [
]);
for (let monitor of list) {
monitorList[monitor.id] = await monitor.toJSON();
}
response.json(monitorList);
});
// Status Page Polling Data
app.get("/api/status-page/heartbeat", async (_request, response) => {
allowDevAllOrigin(response);
const monitorList = {};
let list = await R.find("", " ", [
])
for (let monitor of list) {
monitorList[monitor.id] = await monitor.toJSON();
}
response.json({
monitorList: monitorList,
});
}); });
// Universal Route Handler, must be at the end of all express route. // Universal Route Handler, must be at the end of all express route.

View File

@ -255,6 +255,18 @@ h2 {
background-color: $dark-bg; background-color: $dark-bg;
} }
.monitor-list {
.item {
&:hover {
background-color: $dark-bg2;
}
&.active {
background-color: $dark-bg2;
}
}
}
@media (max-width: 550px) { @media (max-width: 550px) {
.table-shadow-box { .table-shadow-box {
tbody { tbody {
@ -288,3 +300,39 @@ h2 {
transform: translateY(50px); transform: translateY(50px);
opacity: 0; opacity: 0;
} }
.monitor-list {
&.scrollbar {
min-height: calc(100vh - 240px);
max-height: calc(100vh - 30px);
overflow-y: auto;
position: sticky;
top: 10px;
}
.item {
display: block;
text-decoration: none;
padding: 13px 15px 10px 15px;
border-radius: 10px;
transition: all ease-in-out 0.15s;
&.disabled {
opacity: 0.3;
}
.info {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&:hover {
background-color: $highlight-white;
}
&.active {
background-color: #cdf8f4;
}
}
}

View File

@ -25,6 +25,10 @@ export default {
type: Number, type: Number,
required: true, required: true,
}, },
heartbeatList: {
type: Array,
default: null,
}
}, },
data() { data() {
return { return {
@ -38,8 +42,15 @@ export default {
}, },
computed: { computed: {
/**
* If heartbeatList is null, get it from $root.heartbeatList
*/
beatList() { beatList() {
return this.$root.heartbeatList[this.monitorId] if (this.heartbeatList === null) {
return this.$root.heartbeatList[this.monitorId];
} else {
return this.heartbeatList;
}
}, },
shortBeatList() { shortBeatList() {
@ -118,8 +129,10 @@ export default {
window.removeEventListener("resize", this.resize); window.removeEventListener("resize", this.resize);
}, },
beforeMount() { beforeMount() {
if (! (this.monitorId in this.$root.heartbeatList)) { if (this.heartbeatList === null) {
this.$root.heartbeatList[this.monitorId] = []; if (! (this.monitorId in this.$root.heartbeatList)) {
this.$root.heartbeatList[this.monitorId] = [];
}
} }
}, },

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="shadow-box list mb-3" :class="{ scrollbar: scrollbar }"> <div class="shadow-box monitor-list mb-3" :class="{ scrollbar: scrollbar }">
<div v-if="Object.keys($root.monitorList).length === 0" class="text-center mt-3"> <div v-if="Object.keys($root.monitorList).length === 0" class="text-center mt-3">
{{ $t("No Monitors, please") }} <router-link to="/add">{{ $t("add one") }}</router-link> {{ $t("No Monitors, please") }} <router-link to="/add">{{ $t("add one") }}</router-link>
</div> </div>
@ -87,56 +87,6 @@ export default {
padding-right: 5px !important; padding-right: 5px !important;
} }
.list {
&.scrollbar {
min-height: calc(100vh - 240px);
max-height: calc(100vh - 30px);
overflow-y: auto;
position: sticky;
top: 10px;
}
.item {
display: block;
text-decoration: none;
padding: 13px 15px 10px 15px;
border-radius: 10px;
transition: all ease-in-out 0.15s;
&.disabled {
opacity: 0.3;
}
.info {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&:hover {
background-color: $highlight-white;
}
&.active {
background-color: #cdf8f4;
}
}
}
.dark {
.list {
.item {
&:hover {
background-color: $dark-bg2;
}
&.active {
background-color: $dark-bg2;
}
}
}
}
.monitorItem { .monitorItem {
width: 100%; width: 100%;
} }

View File

@ -18,7 +18,7 @@
</a> </a>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
<li class="nav-item"> <li class="nav-item me-2">
<router-link to="/status-page" class="nav-link status-page"> <router-link to="/status-page" class="nav-link status-page">
<font-awesome-icon icon="stream" /> {{ $t("Status Page") }} <font-awesome-icon icon="stream" /> {{ $t("Status Page") }}
</router-link> </router-link>

View File

@ -18,30 +18,24 @@
<font-awesome-icon icon="check-circle" class="ok" /> All Systems Operational <font-awesome-icon icon="check-circle" class="ok" /> All Systems Operational
</div> </div>
<div class="shadow-box list mt-4" :class="{ scrollbar: scrollbar }"> <div class="shadow-box monitor-list mt-4">
<div v-if="Object.keys($root.monitorList).length === 0" class="text-center mt-3"> <div v-if="Object.keys(monitorList).length === 0" class="text-center my-3">
{{ $t("No Monitors, please") }} <router-link to="/add">{{ $t("add one") }}</router-link> {{ $t("No Monitors") }}
</div> </div>
<router-link v-for="(item, index) in sortedMonitorList" :key="index" :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }"> <div v-for="(item, index) in monitorList" :key="index" class="item">
<div class="row"> <div class="row">
<div class="col-6 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }"> <div class="col-6 col-md-8 small-padding">
<div class="info"> <div class="info">
<Uptime :monitor="item" type="24" :pill="true" /> <Uptime :monitor="item" type="24" :pill="true" />
{{ item.name }} {{ item.name }}
</div> </div>
</div> </div>
<div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-6 col-md-4"> <div :key="$root.userHeartbeatBar" class="col-6 col-md-4">
<HeartbeatBar size="small" :monitor-id="item.id" /> <HeartbeatBar size="small" :monitor-id="item.id" />
</div> </div>
</div> </div>
</div>
<div v-if="$root.userHeartbeatBar == 'bottom'" class="row">
<div class="col-12">
<HeartbeatBar size="small" :monitor-id="item.id" />
</div>
</div>
</router-link>
</div> </div>
<footer class="mt-4"> <footer class="mt-4">
@ -51,9 +45,13 @@
</template> </template>
<script> <script>
import { useToast } from "vue-toastification" import { useToast } from "vue-toastification";
import axios from "axios";
const toast = useToast(); const toast = useToast();
import axios from "axios";
import HeartbeatBar from "../components/HeartbeatBar.vue";
import Uptime from "../components/Uptime.vue";
const env = process.env.NODE_ENV || "production"; const env = process.env.NODE_ENV || "production";
// change the axios base url for development // change the axios base url for development
@ -62,10 +60,15 @@ if (env === "development" || localStorage.dev === "dev") {
} }
export default { export default {
components: {
HeartbeatBar,
Uptime,
},
data() { data() {
return { return {
hasToken: false, hasToken: false,
config: {}, config: {},
monitorList: {},
} }
}, },
computed: { computed: {
@ -76,13 +79,13 @@ export default {
}, },
async created() { async created() {
this.hasToken = ("token" in localStorage); this.hasToken = ("token" in localStorage);
this.config = await axios.get("/api/status-page/config"); this.config = (await axios.get("/api/status-page/config")).data;
// Set Theme // Set Theme
this.$root.statusPageTheme = this.config.statusPageTheme; this.$root.statusPageTheme = this.config.statusPageTheme;
}, },
mounted() { async mounted() {
this.monitorList = (await axios.get("/api/status-page/monitor-list")).data;
}, },
methods: { methods: {
edit() { edit() {