ffffng/frontend/src/views/AdminNodesView.vue
2022-07-07 10:59:41 +02:00

230 lines
7.2 KiB
Vue

<script setup lang="ts">
import {useNodesStore} from "@/stores/nodes";
import {onMounted, ref} from "vue";
import type {EnhancedNode, MAC} from "@/types";
import Pager from "@/components/Pager.vue";
type NodeRedactField = "nickname" | "email" | "token";
type NodeRedactFieldsMap = Partial<Record<NodeRedactField, boolean>>;
type NodesRedactFieldsMap = Partial<Record<MAC, NodeRedactFieldsMap>>;
const nodes = useNodesStore();
const redactFieldsByDefault = ref(true);
const nodesRedactFieldsMap = ref({} as NodesRedactFieldsMap)
async function refresh(page: number): Promise<void> {
await nodes.refresh(page, 50);
}
function redactAllFields(shallRedactFields: boolean): void {
redactFieldsByDefault.value = shallRedactFields;
nodesRedactFieldsMap.value = {};
}
function shallRedactField(node: EnhancedNode, field: NodeRedactField): boolean {
const redactFieldsMap = nodesRedactFieldsMap.value[node.mac];
if (!redactFieldsMap) {
return redactFieldsByDefault.value;
}
const redactField = redactFieldsMap[field];
return redactField === undefined ? redactFieldsByDefault.value : redactField;
}
function setRedactField(node: EnhancedNode, field: NodeRedactField, value: boolean): void {
let redactFieldsMap = nodesRedactFieldsMap.value[node.mac];
if (!redactFieldsMap) {
redactFieldsMap = {};
nodesRedactFieldsMap.value[node.mac] = redactFieldsMap;
}
redactFieldsMap[field] = value;
}
onMounted(async () => await refresh(1));
</script>
<template>
<h2>Knoten</h2>
<div>
<span>Gesamt: {{nodes.getTotalNodes}}</span>
<button
v-if="redactFieldsByDefault"
@click="redactAllFields(false)">
Sensible Daten einblenden
</button>
<button
v-if="!redactFieldsByDefault"
@click="redactAllFields(true)">
Sensible Daten ausblenden
</button>
</div>
<Pager
:page="nodes.getPage"
:itemsPerPage="nodes.getNodesPerPage"
:totalItems="nodes.getTotalNodes"
@changePage="refresh" />
<table>
<thead>
<tr>
<th>Name</th>
<th>Besitzer*in</th>
<th>E-Mail</th>
<th>Token</th>
<th>MAC</th>
<th>VPN</th>
<th>Site</th>
<th>Domäne</th>
<th>GPS</th>
<th>Status</th>
<th>Monitoring</th>
</tr>
</thead>
<tbody>
<tr
v-for="node in nodes.getNodes"
:class="[node.onlineState ? node.onlineState.toLowerCase() : 'online-state-unknown']">
<td>{{node.hostname}}</td>
<td v-if="shallRedactField(node, 'nickname')">
<span
class="redacted"
@click="setRedactField(node, 'nickname', false)">
nickname
</span>
</td>
<td v-if="!shallRedactField(node, 'nickname')">
<span
class="redactable"
@click="setRedactField(node, 'nickname', true)">
{{node.nickname}}
</span>
</td>
<td v-if="shallRedactField(node, 'email')">
<span
class="redacted"
@click="setRedactField(node, 'email', false)">
email@example.com
</span>
</td>
<td v-if="!shallRedactField(node, 'email')">
<span
class="redactable"
@click="setRedactField(node, 'email', true)">
{{node.email}}
</span>
</td>
<td v-if="shallRedactField(node, 'token')">
<span
class="redacted"
@click="setRedactField(node, 'token', false)">
0123456789abcdef
</span>
</td>
<td v-if="!shallRedactField(node, 'token')">
<span
class="redactable"
@click="setRedactField(node, 'token', true)">
{{node.token}}
</span>
</td>
<td>{{node.mac}}</td>
<td class="icon">
<i
v-if="node.key"
class="fa fa-lock"
aria-hidden="true"
title="Hat VPN-Schlüssel" />
<i
v-if="!node.key"
class="fa fa-times not-available"
aria-hidden="true"
title="Hat keinen VPN-Schlüssel" />
</td>
<td>{{node.site}}</td>
<td>{{node.domain}}</td>
<td class="icon">
<i
v-if="node.coords"
class="fa fa-map-marker"
aria-hidden="true"
title="Hat Koordinaten" />
<i
v-if="!node.coords"
class="fa fa-times not-available"
aria-hidden="true"
title="Hat keinen Koordinaten" />
</td>
<td v-if="node.onlineState !== undefined">{{node.onlineState.toLowerCase()}}</td>
<td v-if="node.onlineState === undefined">unbekannt</td>
<td class="icon">
<i
v-if="node.monitoring && node.monitoringConfirmed"
class="fa fa-heartbeat"
aria-hidden="true"
title="Monitoring aktiv" />
<i
v-if="node.monitoring && !node.monitoringConfirmed"
class="fa fa-envelope"
aria-hidden="true"
title="Monitoring nicht bestätigt" />
<i
v-if="!node.monitoring"
class="fa fa-times not-available"
aria-hidden="true"
title="Monitoring deaktiviert" />
</td>
</tr>
</tbody>
</table>
<Pager
:page="nodes.getPage"
:itemsPerPage="nodes.getNodesPerPage"
:totalItems="nodes.getTotalNodes"
@changePage="refresh" />
</template>
<style lang="scss" scoped>
@import "../scss/variables";
table {
border-collapse: collapse;
th, td {
padding: 0.5em 0.25em;
}
td {
border-top: 1px solid $gray-light;
}
.online {
color: $variant-color-success;
}
.offline {
color: $variant-color-danger;
}
.online-state-unknown {
color: $variant-color-warning;
}
.icon {
text-align: center;
}
.redacted, .redactable {
cursor: pointer;
}
.redacted {
filter: blur(0.2em);
}
.not-available {
color: $gray-dark
}
}
</style>