aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/services/UptimeKuma.vue152
1 files changed, 152 insertions, 0 deletions
diff --git a/src/components/services/UptimeKuma.vue b/src/components/services/UptimeKuma.vue
new file mode 100644
index 0000000..5dd75f3
--- /dev/null
+++ b/src/components/services/UptimeKuma.vue
@@ -0,0 +1,152 @@
1<template>
2 <Generic :item="item">
3 <template #content>
4 <p class="title is-4">{{ item.name }}</p>
5 <p class="subtitle is-6">
6 <template v-if="item.subtitle">
7 {{ item.subtitle }}
8 </template>
9 <template v-else-if="status">
10 {{ statusMessage }}
11 </template>
12 </p>
13 </template>
14 <template #indicator>
15 <div v-if="status" class="status" :class="status">
16 {{ uptime }}&percnt;
17 </div>
18 </template>
19 </Generic>
20</template>
21
22<script>
23import service from "@/mixins/service.js";
24import Generic from "./Generic.vue";
25
26export default {
27 name: "UptimeKuma",
28 mixins: [service],
29 props: {
30 item: Object,
31 },
32 components: {
33 Generic,
34 },
35 data: () => ({
36 incident: null,
37 heartbeat: null,
38 }),
39 computed: {
40 dashboard: function () {
41 return this.item.slug ? this.item.slug : "default";
42 },
43 status: function () {
44 if (!this.incident) {
45 return "";
46 }
47 return this.incident.incident == null ? this.pageStatus : "bad";
48 },
49 lastHeartBeatList: function () {
50 let result = {};
51
52 for (let id in this.heartbeat.heartbeatList) {
53 let index = this.heartbeat.heartbeatList[id].length - 1;
54 result[id] = this.heartbeat.heartbeatList[id][index];
55 }
56
57 return result;
58 },
59 pageStatus: function () {
60 if (!this.heartbeat) {
61 return "";
62 }
63 if (Object.keys(this.heartbeat.heartbeatList).length === 0) {
64 return "";
65 }
66 let result = "good";
67 let hasUp = false;
68 for (let id in this.lastHeartBeatList) {
69 let beat = this.lastHeartBeatList[id];
70 if (beat.status == 1) {
71 hasUp = true;
72 } else {
73 result = "warn";
74 }
75 }
76 if (!hasUp) {
77 result = "bad";
78 }
79 return result;
80 },
81 statusMessage: function () {
82 if (!this.incident) {
83 return "";
84 }
85 if (this.incident.incident) {
86 return this.incident.incident.title;
87 }
88 return this.pageStatus == "warn"
89 ? "Partially Degraded Service"
90 : "All Systems Operational";
91 },
92 uptime: function () {
93 if (!this.heartbeat) {
94 return 0;
95 }
96 const data = Object.values(this.heartbeat.uptimeList);
97 const percent = data.reduce((a, b) => a + b, 0) / data.length || 0;
98 return (percent * 100).toFixed(1);
99 },
100 },
101 created() {
102 this.item.url = `${this.item.url}/status/${this.dashboard}`;
103 this.fetchStatus();
104 },
105 methods: {
106 fetchStatus: function () {
107 this.fetch(`/api/status-page/${this.dashboard}?cachebust=${Date.now()}`)
108 .catch((e) => console.error(e))
109 .then((resp) => (this.incident = resp));
110
111 this.fetch(`/api/status-page/heartbeat/${this.dashboard}?cachebust=${Date.now()}`)
112 .catch((e) => console.error(e))
113 .then((resp) => (this.heartbeat = resp));
114 },
115 },
116};
117</script>
118
119<style scoped lang="scss">
120.status {
121 font-size: 0.8rem;
122 color: var(--text-title);
123
124 &.good:before {
125 background-color: #94e185;
126 border-color: #78d965;
127 box-shadow: 0 0 5px 1px #94e185;
128 }
129
130 &.warn:before {
131 background-color: #f8a306;
132 border-color: #e1b35e;
133 box-shadow: 0 0 5px 1px #f8a306;
134 }
135
136 &.bad:before {
137 background-color: #c9404d;
138 border-color: #c42c3b;
139 box-shadow: 0 0 5px 1px #c9404d;
140 }
141
142 &:before {
143 content: " ";
144 display: inline-block;
145 width: 7px;
146 height: 7px;
147 margin-right: 10px;
148 border: 1px solid #000;
149 border-radius: 7px;
150 }
151}
152</style>