main
vue 143 lines 3.88 KB
Raw
1 <template>
2 <div>
3 <CardEntity :loading="canceling" hoverable clickable @click.stop="openReport()">
4 <template #default>
5 {{ report }}
6 </template>
7 <template #footerExtra>
8 <n-popconfirm
9 v-model:show="showConfirm"
10 trigger="manual"
11 @positive-click="deleteReport()"
12 @clickoutside="showConfirm = false"
13 >
14 <template #trigger>
15 <n-button quaternary size="tiny" @click.stop="showConfirm = true">delete</n-button>
16 </template>
17 Are you sure you want to delete the report?
18 </n-popconfirm>
19 </template>
20 </CardEntity>
21
22 <n-modal
23 v-model:show="showDetails"
24 preset="card"
25 :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(400px, 90vh)', overflow: 'hidden' }"
26 :title="report"
27 :bordered="false"
28 segmented
29 >
30 <n-spin :show="loading" class="min-h-48">
31 <n-page-header v-if="reportNavigation.length > 1" title="index" class="mb-3" @back="pageBack" />
32 <Markdown :source="reportMarkdown || ''" @click.stop="checkEvent($event)" />
33
34 <n-empty v-if="!loading && !reportMarkdown" description="Page not found" class="h-48 justify-center" />
35 </n-spin>
36 </n-modal>
37 </div>
38 </template>
39
40 <script setup lang="ts">
41 import type { NucleiReport } from "@/types/webVulnerabilityAssessment.d"
42 import { NButton, NEmpty, NModal, NPageHeader, NPopconfirm, NSpin, useMessage } from "naive-ui"
43 import { computed, defineAsyncComponent, ref, watch } from "vue"
44 import Api from "@/api"
45 import CardEntity from "@/components/common/cards/CardEntity.vue"
46
47 const { report } = defineProps<{ report: NucleiReport }>()
48
49 const emit = defineEmits<{
50 (e: "deleted"): void
51 }>()
52
53 const Markdown = defineAsyncComponent(() => import("@/components/common/Markdown.vue"))
54
55 const message = useMessage()
56 const canceling = ref(false)
57 const loading = ref(false)
58 const showConfirm = ref(false)
59 const showDetails = ref(false)
60
61 const reportNavigation = ref<string[]>([])
62 const currentReportPage = ref<string | null>(null)
63 const reportsCache = ref<{ [key: string]: string }>({})
64 const reportMarkdown = computed(() => Reflect.get(reportsCache.value, currentReportPage.value as string))
65
66 watch(showDetails, val => {
67 if (!val) {
68 navigationReset()
69 }
70 })
71
72 function openReport(page?: string) {
73 currentReportPage.value = page || "index.md"
74 reportNavigation.value.push(currentReportPage.value)
75
76 showDetails.value = true
77
78 if (reportsCache.value[currentReportPage.value]) {
79 return
80 }
81
82 loading.value = true
83
84 Api.webVulnerabilityAssessment
85 .getReport(report, currentReportPage.value)
86 .then(res => {
87 if (res.data.success) {
88 reportsCache.value[currentReportPage.value as string] = res.data.markdown
89 } else {
90 message.warning(res.data?.message || "An error occurred. Please try again later.")
91 }
92 })
93 .catch(err => {
94 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
95 })
96 .finally(() => {
97 loading.value = false
98 })
99 }
100
101 function checkEvent(event: PointerEvent) {
102 const el = event.target as HTMLAnchorElement | null
103 const href = el?.attributes.getNamedItem("href")
104 const path = href?.value || ""
105
106 if (path && path.includes(".md")) {
107 event.preventDefault()
108 event.stopImmediatePropagation()
109 openReport(path)
110 }
111 }
112
113 function pageBack() {
114 reportNavigation.value.pop()
115 currentReportPage.value = reportNavigation.value.at(-1) || "index.md"
116 }
117
118 function navigationReset() {
119 reportNavigation.value = []
120 currentReportPage.value = null
121 }
122
123 function deleteReport() {
124 canceling.value = true
125
126 Api.webVulnerabilityAssessment
127 .deleteReport(report)
128 .then(res => {
129 if (res.data.success) {
130 message.success(res.data?.message || "Report deleted successfully")
131 emit("deleted")
132 } else {
133 message.warning(res.data?.message || "An error occurred. Please try again later.")
134 }
135 })
136 .catch(err => {
137 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
138 })
139 .finally(() => {
140 canceling.value = false
141 })
142 }
143 </script>