main
vue 225 lines 4.95 KB
Raw
1 <template>
2 <div class="file-collection-form">
3 <n-form ref="formRef" :model="formData" :rules="formRules">
4 <n-form-item label="File Path" path="file_path">
5 <n-input
6 v-model:value="formData.file_path"
7 placeholder="e.g., Users\Administrator\Downloads\file.txt or /home/user/file.txt"
8 :disabled="loading"
9 clearable
10 >
11 <template #prefix>
12 <Icon :name="FileIcon" :size="16" />
13 </template>
14 </n-input>
15 </n-form-item>
16
17 <n-form-item label="Root Disk" path="root_disk">
18 <n-input
19 v-model:value="formData.root_disk"
20 placeholder="e.g., C: (Windows) or / (Linux)"
21 :disabled="loading"
22 clearable
23 >
24 <template #prefix>
25 <Icon :name="DiskIcon" :size="16" />
26 </template>
27 </n-input>
28 </n-form-item>
29
30 <n-form-item>
31 <div class="flex items-center gap-3">
32 <n-button
33 type="primary"
34 :loading
35 :disabled="!formData.file_path || !formData.root_disk"
36 @click="handleSubmit"
37 >
38 <template #icon>
39 <Icon :name="CollectIcon" />
40 </template>
41 Collect File
42 </n-button>
43
44 <n-button
45 v-if="formData.file_path || formData.root_disk"
46 secondary
47 :disabled="loading"
48 @click="handleReset"
49 >
50 Reset
51 </n-button>
52 </div>
53 </n-form-item>
54 </n-form>
55
56 <n-divider v-if="result" class="my-4!" />
57
58 <n-alert
59 v-if="result"
60 :type="result.success ? 'success' : 'error'"
61 :title="result.success ? 'Collection Started' : 'Collection Failed'"
62 closable
63 @close="result = null"
64 >
65 <template v-if="result.success">
66 <div class="flex flex-col gap-2">
67 <div v-if="result.flow_id">
68 <strong>Flow ID:</strong>
69 <code class="ml-2">{{ result.flow_id }}</code>
70 </div>
71 <div v-if="result.session_id">
72 <strong>Session ID:</strong>
73 <code class="ml-2">{{ result.session_id }}</code>
74 </div>
75 <div class="text-secondary-color mt-2 text-sm">
76 {{ result.message }}
77 </div>
78 </div>
79 </template>
80 <template v-else>
81 {{ result.message }}
82 </template>
83 </n-alert>
84 </div>
85 </template>
86
87 <script setup lang="ts">
88 import type { FormInst, FormRules } from "naive-ui"
89 import type { FileCollection } from "@/types/artifacts.d"
90 import { NAlert, NButton, NDivider, NForm, NFormItem, NInput, useMessage } from "naive-ui"
91 import { ref } from "vue"
92 import Api from "@/api"
93 import Icon from "@/components/common/Icon.vue"
94
95 interface FileCollectionResult extends FileCollection {
96 success: boolean
97 message: string
98 }
99
100 const props = defineProps<{
101 agentId: string
102 }>()
103
104 const emit = defineEmits<{
105 (e: "success", result: FileCollectionResult): void
106 (e: "error", error: string): void
107 }>()
108
109 const FileIcon = "carbon:document"
110 const DiskIcon = "carbon:storage-pool"
111 const CollectIcon = "carbon:download"
112
113 const message = useMessage()
114 const formRef = ref<FormInst | null>(null)
115 const loading = ref(false)
116
117 const formData = ref({
118 file_path: "",
119 root_disk: ""
120 })
121
122 const result = ref<FileCollectionResult | null>(null)
123
124 const formRules: FormRules = {
125 file_path: [
126 {
127 required: true,
128 message: "File path is required",
129 trigger: ["blur", "input"]
130 }
131 ],
132 root_disk: [
133 {
134 required: true,
135 message: "Root disk is required",
136 trigger: ["blur", "input"]
137 }
138 ]
139 }
140
141 async function handleSubmit() {
142 if (!formRef.value) return
143
144 try {
145 await formRef.value.validate()
146 } catch {
147 message.warning("You must fill in the required fields correctly.")
148 return false
149 }
150
151 loading.value = true
152 result.value = null
153
154 Api.artifacts
155 .collectFileByAgentId(props.agentId, {
156 file: formData.value.file_path,
157 root_disk: formData.value.root_disk
158 })
159 .then(res => {
160 if (res.data.success) {
161 result.value = {
162 success: true,
163 message: res.data.message || "File collection started successfully",
164 flow_id: res.data.flow_id,
165 session_id: res.data.session_id
166 }
167 message.success("File collection started successfully")
168 emit("success", result.value)
169 } else {
170 result.value = {
171 success: false,
172 message: res.data?.message || "Failed to start file collection"
173 }
174 message.error(result.value.message)
175 emit("error", result.value.message)
176 }
177 })
178 .catch(err => {
179 const errorMessage = err.response?.data?.message || "An error occurred during file collection"
180 result.value = {
181 success: false,
182 message: errorMessage
183 }
184 message.error(errorMessage)
185 emit("error", errorMessage)
186 })
187 .finally(() => {
188 loading.value = false
189 })
190 }
191
192 function handleReset() {
193 formData.value = {
194 file_path: "",
195 root_disk: ""
196 }
197 result.value = null
198 formRef.value?.restoreValidation()
199 }
200
201 // Expose methods for parent components
202 defineExpose({
203 reset: handleReset
204 })
205 </script>
206
207 <style lang="scss" scoped>
208 // TODO-FE: remove style
209
210 .file-collection-form {
211 max-width: 600px;
212
213 :deep(.n-form-item) {
214 margin-bottom: 20px;
215 }
216
217 code {
218 font-family: var(--font-family-mono);
219 font-size: 12px;
220 padding: 2px 6px;
221 background-color: var(--bg-secondary-color);
222 border-radius: 3px;
223 }
224 }
225 </style>