main
vue 190 lines 4.66 KB
Raw
1 <template>
2 <n-card class="cluster-health" segmented>
3 <template #header>
4 <div class="align-center flex justify-between">
5 <span>Nodes Allocation</span>
6 <span v-if="indicesAllocation.length" class="text-secondary font-mono">
7 {{ indicesAllocation.length }}
8 </span>
9 </div>
10 </template>
11 <n-spin :show="loading">
12 <div class="info min-h-14">
13 <template v-if="indicesAllocation.length">
14 <n-scrollbar style="max-height: 500px" trigger="none">
15 <div
16 v-for="node of indicesAllocation"
17 :key="node.id"
18 class="item"
19 :class="[`percent-${getStatusPercent(node.disk_percent)}`, `node-${node.node}`]"
20 >
21 <div class="group">
22 <div class="box">
23 <div class="value">
24 {{ node.node }}
25 </div>
26 <div class="label">node</div>
27 </div>
28 </div>
29 <div class="group">
30 <div class="box">
31 <div class="value">
32 {{ node.disk_total || "-" }}
33 </div>
34 <div class="label">disk_total</div>
35 </div>
36 <div class="box">
37 <div class="value">
38 {{ node.disk_used || "-" }}
39 </div>
40 <div class="label">disk_used</div>
41 </div>
42 <div class="box">
43 <div class="value">
44 {{ node.disk_available || "-" }}
45 </div>
46 <div class="label">disk_available</div>
47 </div>
48 </div>
49 <div v-if="node.disk_percent" class="disk-percent group">
50 <div class="box w-full">
51 <n-progress
52 type="line"
53 indicator-placement="inside"
54 border-radius="0"
55 :height="24"
56 :percentage="node.disk_percent_value"
57 :status="getStatusPercent(node.disk_percent_value)"
58 />
59 </div>
60 </div>
61 </div>
62 </n-scrollbar>
63 </template>
64 <template v-else>
65 <n-empty v-if="!loading" description="No allocations found" class="h-48 justify-center" />
66 </template>
67 </div>
68 </n-spin>
69 </n-card>
70 </template>
71
72 <script setup lang="ts">
73 import type { IndexAllocation } from "@/types/indices.d"
74 import { NCard, NEmpty, NProgress, NScrollbar, NSpin, useMessage } from "naive-ui"
75 import { nanoid } from "nanoid"
76 import { onBeforeMount, ref } from "vue"
77 import Api from "@/api"
78
79 const message = useMessage()
80 const indicesAllocation = ref<IndexAllocation[]>([])
81 const loading = ref(true)
82
83 function getStatusPercent(percent: string | number | undefined | null) {
84 if (Number.parseFloat(percent?.toString() || "") > 90) return "error"
85 if (Number.parseFloat(percent?.toString() || "") > 80) return "warning"
86 return "success"
87 }
88
89 function getIndicesAllocation() {
90 loading.value = true
91 Api.wazuh.indices
92 .getAllocation()
93 .then(res => {
94 if (res.data.success) {
95 indicesAllocation.value = (res.data?.node_allocation || []).map(obj => {
96 obj.id = nanoid()
97 obj.disk_percent_value = Number.parseFloat(obj.disk_percent || "")
98 return obj
99 })
100 } else {
101 message.error(res.data?.message || "An error occurred. Please try again later.")
102 }
103 })
104 .catch(err => {
105 if (err.response?.status === 401) {
106 message.error(
107 err.response?.data?.message ||
108 "Wazuh-Indexer returned Unauthorized. Please check your connector credentials."
109 )
110 } else if (err.response?.status === 404) {
111 message.error(err.response?.data?.message || "No alerts were found.")
112 } else {
113 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
114 }
115 })
116 .finally(() => {
117 loading.value = false
118 })
119 }
120
121 onBeforeMount(() => {
122 getIndicesAllocation()
123 })
124 </script>
125
126 <style lang="scss" scoped>
127 .cluster-health {
128 .info {
129 margin-left: -5px;
130 margin-right: -5px;
131
132 .item {
133 border: 2px solid transparent;
134 display: flex;
135 border-radius: var(--border-radius);
136 flex-direction: column;
137 overflow: hidden;
138
139 .group {
140 padding-inline: calc(var(--spacing) * 4);
141 padding-block: calc(var(--spacing) * 3);
142 gap: calc(var(--spacing) * 6);
143 display: flex;
144 justify-content: space-between;
145 flex-grow: 1;
146 flex-wrap: wrap;
147 overflow: hidden;
148
149 .box {
150 overflow: hidden;
151
152 .value {
153 font-weight: bold;
154 margin-bottom: 2px;
155 }
156 .label {
157 font-size: var(--text-xs);
158 font-family: var(--font-family-mono);
159 opacity: 0.8;
160 }
161 }
162
163 &.disk-percent {
164 padding: 0;
165 }
166 }
167
168 &.percent-success {
169 border-color: var(--success-color);
170 }
171
172 &.percent-warning {
173 border-color: var(--warning-color);
174 }
175
176 &.percent-error {
177 border-color: var(--error-color);
178 }
179
180 &.node-UNASSIGNED {
181 border-color: var(--info-color);
182 }
183
184 &:not(:last-child) {
185 margin-bottom: calc(var(--spacing) * 4);
186 }
187 }
188 }
189 }
190 </style>