Skip to content

Commit 308005c

Browse files
committed
feat: ✨ let users update alert settings on a system level
1 parent 10328d3 commit 308005c

12 files changed

Lines changed: 168 additions & 7 deletions

File tree

web/components/SystemOverview.vue

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
<template>
22
<UCard>
33
<template #header
4-
><div class="flex justify-between">
4+
><div class="flex flex-wrap justify-between">
55
<span class="capitalize"> {{ name }}</span>
6-
<UButton :to="link">View Details</UButton>
6+
<div class="flex items-center gap-4 flex-wrap">
7+
<span class="flex items-center gap-1">
8+
Alerts
9+
<UToggle
10+
@change="(value) => $emit('changeAlertToggle', value)"
11+
v-model="enableAlert"
12+
/>
13+
</span>
14+
<UButton :to="link">View Details</UButton>
15+
</div>
716
</div>
817
</template>
918

@@ -20,9 +29,14 @@
2029
</template>
2130

2231
<script lang="ts" setup>
32+
const emits = defineEmits<{
33+
(e: "changeAlertToggle", value: boolean): void;
34+
}>();
35+
2336
const props = defineProps<{
2437
name: string;
2538
link: string;
39+
enableAlert?: boolean;
2640
metrics: {
2741
timestamp: number;
2842
memory: {
@@ -46,6 +60,8 @@ const props = defineProps<{
4660
};
4761
}>();
4862
63+
const enableAlert = ref(props.enableAlert ?? true);
64+
4965
const rows = computed(() => {
5066
return {
5167
"Last Update": new Date(props.metrics.timestamp).toLocaleString(),

web/layouts/default.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
<template>
2+
<VitePwaManifest />
23
<UContainer>
34
<MetricsTitle />
45
<slot />
6+
7+
<UNotifications />
58
</UContainer>
69
</template>
710

web/nuxt.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export default defineNuxtConfig({
1919
driver: "fs",
2020
base: "./data/serverState",
2121
},
22+
systemConfigs: {
23+
driver: "fs",
24+
base: "./data/systemConfigs",
25+
},
2226
},
2327
experimental: {
2428
tasks: true,

web/pages/index.vue

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
v-if="systemMetricsStreams[system]?.at(-1)"
1111
:name="system"
1212
:link="`/systems/${system}`"
13+
:enable-alert="systemConfigs[system]?.enableAlerts"
1314
:metrics="systemMetricsStreams[system].at(-1)!"
15+
@change-alert-toggle="
16+
(value) => handleAlertToggleChange(value, system)
17+
"
1418
/>
1519
<USkeleton class="h-60 w-full" v-else />
1620
</div>
@@ -25,7 +29,14 @@ import type { Metrics } from "../../shared/types";
2529
const isLoading = ref(false);
2630
2731
const systems = ref([]);
28-
32+
const systemConfigs = ref<
33+
Record<
34+
string,
35+
{
36+
enableAlerts: boolean;
37+
}
38+
>
39+
>({});
2940
const systemMetricsStreams = reactive<Record<string, Metrics[]>>({});
3041
3142
function setupMetricsStream() {
@@ -44,17 +55,47 @@ async function loadSystems() {
4455
systems.value = (await $fetch("/api/sytems")) ?? [];
4556
}
4657
58+
async function loadSystemConfigs() {
59+
const savedSystemConfigs = await $fetch("/api/system-configs");
60+
61+
if (savedSystemConfigs) {
62+
systemConfigs.value = savedSystemConfigs;
63+
}
64+
}
65+
4766
onMounted(async () => {
4867
try {
4968
isLoading.value = true;
50-
await loadSystems();
69+
await Promise.allSettled([loadSystems(), loadSystemConfigs()]);
5170
setupMetricsStream();
5271
} catch (error) {
5372
console.log({ error });
5473
} finally {
5574
isLoading.value = false;
5675
}
5776
});
77+
78+
async function handleAlertToggleChange(value: boolean, system: string) {
79+
try {
80+
await $fetch("/api/system-config", {
81+
method: "PUT",
82+
body: {
83+
system,
84+
enableAlerts: value,
85+
},
86+
});
87+
useToast().add({
88+
title: "Successfully updated system alert config",
89+
color: "green",
90+
});
91+
} catch (error) {
92+
console.log({ error });
93+
useToast().add({
94+
title: "Error updating system alert config",
95+
color: "red",
96+
});
97+
}
98+
}
5899
</script>
59100

60101
<style></style>

web/pages/systems/[name].vue

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<template>
2-
<VitePwaManifest />
32
<UBreadcrumb :links="breadcrumbLinks" />
43

54
<div
@@ -57,7 +56,6 @@
5756
:rows="processesStats"
5857
/>
5958
</UCard>
60-
<UNotifications />
6159
</template>
6260

6361
<script setup lang="ts">
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export default defineEventHandler(async (event) => {
2+
const { system } = getQuery(event);
3+
4+
if (!system || typeof system !== "string") {
5+
throw createError({
6+
statusCode: 400,
7+
message: "Invalid system",
8+
});
9+
}
10+
11+
return useStorage("systemConfigs").getItem<{
12+
enableAlerts: boolean;
13+
}>(system);
14+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { z } from "zod";
2+
3+
const systemConfigEditSchema = z.object({
4+
enableAlerts: z.boolean().optional(),
5+
system: z.string(),
6+
});
7+
8+
export default defineEventHandler(async (event) => {
9+
const body = await readValidatedBody(event, systemConfigEditSchema.safeParse);
10+
11+
if (body.error) {
12+
throw createError({
13+
statusCode: 400,
14+
data: body.error,
15+
message: "Invalid system config edit options",
16+
});
17+
}
18+
19+
const storage = useStorage("systemConfigs");
20+
21+
const { system, ...updatedConfig } = body.data;
22+
23+
let systemConfig = (await storage.getItem<{ enableAlerts: boolean }>(
24+
system
25+
)) ?? {
26+
enableAlerts: false,
27+
};
28+
29+
systemConfig = {
30+
...systemConfig,
31+
...updatedConfig,
32+
};
33+
34+
try {
35+
await storage.setItem(system, systemConfig);
36+
return {
37+
statusCode: 200,
38+
};
39+
} catch (error) {
40+
throw createError({
41+
statusCode: 500,
42+
message: "Error updating system config",
43+
data: error,
44+
});
45+
}
46+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export default defineEventHandler(async (event) => {
2+
const storage = useStorage("systemConfigs");
3+
4+
const keys = await storage.getKeys();
5+
6+
const systemConfigs: Record<string, { enableAlerts: boolean }> = {};
7+
8+
await Promise.allSettled(
9+
keys.map((k) => {
10+
return storage
11+
.getItem<{
12+
enableAlerts: boolean;
13+
}>(k)
14+
.then((config) => {
15+
if (config) {
16+
systemConfigs[k] = config;
17+
}
18+
});
19+
})
20+
);
21+
22+
return systemConfigs;
23+
});

web/server/tasks/metrics/battery.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ async function batteryCheckForSystem(system: string) {
3737
runTask("server:send-alert", {
3838
payload: {
3939
message: `Warning: Battery % lower than ${batteryAlertThreshold}% for ${system}`,
40+
system,
4041
},
4142
});
4243
}

web/server/tasks/metrics/healthcheck.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ async function healthCheckForSystem(system: string) {
6565
runTask("server:send-alert", {
6666
payload: {
6767
message: `No new metrics found, ${system} might be down`,
68+
system,
6869
},
6970
});
7071
}
@@ -77,6 +78,7 @@ async function healthCheckForSystem(system: string) {
7778
runTask("server:send-alert", {
7879
payload: {
7980
message: `Metrics coming in again, ${system} might be up again`,
81+
system,
8082
},
8183
});
8284
}

0 commit comments

Comments
 (0)