main
vue 159 lines 4.21 KB
Raw
1 <template>
2 <div>
3 <CardEntity
4 :embedded
5 hoverable
6 clickable
7 :status="type === 'healthy' ? 'success' : type === 'unhealthy' ? 'warning' : undefined"
8 @click="showDetails = true"
9 >
10 <template #headerMain>#{{ healthData.id }} - {{ healthData.label }}</template>
11 <template v-if="cardDate" #headerExtra>{{ cardDate }}</template>
12 <template #default>
13 <div class="flex grow flex-col gap-1">
14 <div class="flex flex-wrap items-center gap-2">
15 <Icon :name="iconFromOs(healthData.os)" :size="16" />
16 {{ healthData.os }}
17 </div>
18 <p>
19 {{ healthData.ip_address }}
20 </p>
21 </div>
22 </template>
23 <template #mainExtra>
24 <div class="flex flex-wrap items-center gap-3">
25 <Badge v-if="agentVersion" type="splitted">
26 <template #label>Agent version</template>
27 <template #value>
28 {{ agentVersion }}
29 </template>
30 </Badge>
31
32 <Badge v-if="source === 'velociraptor'" type="splitted">
33 <template #label>Velociraptor Id</template>
34 <template #value>
35 {{ healthData.velociraptor_id }}
36 </template>
37 </Badge>
38
39 <n-popover overlap placement="bottom-start">
40 <template #trigger>
41 <Badge type="splitted" hint-cursor>
42 <template #iconLeft>
43 <Icon :name="AgentIcon" :size="13" class="opacity-80!" />
44 </template>
45 <template #label>Agent</template>
46 <template #value>
47 <div class="flex items-center gap-2">
48 {{ healthData.hostname }}
49 <Icon :name="InfoIcon" :size="14" />
50 </div>
51 </template>
52 </Badge>
53 </template>
54 <div class="flex flex-col gap-1">
55 <div class="box">
56 agent_id:
57 <code
58 class="text-primary cursor-pointer"
59 @click="routeAgent(healthData.agent_id).navigate()"
60 >
61 {{ healthData.agent_id }}
62 <Icon :name="LinkIcon" :size="13" class="relative top-0.5" />
63 </code>
64 </div>
65 <div class="box">
66 hostname:
67 <code>{{ healthData.hostname }}</code>
68 </div>
69 </div>
70 </n-popover>
71 </div>
72 </template>
73 </CardEntity>
74
75 <n-modal
76 v-model:show="showDetails"
77 preset="card"
78 content-class="p-0!"
79 :style="{ maxWidth: 'min(800px, 90vw)', overflow: 'hidden' }"
80 :title="`Health check ${source}`"
81 :bordered="false"
82 segmented
83 >
84 <div class="grid-auto-fit-200 grid gap-2 px-7 py-6">
85 <CardKV v-for="(value, key) of healthData" :key>
86 <template #key>
87 {{ key }}
88 </template>
89 <template #value>
90 {{ value || "-" }}
91 </template>
92 </CardKV>
93 </div>
94 </n-modal>
95 </div>
96 </template>
97
98 <script setup lang="ts">
99 import type { CustomerAgentHealth, CustomerHealthcheckSource } from "@/types/customers.d"
100 import { NModal, NPopover } from "naive-ui"
101 import { computed, ref } from "vue"
102 import Badge from "@/components/common/Badge.vue"
103 import CardEntity from "@/components/common/cards/CardEntity.vue"
104 import CardKV from "@/components/common/cards/CardKV.vue"
105 import Icon from "@/components/common/Icon.vue"
106 import { useNavigation } from "@/composables/useNavigation"
107 import { useSettingsStore } from "@/stores/settings"
108 import { iconFromOs } from "@/utils"
109 import dayjs from "@/utils/dayjs"
110
111 const { healthData, source, embedded, type } = defineProps<{
112 healthData: CustomerAgentHealth
113 source: CustomerHealthcheckSource
114 type?: "healthy" | "unhealthy"
115 embedded?: boolean
116 }>()
117
118 const InfoIcon = "carbon:information"
119 const AgentIcon = "carbon:police"
120 const LinkIcon = "carbon:launch"
121
122 const showDetails = ref(false)
123 const { routeAgent } = useNavigation()
124
125 const agentVersion = computed(() => {
126 let agent = ""
127
128 switch (source) {
129 case "wazuh":
130 agent = healthData.wazuh_agent_version
131 break
132 case "velociraptor":
133 agent = healthData.velociraptor_agent_version
134 break
135 }
136
137 return agent
138 })
139
140 const cardDate = computed(() => {
141 let date = ""
142 switch (source) {
143 case "wazuh":
144 date = healthData.wazuh_last_seen
145 break
146 case "velociraptor":
147 date = healthData.velociraptor_last_seen
148 break
149 }
150
151 return date ? formatDate(date) : ""
152 })
153
154 const dFormats = useSettingsStore().dateFormat
155
156 function formatDate(timestamp: string | number, utc: boolean = true): string {
157 return dayjs(timestamp).utc(utc).format(dFormats.datetimesec)
158 }
159 </script>