Connectors page (#81)
* connector to nav bar, route, and base connector.vue page * connectors_available * list_connectors_available only verify connection once rather than twice * precommit fixes
taylor_socfortress committed
Jul 27, 2023 at 12:10 UTC
b9b776929b620fc90c9041056a43354ec5b23c57
5 files changed
+759
-13
backend/app/routes/connectors.py
+14
-9
@@ -33,15 +33,20 @@ def list_connectors_available():
33
logger.info("Received request to get all available connectors")
34
connectors_service = ConnectorService(db)
35
connectors = ConnectorsAvailable.query.all()
36
- result = connectors_available_schema.dump(connectors)
37
-
38
- instantiated_connectors = [
39
- connectors_service.process_connector(connector["connector_name"])
40
- for connector in result
41
- if connectors_service.process_connector(connector["connector_name"])
42
- ]
43
-
44
- return {"message": "All available connectors", "connectors": instantiated_connectors, "success": True}
36
+ connectors_available = connectors_available_schema.dump(connectors)
37
+
38
+ instantiated_connectors = []
39
+ for connector in connectors_available:
40
+ processed_connector = connectors_service.process_connector(connector["connector_name"])
41
+ if processed_connector:
42
+ instantiated_connectors.append(processed_connector)
43
+
44
+ return {
45
+ "message": "All available connectors",
46
+ "connectors": instantiated_connectors,
47
+ "connectors_available": connectors_available,
48
+ "success": True,
49
+ }
50
except Exception as e:
51
logger.error(f"Error while getting all available connectors: {e}")
52
return {"message": "Error while getting all available connectors", "success": False}, 500
src/core/nav.vue
+1
-1
@@ -22,7 +22,7 @@
22
<span slot="title">Crypto</span>
23
</el-menu-item>
24
</el-sub-menu>
25
- <el-menu-item index="/calendar"> <i class="mdi mdi-calendar"></i><span slot="title">Calendar</span> </el-menu-item>
25
+ <el-menu-item index="/connectors"> <i class="mdi mdi-antenna"></i><span slot="title">Connectors</span> </el-menu-item>
26
<el-menu-item index="/contacts"> <i class="mdi mdi-account-multiple-outline"></i><span slot="title">Contacts</span> </el-menu-item>
27
<el-menu-item index="/gallery"> <i class="mdi mdi-image-multiple-outline"></i><span slot="title">Gallery</span> </el-menu-item>
28
<el-menu-item index="/cards"> <i class="mdi mdi-view-dashboard-outline"></i><span slot="title">Cards</span> </el-menu-item>
src/router/connectors.ts
new
+26
@@ -0,0 +1,26 @@
1
+import Connectors from "../views/apps/Connectors.vue"
2
+
3
+import layouts from "../layout"
4
+
5
+export default {
6
+ path: "/connectors",
7
+ name: "connectors",
8
+ component: Connectors,
9
+ meta: {
10
+ auth: true,
11
+ layout: layouts.navLeft
12
+ },
13
+ children: [
14
+ {
15
+ path: "/connectors",
16
+ name: "connectors",
17
+ component: Connectors,
18
+ meta: {
19
+ auth: true,
20
+ layout: layouts.navLeft,
21
+ searchable: true,
22
+ tags: ["simple"]
23
+ }
24
+ }
25
+ ]
26
+}
src/router/index.ts
+4
-3
@@ -10,6 +10,7 @@ import Gallery from "../views/apps/Gallery.vue"
10
import Cards from "../views/apps/Cards.vue"
11
import Mail from "../views/apps/Mail.vue"
12
import Ecommerce from "./ecommerce"
13
+import Connectors from "../views/apps/Connectors.vue"
14
/*
15
16
//pages
@@ -81,9 +82,9 @@ const router = createRouter({
82
}
83
},
84
{
84
- path: "/calendar",
85
- name: "calendar",
86
- component: Calendar,
85
+ path: "/connectors",
86
+ name: "connectors",
87
+ component: Connectors,
88
meta: {
89
auth: true,
90
layout: layouts.navLeft,
src/views/apps/Connectors.vue
new
+714
@@ -0,0 +1,714 @@
1
+<template>
2
+ <div class="page-table scrollable only-y" id="affix-container">
3
+ <div class="page-header">
4
+ <h1>Connectors</h1>
5
+ <h4>Configure connections to your toolset.</h4>
6
+ <el-breadcrumb separator="/">
7
+ <el-breadcrumb-item :to="{ path: '/' }"><i class="mdi mdi-home-outline"></i></el-breadcrumb-item>
8
+ <el-breadcrumb-item>Components</el-breadcrumb-item>
9
+ <el-breadcrumb-item>Tables</el-breadcrumb-item>
10
+ <el-breadcrumb-item>Table</el-breadcrumb-item>
11
+ </el-breadcrumb>
12
+ </div>
13
+
14
+ <div class="table-box card-base card-shadow--medium scrollable only-x">
15
+ <table class="styled striped">
16
+ <thead>
17
+ <tr>
18
+ <th scope="col">Connector Name</th>
19
+ <th scope="col">Connector Description</th>
20
+ <th scope="col">Connector Supports</th>
21
+ <th scope="col">Connector Configured</th>
22
+ <th scope="col">Connector Verified</th>
23
+ <th scope="col">Connector Options</th>
24
+ </tr>
25
+ </thead>
26
+ <tbody>
27
+ <tr v-for="connector in connectors" :key="connector.id">
28
+ <!-- Display the connector details in the table -->
29
+ <td>{{ connector.connector_name }}</td>
30
+ <td>{{ connector.connector_description }}</td>
31
+ <td>{{ connector.connector_supports }}</td>
32
+ <td>
33
+ <el-button type="primary" v-if="connector.connector_configured == true">True</el-button>
34
+ <el-button type="info" v-else>False</el-button>
35
+ </td>
36
+ <!-- Show the connector verified which is in the `connector` table -->
37
+ <td>
38
+ <el-button type="success" v-if="connector.connector_verified == true">True</el-button>
39
+ <el-button type="danger" v-else>False</el-button>
40
+ </td>
41
+ <td>
42
+ <div class="btn-group" role="group">
43
+ <!--If the connector is not already configured then display the configure button -->
44
+ <el-button
45
+ type="primary"
46
+ round
47
+ v-if="
48
+ (connector.connector_configured == false || connector.connector_configured == null) &&
49
+ connector.connector_name.toLowerCase() !== 'velociraptor'
50
+ "
51
+ @click="openConfigureModal(connector)"
52
+ >
53
+ Configure
54
+ </el-button>
55
+
56
+ <!--If the connector is not already configured and the connector_name IS `velociraptor`, then display the configure button -->
57
+ <el-button
58
+ type="primary"
59
+ round
60
+ v-if="
61
+ (connector.connector_configured == false || connector.connector_configured == null) &&
62
+ connector.connector_name.toLowerCase() === 'velociraptor'
63
+ "
64
+ @click="openConfigureModalFile(connector)"
65
+ >
66
+ Configure
67
+ </el-button>
68
+ <!-- If the connector is already configured and the connector_name is not `shuffle` or `dfir-iris` to lower, then display the update button -->
69
+ <el-button
70
+ type="warning"
71
+ round
72
+ v-if="
73
+ connector.connector_configured == true &&
74
+ // connector.connector_name.toLowerCase() !==
75
+ // 'shuffle' &&
76
+ // connector.connector_name.toLowerCase() !==
77
+ // 'dfir-iris' &&
78
+ connector.connector_name.toLowerCase() !== 'velociraptor'
79
+ "
80
+ @click="openUpdateModal(connector)"
81
+ >
82
+ Update
83
+ </el-button>
84
+ <!-- Update Velociraptor -->
85
+ <el-button
86
+ type="warning"
87
+ round
88
+ v-if="
89
+ connector.connector_configured == true &&
90
+ // connector.connector_name.toLowerCase() !==
91
+ // 'shuffle' &&
92
+ // connector.connector_name.toLowerCase() !==
93
+ // 'dfir-iris' &&
94
+ connector.connector_name.toLowerCase() === 'velociraptor'
95
+ "
96
+ @click="openConfigureModalFile(connector)"
97
+ >
98
+ Update
99
+ </el-button>
100
+ <!-- If the connector is already configured and the connector_name IS `shuffle` or `dfir-iris` to lower, then display the update button -->
101
+ <!-- <el-button type="warning" round
102
+ v-if="
103
+ connector.connector_configured == true &&
104
+ (connector.connector_name.toLowerCase() ===
105
+ 'shuffle' ||
106
+ connector.connector_name.toLowerCase() ===
107
+ 'dfir-iris')
108
+ "
109
+ @click="openUpdateModal(connector)"
110
+ >
111
+ Update
112
+ </el-button> -->
113
+
114
+ <!--<button type="button" class="btn btn-info btn-sm" @click="updateConnector(connector)">Update</button>-->
115
+ <!--<button type="button" class="btn btn-info btn-sm" @click="deleteConnector(connector)">Delete</button>-->
116
+ </div>
117
+ </td>
118
+ </tr>
119
+ </tbody>
120
+ </table>
121
+ </div>
122
+
123
+ <!-- Configure Modal Username and Password API Key -->
124
+ <div v-bind:class="{ 'modal-window': true, 'is-active': isConfigureModalActive }">
125
+ <div class="modal-background" @click="openConfigureModal = false"></div>
126
+ <div>
127
+ <el-form :model="connectorForm" status-icon :rules="rules2" ref="connectorForm" label-width="120px" class="demo-ruleForm">
128
+ <div class="modal-card">
129
+ <header class="modal-card-head">
130
+ <h2 class="modal-card-title">Configure {{ currentConnector ? currentConnector.connector_name : "" }}</h2>
131
+ <img
132
+ :src="`/src/assets/images/${
133
+ currentConnector ? currentConnector.connector_name.toLowerCase() + '.svg' : 'default-logo.svg'
134
+ }`"
135
+ alt="Logo"
136
+ class="modal-logo"
137
+ />
138
+ </header>
139
+ <section class="modal-card-body">
140
+ <div class="field">
141
+ <label class="label">Connector URL</label>
142
+ <div class="control">
143
+ <input class="input" type="text" v-model="connectorForm.connector_url" required />
144
+ </div>
145
+ </div>
146
+
147
+ <!-- API Key input for dfir-iris and shuffle -->
148
+ <div
149
+ class="field"
150
+ v-if="
151
+ ['dfir-iris', 'shuffle'].includes(currentConnector ? currentConnector.connector_name.toLowerCase() : '')
152
+ "
153
+ >
154
+ <label class="label">API Key</label>
155
+ <div class="control">
156
+ <input class="input" type="text" v-model="connectorForm.connector_api_key" required />
157
+ </div>
158
+ </div>
159
+
160
+ <!-- Username and Password inputs for other connectors -->
161
+ <template v-else>
162
+ <div class="field">
163
+ <label class="label">Username</label>
164
+ <div class="control">
165
+ <input class="input" type="text" v-model="connectorForm.username" required />
166
+ </div>
167
+ </div>
168
+ <div class="field">
169
+ <label class="label">Password</label>
170
+ <div class="control">
171
+ <input class="input" type="password" v-model="connectorForm.password" required />
172
+ </div>
173
+ </div>
174
+ </template>
175
+
176
+ <div class="field">
177
+ <div class="control">
178
+ <button class="button is-primary" type="submit" @click.prevent="configureConnector">Save</button>
179
+ <button class="button" type="button" @click="closeDialogUserandPass">Cancel</button>
180
+ </div>
181
+ </div>
182
+ </section>
183
+ </div>
184
+ </el-form>
185
+ </div>
186
+ </div>
187
+
188
+ <!-- Configure Modal File -->
189
+ <div v-bind:class="{ 'modal-window': true, 'is-active': isConfigureModalFileActive }">
190
+ <div class="modal-background" @click="openConfigureModalFile = false"></div>
191
+ <div>
192
+ <el-form :model="connectorForm" status-icon :rules="rules2" ref="connectorForm" label-width="120px" class="demo-ruleForm">
193
+ <div class="modal-card">
194
+ <header class="modal-card-head">
195
+ <h2 class="modal-card-title">Configure File {{ currentConnector ? currentConnector.connector_name : "" }}</h2>
196
+ <img
197
+ :src="`/src/assets/images/${
198
+ currentConnector ? currentConnector.connector_name.toLowerCase() + '.svg' : 'default-logo.svg'
199
+ }`"
200
+ alt="Logo"
201
+ class="modal-logo"
202
+ />
203
+ </header>
204
+ <section class="modal-card-body">
205
+ <!-- Add the el-upload component here -->
206
+ <el-upload
207
+ class="upload-demo"
208
+ action="http://localhost:5000/connectors/upload"
209
+ :on-preview="handlePreview"
210
+ :on-remove="handleRemove"
211
+ :before-remove="beforeRemove"
212
+ :on-success="handleSuccess"
213
+ multiple
214
+ drag
215
+ :limit="3"
216
+ :on-exceed="handleExceed"
217
+ :file-list="fileList"
218
+ >
219
+ <i class="el-icon-upload"></i>
220
+ <div class="el-upload__text">Drop file here or <em>click to upload</em></div>
221
+ <div class="el-upload__tip" slot="tip">jpg/png files with a size less than 500kb</div>
222
+ </el-upload>
223
+ <!-- Add the cancel and submit buttons -->
224
+ <div class="field">
225
+ <div class="control">
226
+ <button class="button" type="button" @click="closeConfigureModuleFile">Cancel</button>
227
+ </div>
228
+ </div>
229
+ <!-- Rest of your existing code... -->
230
+ </section>
231
+ </div>
232
+ </el-form>
233
+ </div>
234
+ </div>
235
+
236
+ <!-- Update Modal Username and Password-->
237
+ <div v-bind:class="{ 'modal-window': true, 'is-active': isUpdateModalActive }">
238
+ <div class="modal-background" @click="openUpdateModal = false"></div>
239
+ <div>
240
+ <el-form :model="connectorForm" status-icon :rules="rules2" ref="connectorForm" label-width="120px" class="demo-ruleForm">
241
+ <div class="modal-card">
242
+ <header class="modal-card-head">
243
+ <h2 class="modal-card-title">Update {{ currentConnector ? currentConnector.connector_name : "" }}</h2>
244
+ <img
245
+ :src="`/src/assets/images/${
246
+ currentConnector ? currentConnector.connector_name.toLowerCase() + '.svg' : 'default-logo.svg'
247
+ }`"
248
+ alt="Logo"
249
+ class="modal-logo"
250
+ />
251
+ </header>
252
+ <section class="modal-card-body">
253
+ <div class="field">
254
+ <label class="label">Connector URL</label>
255
+ <div class="control">
256
+ <input class="input" type="text" v-model="connectorForm.connector_url" required />
257
+ </div>
258
+ </div>
259
+
260
+ <!-- API Key input for dfir-iris and shuffle -->
261
+ <div
262
+ class="field"
263
+ v-if="
264
+ ['dfir-iris', 'shuffle'].includes(currentConnector ? currentConnector.connector_name.toLowerCase() : '')
265
+ "
266
+ >
267
+ <label class="label">API Key</label>
268
+ <div class="control">
269
+ <input class="input" type="text" v-model="connectorForm.connector_api_key" required />
270
+ </div>
271
+ </div>
272
+
273
+ <!-- Username and Password inputs for other connectors -->
274
+ <template v-else>
275
+ <div class="field">
276
+ <label class="label">Username</label>
277
+ <div class="control">
278
+ <input class="input" type="text" v-model="connectorForm.username" required />
279
+ </div>
280
+ </div>
281
+ <div class="field">
282
+ <label class="label">Password</label>
283
+ <div class="control">
284
+ <input class="input" type="password" v-model="connectorForm.password" required />
285
+ </div>
286
+ </div>
287
+ </template>
288
+
289
+ <div class="field">
290
+ <div class="control">
291
+ <button class="button is-primary" type="submit" @click.prevent="updateConnector">Save</button>
292
+ <button class="button" type="button" @click="closeUpdateModal">Cancel</button>
293
+ </div>
294
+ </div>
295
+ </section>
296
+ </div>
297
+ </el-form>
298
+ </div>
299
+ </div>
300
+ </div>
301
+</template>
302
+
303
+<script>
304
+import Affix from "@/components/Affix.vue"
305
+import axios from "axios"
306
+
307
+import { defineComponent } from "@vue/runtime-core"
308
+import { component } from "v-viewer"
309
+
310
+export default defineComponent({
311
+ data() {
312
+ return {
313
+ connectors: [],
314
+ currentConnector: null,
315
+
316
+ // Configure Modal
317
+ isConfigureModalActive: false,
318
+ isConfigureModalFileActive: false,
319
+
320
+ // Update Modal
321
+ isUpdateModalActive: false,
322
+
323
+ loading: false,
324
+ showForm: false,
325
+ successMessage: "",
326
+ errorMessage: "",
327
+ connectorForm: {
328
+ connector_url: "",
329
+ username: "",
330
+ password: "",
331
+ connector_api_key: ""
332
+ }
333
+ }
334
+ },
335
+ methods: {
336
+ showDialogUserandPass(connector) {
337
+ this.currentConnector = connector
338
+ this.showForm = true
339
+ // this.$refs.userAndPassDialog.showModal();
340
+ },
341
+
342
+ openConfigureModal(connector) {
343
+ // Open the configure modal and set the initial form values
344
+ this.isConfigureModalActive = true
345
+ this.currentConnector = connector // Store the current connector
346
+ this.connectorForm.connector_url = connector.connector_url
347
+ this.connectorForm.username = connector.connector_username
348
+ this.connectorForm.password = connector.connector_password
349
+ this.connectorForm.connector_api_key = connector.connector_api_key
350
+ },
351
+
352
+ openConfigureModalFile(connector) {
353
+ // Open the configure modal and set the initial form values
354
+ this.isConfigureModalFileActive = true
355
+ this.currentConnector = connector // Store the current connector
356
+ this.connectorForm.connector_url = connector.connector_url
357
+ this.connectorForm.username = connector.connector_username
358
+ this.connectorForm.password = connector.connector_password
359
+ this.connectorForm.connector_api_key = connector.connector_api_key
360
+ },
361
+
362
+ closeDialogUserandPass() {
363
+ // this.$refs.userAndPassDialog.close();
364
+ this.showForm = false
365
+ this.isConfigureModalActive = false
366
+ },
367
+
368
+ closeConfigureModuleFile() {
369
+ // this.$refs.userAndPassDialog.close();
370
+ this.showForm = false
371
+ this.isConfigureModalFileActive = false
372
+ },
373
+
374
+ openUpdateModal(connector) {
375
+ // Open the update modal and set the initial form values
376
+ this.isUpdateModalActive = true
377
+ this.currentConnector = connector // Store the current connector
378
+ this.connectorForm.connector_url = connector.connector_url
379
+ this.connectorForm.username = connector.connector_username
380
+ this.connectorForm.password = connector.connector_password
381
+ this.connectorForm.connector_api_key = connector.connector_api_key
382
+ },
383
+
384
+ closeUpdateModal() {
385
+ // this.$refs.userAndPassDialog.close();
386
+ this.showForm = false
387
+ this.isUpdateModalActive = false
388
+ },
389
+
390
+ configureConnector(event) {
391
+ event.preventDefault()
392
+ const { connector_url, username, password, connector_api_key } = this.connectorForm
393
+ const path = `http://localhost:5000/connectors/${this.currentConnector.id}`
394
+ this.loading = true
395
+ this.closeDialogUserandPass()
396
+
397
+ if (connector_api_key) {
398
+ console.log("POST request to: ", path)
399
+ console.log("Data: ", {
400
+ connector_url: connector_url,
401
+ connector_api_key: connector_api_key
402
+ })
403
+ axios
404
+ .post(path, {
405
+ connector_url: connector_url,
406
+ connector_api_key: connector_api_key
407
+ })
408
+ .then(() => {
409
+ this.successMessage = "Connector has been successfully configured." // Set success message
410
+ setTimeout(() => {
411
+ this.successMessage = "" // Clear success message after 5 seconds
412
+ }, 5000)
413
+ this.getConnectors() // Refresh the connectors
414
+ })
415
+ .catch(err => {
416
+ if (err.response.status === 400) {
417
+ this.errorMessage =
418
+ "This connector is already configured. If you would like to reconfigure this connector select `Edit`." // Set the error message
419
+ } else if (err.response.status === 401) {
420
+ this.errorMessage = "Unauthorized. Please check your endpoint URL, username and password." // Set the error message
421
+ } else {
422
+ this.errorMessage =
423
+ "Error updating the connector. Your settings were not inserted into the keystore. Please try again." // Set the error message
424
+ }
425
+ setTimeout(() => {
426
+ this.errorMessage = "" // Clear error message after 5 seconds
427
+ }, 5000)
428
+ this.getConnectors() // Refresh the connectors
429
+ console.error(err) // Also log the error for debugging
430
+ })
431
+ .finally(() => {
432
+ this.loading = false // Set loading to false
433
+ })
434
+ } else {
435
+ axios
436
+ .post(path, {
437
+ connector_url: connector_url,
438
+ connector_username: username,
439
+ connector_password: password
440
+ })
441
+ .then(() => {
442
+ this.successMessage = "Connector has been successfully configured." // Set success message
443
+ setTimeout(() => {
444
+ this.successMessage = "" // Clear success message after 5 seconds
445
+ }, 5000)
446
+ this.getConnectors() // Refresh the connectors
447
+ })
448
+ .catch(err => {
449
+ if (err.response.status === 400) {
450
+ this.errorMessage =
451
+ "This connector is already configured. If you would like to reconfigure this connector select `Edit`." // Set the error message
452
+ } else if (err.response.status === 401) {
453
+ this.errorMessage = "Unauthorized. Please check your endpoint URL, username and password." // Set the error message
454
+ } else {
455
+ this.errorMessage =
456
+ "Error updating the connector. Your settings were not inserted into the keystore. Please try again." // Set the error message
457
+ }
458
+ setTimeout(() => {
459
+ this.errorMessage = "" // Clear error message after 5 seconds
460
+ }, 5000)
461
+ this.getConnectors() // Refresh the connectors
462
+ console.error(err) // Also log the error for debugging
463
+ })
464
+ .finally(() => {
465
+ this.loading = false // Set loading to false
466
+ })
467
+ }
468
+ },
469
+
470
+ updateConnector(event) {
471
+ event.preventDefault()
472
+ const { connector_url, username, password, connector_api_key } = this.connectorForm
473
+ const path = `http://localhost:5000/connectors/${this.currentConnector.id}`
474
+ this.loading = true
475
+ this.closeUpdateModal()
476
+
477
+ if (connector_api_key) {
478
+ console.log("POST request to: ", path)
479
+ console.log("Data: ", {
480
+ connector_url: connector_url,
481
+ connector_api_key: connector_api_key
482
+ })
483
+
484
+ axios
485
+ .put(path, {
486
+ connector_url: connector_url,
487
+ connector_username: username,
488
+ connector_password: password,
489
+ connector_api_key: connector_api_key
490
+ })
491
+ .then(() => {
492
+ this.successMessage = "Connector has been successfully updated." // Set success message
493
+ setTimeout(() => {
494
+ this.successMessage = "" // Clear success message after 5 seconds
495
+ }, 5000)
496
+ this.getConnectors() // Refresh the connectors
497
+ })
498
+ .catch(err => {
499
+ if (err.response.status === 400) {
500
+ this.errorMessage =
501
+ "This connector is not configured. If you would like to configure this connector select `Configure`." // Set the error message
502
+ } else if (err.response.status === 401) {
503
+ this.errorMessage = "Unauthorized. Please check your endpoint URL, username and password." // Set the error message
504
+ } else {
505
+ this.errorMessage = "Error updating the connector. Please try again." // Set the error message
506
+ }
507
+ setTimeout(() => {
508
+ this.errorMessage = "" // Clear error message after 5 seconds
509
+ }, 5000)
510
+ this.getConnectors() // Refresh the connectors
511
+ console.error(err) // Also log the error for debugging
512
+ })
513
+ .finally(() => {
514
+ this.loading = false // Set loading to false
515
+ })
516
+ } else {
517
+ axios
518
+ .put(path, {
519
+ connector_url: connector_url,
520
+ connector_username: username,
521
+ connector_password: password
522
+ })
523
+ .then(() => {
524
+ this.successMessage = "Connector has been successfully updated." // Set success message
525
+ setTimeout(() => {
526
+ this.successMessage = "" // Clear success message after 5 seconds
527
+ }, 5000)
528
+ this.getConnectors() // Refresh the connectors
529
+ })
530
+ .catch(err => {
531
+ if (err.response.status === 400) {
532
+ this.errorMessage =
533
+ "This connector is not configured. If you would like to configure this connector select `Configure`." // Set the error message
534
+ } else if (err.response.status === 401) {
535
+ this.errorMessage = "Unauthorized. Please check your endpoint URL, username and password." // Set the error message
536
+ } else {
537
+ this.errorMessage = "Error updating the connector. Please try again." // Set the error message
538
+ }
539
+ setTimeout(() => {
540
+ this.errorMessage = "" // Clear error message after 5 seconds
541
+ }, 5000)
542
+ this.getConnectors() // Refresh the connectors
543
+ console.error(err) // Also log the error for debugging
544
+ })
545
+ .finally(() => {
546
+ this.loading = false // Set loading to false
547
+ })
548
+ }
549
+ },
550
+
551
+ getConnectors() {
552
+ const path = "http://localhost:5000/connectors"
553
+ this.loading = true
554
+ axios
555
+ .get(path)
556
+ .then(res => {
557
+ this.connectors = res.data.connectors
558
+ })
559
+ .catch(err => {
560
+ console.error(err)
561
+ })
562
+ .finally(() => {
563
+ this.loading = false
564
+ })
565
+ },
566
+
567
+ resetForm(formName) {
568
+ this.$refs[formName].resetFields()
569
+ }
570
+ },
571
+ created() {
572
+ this.getConnectors()
573
+ }
574
+})
575
+</script>
576
+
577
+<style lang="scss" scoped>
578
+@import "../../assets/scss/_variables";
579
+
580
+.page-table {
581
+ padding-left: 20px;
582
+ padding-right: 15px;
583
+ padding-bottom: 20px;
584
+}
585
+.table-box {
586
+ overflow: auto;
587
+}
588
+
589
+.modal-window {
590
+ position: fixed;
591
+ background-color: rgba(255, 255, 255, 0.25);
592
+ top: 0;
593
+ right: 0;
594
+ bottom: 0;
595
+ left: 0;
596
+ z-index: 999;
597
+ visibility: hidden;
598
+ opacity: 0;
599
+ pointer-events: none;
600
+ transition: all 0.3s;
601
+ &.is-active {
602
+ visibility: visible;
603
+ opacity: 1;
604
+ pointer-events: auto;
605
+ }
606
+ &:target {
607
+ visibility: visible;
608
+ opacity: 1;
609
+ pointer-events: auto;
610
+ }
611
+ & > div {
612
+ width: 400px;
613
+ position: absolute;
614
+ top: 50%;
615
+ left: 50%;
616
+ transform: translate(-50%, -50%);
617
+ padding: 2em;
618
+ background: white;
619
+ }
620
+ header {
621
+ font-weight: bold;
622
+ }
623
+ h1 {
624
+ font-size: 150%;
625
+ margin: 0 0 15px;
626
+ }
627
+}
628
+
629
+.modal-close {
630
+ color: #aaa;
631
+ line-height: 50px;
632
+ font-size: 80%;
633
+ position: absolute;
634
+ right: 0;
635
+ text-align: center;
636
+ top: 0;
637
+ width: 70px;
638
+ text-decoration: none;
639
+ &:hover {
640
+ color: black;
641
+ }
642
+}
643
+
644
+/* Demo Styles */
645
+
646
+html,
647
+body {
648
+ height: 100%;
649
+}
650
+
651
+html {
652
+ font-size: 18px;
653
+ line-height: 1.4;
654
+}
655
+
656
+body {
657
+ font-family: apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
658
+ font-weight: 600;
659
+ background-image: linear-gradient(to right, #7f53ac 0, #657ced 100%);
660
+ color: black;
661
+}
662
+
663
+a {
664
+ color: inherit;
665
+ text-decoration: none;
666
+}
667
+
668
+.container {
669
+ display: grid;
670
+ justify-content: center;
671
+ align-items: center;
672
+ height: 100vh;
673
+}
674
+
675
+.modal-window {
676
+ & > div {
677
+ border-radius: 1rem;
678
+ }
679
+}
680
+
681
+.modal-window div:not(:last-of-type) {
682
+ margin-bottom: 15px;
683
+}
684
+
685
+.logo {
686
+ max-width: 150px;
687
+ display: block;
688
+}
689
+
690
+small {
691
+ color: lightgray;
692
+}
693
+
694
+.btn {
695
+ background-color: white;
696
+ padding: 1em 1.5em;
697
+ border-radius: 0.5rem;
698
+ text-decoration: none;
699
+ i {
700
+ padding-right: 0.3em;
701
+ }
702
+}
703
+
704
+.modal-card-head {
705
+ display: flex;
706
+ justify-content: space-between;
707
+ align-items: center;
708
+}
709
+
710
+.modal-logo {
711
+ height: auto; /* adjust as needed */
712
+ width: auto; /* adjust as needed */
713
+}
714
+</style>