main
vue 223 lines 5.26 KB
Raw
1 <template>
2 <n-spin :show="submitting" class="flex min-h-48 grow flex-col" content-class="flex grow flex-col gap-4">
3 <n-form ref="formRef" :label-width="80" :model="form" :rules>
4 <div class="flex flex-col gap-3">
5 <div>
6 <n-form-item label="Description" path="ioc_description">
7 <n-input
8 v-model:value.trim="form.ioc_description"
9 placeholder="Input the description..."
10 clearable
11 type="textarea"
12 :autosize="{
13 minRows: 5,
14 maxRows: 15
15 }"
16 />
17 </n-form-item>
18 </div>
19 <div class="flex flex-col gap-3 sm:flex-row">
20 <n-form-item label="Type" path="ioc_type" class="basis-1/2">
21 <n-select
22 v-model:value="form.ioc_type"
23 :options="iocTypeOptions"
24 placeholder="Select the Type..."
25 clearable
26 to="body"
27 @update:value="form.ioc_value && validate()"
28 />
29 </n-form-item>
30 <n-form-item label="Value" path="ioc_value" class="basis-1/2">
31 <n-input
32 v-model:value.trim="form.ioc_value"
33 :disabled="!form.ioc_type"
34 placeholder="Input the value..."
35 clearable
36 />
37 </n-form-item>
38 </div>
39
40 <div class="flex justify-between gap-4">
41 <div class="flex gap-4">
42 <slot name="additionalActions"></slot>
43 </div>
44 <div class="flex gap-4">
45 <n-button :disabled="submitting" @click="reset()">Reset</n-button>
46 <n-button :disabled="!isValid" :loading="submitting" type="primary" @click="validate(submit)">
47 Submit
48 </n-button>
49 </div>
50 </div>
51 </div>
52 </n-form>
53 </n-spin>
54 </template>
55
56 <script setup lang="ts">
57 import type { FormInst, FormItemRule, FormRules, FormValidationError } from "naive-ui"
58 import type { AlertIocPayload } from "@/api/endpoints/incidentManagement/alerts"
59 import type { DeepNullable } from "@/types/common"
60 import type { AlertIOC } from "@/types/incidentManagement/alerts.d"
61 import _get from "lodash/get"
62 import _trim from "lodash/trim"
63 import { NButton, NForm, NFormItem, NInput, NSelect, NSpin, useMessage } from "naive-ui"
64 import isIP from "validator/es/lib/isIP"
65 import isURL from "validator/es/lib/isURL"
66 import { computed, onMounted, ref, toRefs, watch } from "vue"
67 import Api from "@/api"
68
69 const props = defineProps<{ alertId: number }>()
70 const emit = defineEmits<{
71 (e: "update:loading", value: boolean): void
72 (e: "submitted", value: AlertIOC): void
73 (
74 e: "mounted",
75 value: {
76 reset: () => void
77 }
78 ): void
79 }>()
80
81 const { alertId } = toRefs(props)
82 const submitting = ref(false)
83 const message = useMessage()
84 const form = ref<DeepNullable<AlertIocPayload>>(getForm())
85 const formRef = ref<FormInst | null>(null)
86
87 const rules: FormRules = {
88 ioc_value: {
89 validator: validateValue,
90 required: true,
91 trigger: ["input", "blur"]
92 },
93 ioc_type: {
94 message: "Please input the Type",
95 required: true,
96 trigger: ["input", "blur"]
97 },
98 ioc_description: {
99 message: "Please input the Description",
100 required: true,
101 trigger: ["input", "blur"]
102 }
103 }
104
105 const iocTypeOptions: { label: string; value: string }[] = [
106 { label: "IP", value: "IP" },
107 { label: "DOMAIN", value: "DOMAIN" },
108 { label: "HASH", value: "HASH" },
109 { label: "URL", value: "URL" }
110 ]
111
112 const isValid = computed(() => {
113 let valid = true
114
115 for (const key in rules) {
116 const rule = rules[key] as FormRules
117
118 if (rule.required && !_trim(_get(form.value, key))) {
119 valid = false
120 }
121 }
122
123 return valid
124 })
125
126 function validateValue(_rule: FormItemRule, value: string) {
127 if (!value) {
128 return new Error("Please input a valid Value")
129 }
130
131 switch (form.value.ioc_type) {
132 case "URL":
133 if (!isURL(value, { require_tld: false })) {
134 return new Error("Please input a valid URL")
135 }
136 break
137 case "DOMAIN":
138 if (!isURL(value, { require_tld: false })) {
139 return new Error("Please input a valid DOMAIN")
140 }
141 break
142 case "IP":
143 if (!isIP(value)) {
144 return new Error("Please input a valid IP Address")
145 }
146 break
147 }
148
149 return true
150 }
151
152 function validate(cb?: () => void) {
153 if (!formRef.value) return
154
155 formRef.value.validate((errors?: Array<FormValidationError>) => {
156 if (!errors) {
157 if (cb && typeof cb === "function") {
158 cb()
159 }
160 } else {
161 message.warning("You must fill in the required fields correctly.")
162 return false
163 }
164 })
165 }
166
167 function getForm() {
168 const payload = {
169 alert_id: alertId.value,
170 ioc_value: null,
171 ioc_type: null,
172 ioc_description: null
173 }
174 return payload
175 }
176
177 function reset(force?: boolean) {
178 if (!submitting.value || force) {
179 resetForm()
180 formRef.value?.restoreValidation()
181 }
182 }
183
184 function resetForm() {
185 form.value = getForm()
186 }
187
188 function submit() {
189 submitting.value = true
190
191 Api.incidentManagement.alerts
192 .createAlertIoc(form.value as AlertIocPayload)
193 .then(res => {
194 if (res.data.success) {
195 emit("submitted", {
196 id: res.data.alert_ioc.ioc_id,
197 description: form.value.ioc_description || "",
198 type: form.value.ioc_type || "",
199 value: form.value.ioc_value || ""
200 })
201 reset()
202 } else {
203 message.warning(res.data?.message || "An error occurred. Please try again later.")
204 }
205 })
206 .catch(err => {
207 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
208 })
209 .finally(() => {
210 submitting.value = false
211 })
212 }
213
214 watch(submitting, val => {
215 emit("update:loading", val)
216 })
217
218 onMounted(() => {
219 emit("mounted", {
220 reset
221 })
222 })
223 </script>