main
vue 192 lines 4.84 KB
Raw
1 <template>
2 <n-spin class="sca-table" :show="loading">
3 <n-scrollbar x-scrollable class="w-full">
4 <n-table bordered class="min-w-max">
5 <thead>
6 <tr>
7 <th></th>
8 <th>Policy</th>
9 <th>End scan</th>
10 <th>Passed</th>
11 <th>Failed</th>
12 <th>Total checks</th>
13 <th>Score</th>
14 </tr>
15 </thead>
16 <tbody>
17 <tr v-for="item of scaList" :key="item.policy_id">
18 <td class="w-6">
19 <div class="flex items-center gap-2">
20 <n-tooltip trigger="hover">
21 <template #trigger>
22 <n-button size="small" @click="showScaDetails(item)">
23 <template #icon>
24 <Icon :name="InfoIcon" />
25 </template>
26 </n-button>
27 </template>
28 Details
29 </n-tooltip>
30
31 <n-tooltip trigger="hover">
32 <template #trigger>
33 <n-button size="small" :loading="item.downloading" @click="scaDownload(item)">
34 <template #icon>
35 <Icon :name="DownloadIcon" />
36 </template>
37 </n-button>
38 </template>
39 Download CSV
40 </n-tooltip>
41 </div>
42 </td>
43 <td>
44 <div class="flex flex-col gap-1">
45 <strong>{{ item.policy_id }}</strong>
46
47 <p class="hidden lg:flex">
48 {{ item.extract }}
49
50 <n-popover
51 v-if="item.description !== item.extract"
52 placement="top-end"
53 content-class="max-w-96"
54 scrollable
55 to="body"
56 >
57 <template #trigger>
58 <span class="cursor-help underline">...</span>
59 </template>
60 <div class="flex flex-col px-1 py-2">
61 {{ item.description }}
62 </div>
63 </n-popover>
64 </p>
65 </div>
66 </td>
67 <td>
68 {{ item.end_scan_text }}
69 </td>
70 <td>
71 {{ item.pass }}
72 </td>
73 <td>
74 {{ item.fail }}
75 </td>
76 <td>
77 {{ item.total_checks }}
78 </td>
79 <td>{{ item.score }}%</td>
80 </tr>
81 </tbody>
82 </n-table>
83 </n-scrollbar>
84 <n-empty v-if="!loading && !scaList.length" description="No items found" class="h-48 justify-center" />
85
86 <n-modal
87 v-model:show="showDetails"
88 preset="card"
89 :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(400px, 90vh)', overflow: 'hidden' }"
90 :title="selectedSca?.policy_id || ''"
91 :bordered="false"
92 segmented
93 content-class="p-0!"
94 >
95 <ScaItem v-if="selectedSca" :sca="selectedSca" :agent></ScaItem>
96 </n-modal>
97 </n-spin>
98 </template>
99
100 <script setup lang="ts">
101 import type { Agent, AgentSca } from "@/types/agents.d"
102 import { saveAs } from "file-saver"
103 import _truncate from "lodash/truncate"
104 import { NButton, NEmpty, NModal, NPopover, NScrollbar, NSpin, NTable, NTooltip, useMessage } from "naive-ui"
105 import { onBeforeMount, ref, toRefs } from "vue"
106 import Api from "@/api"
107 import Icon from "@/components/common/Icon.vue"
108 import { useSettingsStore } from "@/stores/settings"
109 import { formatDate } from "@/utils/format"
110 import ScaItem from "./ScaItem.vue"
111
112 interface SCAExt extends AgentSca {
113 end_scan_text?: string
114 extract?: string
115 downloading: boolean
116 }
117
118 const props = defineProps<{
119 agent: Agent
120 }>()
121 const { agent } = toRefs(props)
122
123 const DownloadIcon = "carbon:document-download"
124 const InfoIcon = "carbon:information"
125 const message = useMessage()
126 const loading = ref(false)
127 const showDetails = ref(false)
128 const scaList = ref<SCAExt[]>([])
129 const dFormats = useSettingsStore().dateFormat
130 const selectedSca = ref<SCAExt | null>(null)
131
132 function getSCA(agentId: string) {
133 loading.value = true
134
135 Api.agents
136 .getSCA(agentId)
137 .then(res => {
138 if (res.data.success) {
139 scaList.value = (res.data.sca || []).map(o => {
140 return {
141 ...o,
142 downloading: false,
143 end_scan_text: formatDate(o.end_scan, dFormats.datetime).toString(),
144 extract: _truncate(o.description, {
145 length: 50,
146 omission: ""
147 })
148 }
149 })
150 } else {
151 message.warning(res.data?.message || "An error occurred. Please try again later.")
152 }
153 })
154 .catch(err => {
155 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
156 })
157 .finally(() => {
158 loading.value = false
159 })
160 }
161
162 function showScaDetails(sca: SCAExt) {
163 showDetails.value = true
164 selectedSca.value = sca
165 }
166
167 function scaDownload(sca: SCAExt) {
168 sca.downloading = true
169
170 const fileName = `sca_policy:${sca.policy_id}_${formatDate(new Date(), dFormats.datetimesec)}.csv`
171
172 Api.agents
173 .scaResultsDownload(agent.value.agent_id, sca.policy_id)
174 .then(res => {
175 if (res.data) {
176 saveAs(res.data, fileName)
177 } else {
178 message.warning("An error occurred. Please try again later.")
179 }
180 })
181 .catch(err => {
182 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
183 })
184 .finally(() => {
185 sca.downloading = false
186 })
187 }
188
189 onBeforeMount(() => {
190 if (agent?.value?.agent_id) getSCA(agent.value.agent_id)
191 })
192 </script>