main
vue 176 lines 4.09 KB
Raw
1 <template>
2 <n-spin :show="loading">
3 <div class="flex flex-col gap-3">
4 <n-form-item label="IOC Value" :show-feedback="false">
5 <n-input v-model:value.trim="iocValue" placeholder="IPv4, domain, or SHA256 hash" clearable />
6 </n-form-item>
7 <div class="flex justify-end">
8 <n-button type="primary" :disabled="!isValid" @click="create()">Submit</n-button>
9 </div>
10 <div v-if="error || !!response" class="response bg-secondary" :class="{ error }">
11 <div v-if="error" class="message">
12 {{ error }}
13 </div>
14 <div v-else class="list">
15 <div class="item">
16 <div class="key">type</div>
17 <div class="value">
18 {{ response?.type || "-" }}
19 </div>
20 </div>
21 <div class="item">
22 <div class="key">value</div>
23 <div class="value">
24 {{ response?.value || "-" }}
25 </div>
26 </div>
27 <div class="item">
28 <div class="key">ioc_source</div>
29 <div class="value">
30 {{ response?.ioc_source || "-" }}
31 </div>
32 </div>
33 <div class="item">
34 <div class="key">comment</div>
35 <div class="value">
36 {{ response?.comment || "-" }}
37 </div>
38 </div>
39 <div class="item">
40 <div class="key">score</div>
41 <div class="value">
42 {{ response?.score || "-" }}
43 </div>
44 </div>
45 <div class="item">
46 <div class="key">timestamp</div>
47 <div class="value">
48 {{ response?.timestamp ? formatDate(response.timestamp, dFormats.datetime) : "-" }}
49 </div>
50 </div>
51 <div class="item">
52 <div class="key">report_url</div>
53 <div class="value">
54 <a v-if="response?.report_url" :href="response.report_url" target="_blank">
55 {{ response.report_url }}
56 </a>
57 <span v-else>-</span>
58 </div>
59 </div>
60 <div class="item">
61 <div class="key">virustotal_url</div>
62 <div class="value">
63 <a v-if="response?.virustotal_url" :href="response.virustotal_url" target="_blank">
64 {{ response.virustotal_url }}
65 </a>
66 <span v-else>-</span>
67 </div>
68 </div>
69 </div>
70 </div>
71 </div>
72 </n-spin>
73 </template>
74
75 <script setup lang="ts">
76 import type { ThreatIntelResponse } from "@/types/threatIntel.d"
77 import _trim from "lodash/trim"
78 import { NButton, NFormItem, NInput, NSpin, useMessage } from "naive-ui"
79 import { computed, onMounted, ref } from "vue"
80 import Api from "@/api"
81 import { useSettingsStore } from "@/stores/settings"
82 import { formatDate } from "@/utils/format"
83
84 const emit = defineEmits<{
85 (
86 e: "mounted",
87 value: {
88 restore: () => void
89 }
90 ): void
91 }>()
92
93 const message = useMessage()
94 const dFormats = useSettingsStore().dateFormat
95 const loading = ref(false)
96 const iocValue = ref<string>("")
97 const response = ref<ThreatIntelResponse | null>(null)
98 const error = ref<string>("")
99 const isValid = computed(() => {
100 return !!_trim(iocValue.value)
101 })
102
103 function clear() {
104 iocValue.value = ""
105 }
106
107 function restore() {
108 clear()
109 loading.value = false
110 response.value = null
111 error.value = ""
112 }
113
114 function create() {
115 loading.value = true
116
117 Api.threatIntel
118 .create(iocValue.value)
119 .then(res => {
120 if (res.data.success) {
121 error.value = ""
122 response.value = res.data.data
123 clear()
124 message.success(res.data?.message || "SOCFortress Threat Intel submitted.")
125 } else {
126 error.value = res.data?.message || "An error occurred. Please try again later."
127 message.warning(error.value)
128 }
129 })
130 .catch(err => {
131 error.value = err.response?.data?.message || "An error occurred. Please try again later."
132 message.error(error.value)
133 })
134 .finally(() => {
135 loading.value = false
136 })
137 }
138
139 onMounted(() => {
140 emit("mounted", {
141 restore
142 })
143 })
144 </script>
145
146 <style scoped lang="scss">
147 .response {
148 border-radius: var(--border-radius);
149 border: 1px solid var(--success-color);
150
151 .message {
152 padding: 10px 16px;
153 }
154
155 .list {
156 .item {
157 padding: 10px 16px;
158
159 .key {
160 color: var(--fg-secondary-color);
161 font-size: 12px;
162 margin-bottom: 2px;
163 font-family: var(--font-family-mono);
164 }
165
166 &:not(:last-child) {
167 border-bottom: 1px solid var(--border-color);
168 }
169 }
170 }
171
172 &.error {
173 border-color: var(--error-color);
174 }
175 }
176 </style>