@drax/audit-vue 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +66 -0
- package/src/components/AuditView.vue +380 -0
- package/src/cruds/AuditCrud.ts +204 -0
- package/src/index.ts +17 -0
- package/src/pages/crud/AuditCrudPage.vue +28 -0
- package/src/routes/AuditCrudRoute.ts +18 -0
- package/src/routes/index.ts +8 -0
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drax/audit-vue",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "0.38.0",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./src/index.ts",
|
|
9
|
+
"module": "./src/index.ts",
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev": "vite",
|
|
16
|
+
"build": "run-p type-check \"build-only {@}\" --",
|
|
17
|
+
"preview": "vite preview",
|
|
18
|
+
"test:unit": "vitest",
|
|
19
|
+
"test:e2e": "start-server-and-test preview http://localhost:4173 'cypress run --e2e'",
|
|
20
|
+
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' http://localhost:4173 'cypress open --e2e'",
|
|
21
|
+
"build-only": "vite build",
|
|
22
|
+
"type-check": "vue-tsc --build --force",
|
|
23
|
+
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
|
24
|
+
"format": "prettier --write src/"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@drax/crud-front": "^0.38.0",
|
|
28
|
+
"@drax/crud-share": "^0.38.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"pinia": "^2.2.2",
|
|
32
|
+
"vue": "^3.5.7",
|
|
33
|
+
"vue-i18n": "^9.14.0",
|
|
34
|
+
"vuetify": "^3.7.2"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@rushstack/eslint-patch": "^1.8.0",
|
|
38
|
+
"@tsconfig/node20": "^20.1.4",
|
|
39
|
+
"@types/jsdom": "^21.1.7",
|
|
40
|
+
"@types/node": "^20.12.5",
|
|
41
|
+
"@vitejs/plugin-vue": "^5.0.4",
|
|
42
|
+
"@vue/eslint-config-prettier": "^9.0.0",
|
|
43
|
+
"@vue/eslint-config-typescript": "^13.0.0",
|
|
44
|
+
"@vue/test-utils": "^2.4.5",
|
|
45
|
+
"@vue/tsconfig": "^0.5.1",
|
|
46
|
+
"cypress": "^13.7.2",
|
|
47
|
+
"eslint": "^8.57.0",
|
|
48
|
+
"eslint-plugin-cypress": "^2.15.1",
|
|
49
|
+
"eslint-plugin-vue": "^9.23.0",
|
|
50
|
+
"jsdom": "^24.0.0",
|
|
51
|
+
"npm-run-all2": "^6.1.2",
|
|
52
|
+
"pinia": "^2.1.7",
|
|
53
|
+
"pinia-plugin-persistedstate": "^3.2.1",
|
|
54
|
+
"prettier": "^3.2.5",
|
|
55
|
+
"start-server-and-test": "^2.0.3",
|
|
56
|
+
"typescript": "5.6.3",
|
|
57
|
+
"vite": "^5.4.3",
|
|
58
|
+
"vite-plugin-css-injected-by-js": "^3.5.1",
|
|
59
|
+
"vite-plugin-dts": "^3.9.1",
|
|
60
|
+
"vitest": "^1.4.0",
|
|
61
|
+
"vue": "^3.5.3",
|
|
62
|
+
"vue-tsc": "^2.0.11",
|
|
63
|
+
"vuetify": "^3.7.1"
|
|
64
|
+
},
|
|
65
|
+
"gitHead": "43c90f3c12165e7527edefbc80dd327a59236dd5"
|
|
66
|
+
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import type { IAudit } from '@drax/audit-share'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
audit: IAudit
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const props = defineProps<Props>()
|
|
11
|
+
|
|
12
|
+
const actionColor = computed(() => {
|
|
13
|
+
const actionMap: Record<string, string> = {
|
|
14
|
+
'CREATE': 'success',
|
|
15
|
+
'UPDATE': 'warning',
|
|
16
|
+
'DELETE': 'error',
|
|
17
|
+
'READ': 'info',
|
|
18
|
+
'EXPORT': 'primary'
|
|
19
|
+
}
|
|
20
|
+
return actionMap[props.audit.action] || 'secondary'
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const actionIcon = computed(() => {
|
|
24
|
+
const iconMap: Record<string, string> = {
|
|
25
|
+
'CREATE': 'mdi-plus-circle',
|
|
26
|
+
'UPDATE': 'mdi-pencil-circle',
|
|
27
|
+
'DELETE': 'mdi-delete-circle',
|
|
28
|
+
'READ': 'mdi-eye-circle',
|
|
29
|
+
'EXPORT': 'mdi-download-circle'
|
|
30
|
+
}
|
|
31
|
+
return iconMap[props.audit.action] || 'mdi-information-circle'
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const formattedDate = computed(() => {
|
|
35
|
+
if (!props.audit.createdAt) return 'N/A'
|
|
36
|
+
return new Date(props.audit.createdAt).toLocaleString('es-ES', {
|
|
37
|
+
year: 'numeric',
|
|
38
|
+
month: '2-digit',
|
|
39
|
+
day: '2-digit',
|
|
40
|
+
hour: '2-digit',
|
|
41
|
+
minute: '2-digit',
|
|
42
|
+
second: '2-digit'
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const hasChanges = computed(() => {
|
|
47
|
+
return props.audit.changes && props.audit.changes.length > 0
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const copyToClipboard = (text: string) => {
|
|
51
|
+
navigator.clipboard.writeText(text)
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<template>
|
|
56
|
+
<v-card class="audit-view-card" elevation="2">
|
|
57
|
+
<!-- Header con acción y fecha -->
|
|
58
|
+
<v-card-title class="d-flex align-center pa-4 bg-gradient">
|
|
59
|
+
<v-icon :color="actionColor" size="32" class="mr-3">
|
|
60
|
+
{{ actionIcon }}
|
|
61
|
+
</v-icon>
|
|
62
|
+
<div class="flex-grow-1">
|
|
63
|
+
<div class="text-h6 font-weight-bold">
|
|
64
|
+
{{ audit.action }}
|
|
65
|
+
</div>
|
|
66
|
+
<div class="text-caption text-medium-emphasis">
|
|
67
|
+
{{ formattedDate }}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<v-chip :color="actionColor" variant="flat" size="small" class="font-weight-bold">
|
|
71
|
+
{{ audit.entity }}
|
|
72
|
+
</v-chip>
|
|
73
|
+
</v-card-title>
|
|
74
|
+
|
|
75
|
+
<v-divider />
|
|
76
|
+
|
|
77
|
+
<v-card-text class="pa-6">
|
|
78
|
+
<!-- Información General -->
|
|
79
|
+
<div class="mb-8">
|
|
80
|
+
<div class="section-header mb-4">
|
|
81
|
+
<v-icon color="primary" class="mr-2">mdi-information</v-icon>
|
|
82
|
+
<span class="text-subtitle-1 font-weight-bold">Información General</span>
|
|
83
|
+
</div>
|
|
84
|
+
<v-card variant="outlined" class="pa-4">
|
|
85
|
+
<div class="info-row mb-4">
|
|
86
|
+
<span class="info-label">Acción:</span>
|
|
87
|
+
<v-chip :color="actionColor" variant="tonal" size="small" class="ml-2">
|
|
88
|
+
{{ audit.action }}
|
|
89
|
+
</v-chip>
|
|
90
|
+
</div>
|
|
91
|
+
<v-divider class="my-3" />
|
|
92
|
+
<div class="info-row">
|
|
93
|
+
<span class="info-label">Entidad:</span>
|
|
94
|
+
<span class="info-value">{{ audit.entity }}</span>
|
|
95
|
+
</div>
|
|
96
|
+
</v-card>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<!-- Información del Usuario -->
|
|
100
|
+
<div class="mb-8">
|
|
101
|
+
<div class="section-header mb-4">
|
|
102
|
+
<v-icon color="primary" class="mr-2">mdi-account-circle</v-icon>
|
|
103
|
+
<span class="text-subtitle-1 font-weight-bold">Usuario</span>
|
|
104
|
+
</div>
|
|
105
|
+
<v-card variant="outlined" class="pa-4">
|
|
106
|
+
<div class="info-row mb-4">
|
|
107
|
+
<span class="info-label">Nombre de Usuario:</span>
|
|
108
|
+
<span class="info-value">{{ audit.user.username }}</span>
|
|
109
|
+
</div>
|
|
110
|
+
<v-divider class="my-3" />
|
|
111
|
+
<div class="info-row mb-4">
|
|
112
|
+
<span class="info-label">Rol:</span>
|
|
113
|
+
<v-chip size="small" color="primary" variant="tonal" class="ml-2">
|
|
114
|
+
{{ audit.user.rolName }}
|
|
115
|
+
</v-chip>
|
|
116
|
+
</div>
|
|
117
|
+
<v-divider class="my-3" />
|
|
118
|
+
<div class="info-row">
|
|
119
|
+
<span class="info-label">ID de Usuario:</span>
|
|
120
|
+
<div class="d-flex align-center gap-2 ml-2">
|
|
121
|
+
<span class="info-value font-monospace text-caption">{{ audit.user.id }}</span>
|
|
122
|
+
<v-btn
|
|
123
|
+
icon
|
|
124
|
+
size="x-small"
|
|
125
|
+
variant="text"
|
|
126
|
+
@click="copyToClipboard(audit.user.id)"
|
|
127
|
+
>
|
|
128
|
+
<v-icon size="small">mdi-content-copy</v-icon>
|
|
129
|
+
</v-btn>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</v-card>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<!-- Información de Conexión -->
|
|
136
|
+
<div class="mb-8">
|
|
137
|
+
<div class="section-header mb-4">
|
|
138
|
+
<v-icon color="info" class="mr-2">mdi-network</v-icon>
|
|
139
|
+
<span class="text-subtitle-1 font-weight-bold">Información de Conexión</span>
|
|
140
|
+
</div>
|
|
141
|
+
<v-card variant="outlined" class="pa-4">
|
|
142
|
+
<div class="info-row mb-4">
|
|
143
|
+
<span class="info-label">
|
|
144
|
+
<v-icon size="x-small" class="mr-1">mdi-ip</v-icon>
|
|
145
|
+
Dirección IP:
|
|
146
|
+
</span>
|
|
147
|
+
<div class="d-flex align-center gap-2 ml-2">
|
|
148
|
+
<v-chip size="small" variant="tonal" color="info">
|
|
149
|
+
{{ audit.ip }}
|
|
150
|
+
</v-chip>
|
|
151
|
+
<v-btn
|
|
152
|
+
icon
|
|
153
|
+
size="x-small"
|
|
154
|
+
variant="text"
|
|
155
|
+
@click="copyToClipboard(audit.ip)"
|
|
156
|
+
>
|
|
157
|
+
<v-icon size="small">mdi-content-copy</v-icon>
|
|
158
|
+
</v-btn>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
<v-divider class="my-3" />
|
|
162
|
+
<div class="info-row">
|
|
163
|
+
<span class="info-label">
|
|
164
|
+
<v-icon size="x-small" class="mr-1">mdi-web</v-icon>
|
|
165
|
+
User Agent:
|
|
166
|
+
</span>
|
|
167
|
+
<span class="info-value text-caption">{{ audit.userAgent }}</span>
|
|
168
|
+
</div>
|
|
169
|
+
</v-card>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<!-- Cambios Realizados -->
|
|
173
|
+
<div v-if="hasChanges" class="mb-8">
|
|
174
|
+
<div class="section-header mb-4">
|
|
175
|
+
<v-icon color="warning" class="mr-2">mdi-file-document-edit</v-icon>
|
|
176
|
+
<span class="text-subtitle-1 font-weight-bold">Cambios Realizados</span>
|
|
177
|
+
<v-chip size="x-small" color="warning" variant="tonal" class="ml-2">
|
|
178
|
+
{{ audit.changes?.length }} cambios
|
|
179
|
+
</v-chip>
|
|
180
|
+
</div>
|
|
181
|
+
<v-card variant="outlined" class="overflow-hidden">
|
|
182
|
+
<v-list lines="three" class="pa-0">
|
|
183
|
+
<template v-for="(change, index) in audit.changes" :key="index">
|
|
184
|
+
<v-list-item class="change-item">
|
|
185
|
+
<template #prepend>
|
|
186
|
+
<v-avatar color="warning" size="40" class="font-weight-bold">
|
|
187
|
+
<v-icon size="small">mdi-swap-horizontal</v-icon>
|
|
188
|
+
</v-avatar>
|
|
189
|
+
</template>
|
|
190
|
+
<div class="w-100">
|
|
191
|
+
<v-list-item-title class="font-weight-bold text-base mb-3">
|
|
192
|
+
Campo: {{ change.field }}
|
|
193
|
+
</v-list-item-title>
|
|
194
|
+
<div v-if="change.old" class="mb-2">
|
|
195
|
+
<span class="info-label">Valor Anterior:</span>
|
|
196
|
+
<v-chip
|
|
197
|
+
size="small"
|
|
198
|
+
color="error"
|
|
199
|
+
variant="tonal"
|
|
200
|
+
class="font-monospace ml-2 mt-1"
|
|
201
|
+
>
|
|
202
|
+
<v-icon start size="x-small">mdi-minus</v-icon>
|
|
203
|
+
{{ change.old }}
|
|
204
|
+
</v-chip>
|
|
205
|
+
</div>
|
|
206
|
+
<div v-if="change.new">
|
|
207
|
+
<span class="info-label">Valor Nuevo:</span>
|
|
208
|
+
<v-chip
|
|
209
|
+
size="small"
|
|
210
|
+
color="success"
|
|
211
|
+
variant="tonal"
|
|
212
|
+
class="font-monospace ml-2 mt-1"
|
|
213
|
+
>
|
|
214
|
+
<v-icon start size="x-small">mdi-plus</v-icon>
|
|
215
|
+
{{ change.new }}
|
|
216
|
+
</v-chip>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</v-list-item>
|
|
220
|
+
<v-divider v-if="index < audit.changes!.length - 1" />
|
|
221
|
+
</template>
|
|
222
|
+
</v-list>
|
|
223
|
+
</v-card>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<!-- Detalles Adicionales -->
|
|
227
|
+
<div v-if="audit.detail" class="mb-8">
|
|
228
|
+
<div class="section-header mb-4">
|
|
229
|
+
<v-icon color="info" class="mr-2">mdi-text-box</v-icon>
|
|
230
|
+
<span class="text-subtitle-1 font-weight-bold">Detalles Adicionales</span>
|
|
231
|
+
</div>
|
|
232
|
+
<v-card variant="outlined" class="pa-4">
|
|
233
|
+
<pre class="detail-text">{{ audit.detail }}</pre>
|
|
234
|
+
</v-card>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<!-- Información del Tenant -->
|
|
238
|
+
<div v-if="audit.tenant" class="mb-8">
|
|
239
|
+
<div class="section-header mb-4">
|
|
240
|
+
<v-icon color="secondary" class="mr-2">mdi-domain</v-icon>
|
|
241
|
+
<span class="text-subtitle-1 font-weight-bold">Tenant</span>
|
|
242
|
+
</div>
|
|
243
|
+
<v-card variant="outlined" class="pa-4">
|
|
244
|
+
<div class="info-row mb-4">
|
|
245
|
+
<span class="info-label">Nombre del Tenant:</span>
|
|
246
|
+
<span class="info-value">{{ audit.tenant.name }}</span>
|
|
247
|
+
</div>
|
|
248
|
+
<v-divider class="my-3" />
|
|
249
|
+
<div class="info-row">
|
|
250
|
+
<span class="info-label">ID del Tenant:</span>
|
|
251
|
+
<div class="d-flex align-center gap-2 ml-2">
|
|
252
|
+
<span class="info-value text-caption font-monospace">{{ audit.tenant.id }}</span>
|
|
253
|
+
<v-btn
|
|
254
|
+
icon
|
|
255
|
+
size="x-small"
|
|
256
|
+
variant="text"
|
|
257
|
+
@click="copyToClipboard(audit.tenant.id)"
|
|
258
|
+
>
|
|
259
|
+
<v-icon size="small">mdi-content-copy</v-icon>
|
|
260
|
+
</v-btn>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
</v-card>
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<!-- IDs de Sesión y Request -->
|
|
267
|
+
<div>
|
|
268
|
+
<div class="section-header mb-4">
|
|
269
|
+
<v-icon color="grey" class="mr-2">mdi-identifier</v-icon>
|
|
270
|
+
<span class="text-subtitle-1 font-weight-bold">Identificadores de Sesión</span>
|
|
271
|
+
</div>
|
|
272
|
+
<v-card variant="outlined" class="pa-4">
|
|
273
|
+
<div class="info-row mb-4">
|
|
274
|
+
<span class="info-label">ID de Auditoría:</span>
|
|
275
|
+
<div class="d-flex align-center gap-2 ml-2">
|
|
276
|
+
<span class="info-value text-caption font-monospace">{{ audit._id }}</span>
|
|
277
|
+
<v-btn
|
|
278
|
+
icon
|
|
279
|
+
size="x-small"
|
|
280
|
+
variant="text"
|
|
281
|
+
@click="copyToClipboard(audit._id)"
|
|
282
|
+
>
|
|
283
|
+
<v-icon size="small">mdi-content-copy</v-icon>
|
|
284
|
+
</v-btn>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
<v-divider class="my-3" />
|
|
288
|
+
<div v-if="audit.sessionId" class="info-row mb-4">
|
|
289
|
+
<span class="info-label">ID de Sesión:</span>
|
|
290
|
+
<div class="d-flex align-center gap-2 ml-2">
|
|
291
|
+
<span class="info-value text-caption font-monospace">{{ audit.sessionId }}</span>
|
|
292
|
+
<v-btn
|
|
293
|
+
icon
|
|
294
|
+
size="x-small"
|
|
295
|
+
variant="text"
|
|
296
|
+
@click="copyToClipboard(audit.sessionId)"
|
|
297
|
+
>
|
|
298
|
+
<v-icon size="small">mdi-content-copy</v-icon>
|
|
299
|
+
</v-btn>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
<v-divider class="my-3" />
|
|
303
|
+
<div v-if="audit.requestId" class="info-row">
|
|
304
|
+
<span class="info-label">ID de Petición:</span>
|
|
305
|
+
<div class="d-flex align-center gap-2 ml-2">
|
|
306
|
+
<span class="info-value text-caption font-monospace">{{ audit.requestId }}</span>
|
|
307
|
+
<v-btn
|
|
308
|
+
icon
|
|
309
|
+
size="x-small"
|
|
310
|
+
variant="text"
|
|
311
|
+
@click="copyToClipboard(audit.requestId)"
|
|
312
|
+
>
|
|
313
|
+
<v-icon size="small">mdi-content-copy</v-icon>
|
|
314
|
+
</v-btn>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
</v-card>
|
|
318
|
+
</div>
|
|
319
|
+
</v-card-text>
|
|
320
|
+
</v-card>
|
|
321
|
+
</template>
|
|
322
|
+
|
|
323
|
+
<style scoped>
|
|
324
|
+
.audit-view-card {
|
|
325
|
+
border-radius: 12px;
|
|
326
|
+
overflow: hidden;
|
|
327
|
+
transition: all 0.3s ease;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.audit-view-card:hover {
|
|
331
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12) !important;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.bg-gradient {
|
|
335
|
+
background: linear-gradient(135deg, rgb(var(--v-theme-surface-variant)) 0%, rgb(var(--v-theme-surface)) 100%);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.section-header {
|
|
339
|
+
display: flex;
|
|
340
|
+
align-items: center;
|
|
341
|
+
padding-bottom: 8px;
|
|
342
|
+
border-bottom: 2px solid rgba(var(--v-theme-primary), 0.2);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.info-row {
|
|
346
|
+
display: flex;
|
|
347
|
+
flex-direction: column;
|
|
348
|
+
gap: 6px;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.info-label {
|
|
352
|
+
font-size: 0.875rem;
|
|
353
|
+
font-weight: 700;
|
|
354
|
+
color: rgb(var(--v-theme-on-surface-variant));
|
|
355
|
+
text-transform: uppercase;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.info-value {
|
|
359
|
+
font-size: 0.875rem;
|
|
360
|
+
color: rgb(var(--v-theme-on-surface));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.detail-text {
|
|
364
|
+
white-space: pre-wrap;
|
|
365
|
+
word-break: break-word;
|
|
366
|
+
margin: 0;
|
|
367
|
+
padding: 0;
|
|
368
|
+
font-family: 'Courier New', monospace;
|
|
369
|
+
font-size: 0.875rem;
|
|
370
|
+
color: rgb(var(--v-theme-on-surface));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.change-item {
|
|
374
|
+
transition: background-color 0.2s ease;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.change-item:hover {
|
|
378
|
+
background-color: rgba(var(--v-theme-surface-variant), 0.5);
|
|
379
|
+
}
|
|
380
|
+
</style>
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import {EntityCrud} from "@drax/crud-vue";
|
|
2
|
+
import type {
|
|
3
|
+
IDraxCrudProvider,
|
|
4
|
+
IEntityCrud,
|
|
5
|
+
IEntityCrudField,
|
|
6
|
+
IEntityCrudFilter,
|
|
7
|
+
IEntityCrudHeader,
|
|
8
|
+
IEntityCrudPermissions,
|
|
9
|
+
IEntityCrudRefs,
|
|
10
|
+
IEntityCrudRules
|
|
11
|
+
} from "@drax/crud-share";
|
|
12
|
+
import {AuditProvider} from "@drax/audit-front";
|
|
13
|
+
|
|
14
|
+
//Import EntityCrud Refs
|
|
15
|
+
|
|
16
|
+
class AuditCrud extends EntityCrud implements IEntityCrud {
|
|
17
|
+
|
|
18
|
+
static singleton: AuditCrud
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
super();
|
|
22
|
+
this.name = 'Audit'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static get instance(): AuditCrud {
|
|
26
|
+
if (!AuditCrud.singleton) {
|
|
27
|
+
AuditCrud.singleton = new AuditCrud()
|
|
28
|
+
}
|
|
29
|
+
return AuditCrud.singleton
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get permissions(): IEntityCrudPermissions {
|
|
33
|
+
return {
|
|
34
|
+
manage: 'audit:manage',
|
|
35
|
+
view: 'audit:view',
|
|
36
|
+
create: 'audit:create',
|
|
37
|
+
update: 'audit:update',
|
|
38
|
+
delete: 'audit:delete'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get headers(): IEntityCrudHeader[] {
|
|
43
|
+
return [
|
|
44
|
+
{title: 'createdAt',key:'createdAt', align: 'start'},
|
|
45
|
+
{title: 'entity',key:'entity', align: 'start'},
|
|
46
|
+
{title: 'user',key:'user', align: 'start'},
|
|
47
|
+
{title: 'action',key:'action', align: 'start'},
|
|
48
|
+
{title: 'ip',key:'ip', align: 'start'},
|
|
49
|
+
{title: 'userAgent',key:'userAgent', align: 'start'},
|
|
50
|
+
{title: 'changes',key:'changes', align: 'start'},
|
|
51
|
+
{title: 'sessionId',key:'sessionId', align: 'start'},
|
|
52
|
+
{title: 'requestId',key:'requestId', align: 'start'},
|
|
53
|
+
{title: 'detail',key:'detail', align: 'start'},
|
|
54
|
+
{title: 'tenant',key:'tenant', align: 'start'}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get selectedHeaders(): string[] {
|
|
59
|
+
return this.headers
|
|
60
|
+
.filter(header => ['detail','sessionId','requestId','userAgent','ip'].includes(header.key))
|
|
61
|
+
.map(header => header.key)
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get actionHeaders(): IEntityCrudHeader[] {
|
|
66
|
+
return [
|
|
67
|
+
{
|
|
68
|
+
title: 'action.actions',
|
|
69
|
+
key: 'actions',
|
|
70
|
+
sortable: false,
|
|
71
|
+
align: 'center',
|
|
72
|
+
minWidth: '190px'
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get provider(): IDraxCrudProvider<any, any, any> {
|
|
78
|
+
return AuditProvider.instance
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get refs(): IEntityCrudRefs {
|
|
82
|
+
return {}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get rules(): IEntityCrudRules {
|
|
86
|
+
return {
|
|
87
|
+
entity: [(v: any) => !!v || 'validation.required'],
|
|
88
|
+
user: [(v: any) => !!v || 'validation.required'],
|
|
89
|
+
action: [(v: any) => !!v || 'validation.required'],
|
|
90
|
+
ip: [(v: any) => !!v || 'validation.required'],
|
|
91
|
+
userAgent: [(v: any) => !!v || 'validation.required'],
|
|
92
|
+
changes: [],
|
|
93
|
+
sessionId: [],
|
|
94
|
+
requestId: [],
|
|
95
|
+
detail: [],
|
|
96
|
+
tenant: []
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get fields(): IEntityCrudField[] {
|
|
101
|
+
return [
|
|
102
|
+
{name: 'entity', type: 'string', label: 'entity', default: ''},
|
|
103
|
+
{
|
|
104
|
+
name: 'user',
|
|
105
|
+
type: 'object',
|
|
106
|
+
label: 'user',
|
|
107
|
+
default: {"id": "''", "username": "''", "rolName": "''"},
|
|
108
|
+
objectFields: [{name: 'id', type: 'string', label: 'id', default: ''},
|
|
109
|
+
{name: 'username', type: 'string', label: 'username', default: ''},
|
|
110
|
+
{name: 'rolName', type: 'string', label: 'rolName', default: ''}]
|
|
111
|
+
},
|
|
112
|
+
{name: 'action', type: 'string', label: 'action', default: ''},
|
|
113
|
+
{name: 'ip', type: 'string', label: 'ip', default: ''},
|
|
114
|
+
{name: 'userAgent', type: 'string', label: 'userAgent', default: ''},
|
|
115
|
+
{
|
|
116
|
+
name: 'changes',
|
|
117
|
+
type: 'array.object',
|
|
118
|
+
label: 'changes',
|
|
119
|
+
default: [],
|
|
120
|
+
objectFields: [{name: 'field', type: 'string', label: 'field', default: ''},
|
|
121
|
+
{name: 'old', type: 'string', label: 'old', default: ''},
|
|
122
|
+
{name: 'new', type: 'string', label: 'new', default: ''}]
|
|
123
|
+
},
|
|
124
|
+
{name: 'sessionId', type: 'string', label: 'sessionId', default: ''},
|
|
125
|
+
{name: 'requestId', type: 'string', label: 'requestId', default: ''},
|
|
126
|
+
{name: 'detail', type: 'longString', label: 'detail', default: ''},
|
|
127
|
+
{
|
|
128
|
+
name: 'tenant',
|
|
129
|
+
type: 'object',
|
|
130
|
+
label: 'tenant',
|
|
131
|
+
default: {"id": "''", "name": "''"},
|
|
132
|
+
objectFields: [{name: 'id', type: 'string', label: 'id', default: ''},
|
|
133
|
+
{name: 'name', type: 'string', label: 'name', default: ''}]
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get filters(): IEntityCrudFilter[] {
|
|
139
|
+
return [
|
|
140
|
+
//{name: '_id', type: 'string', label: 'ID', default: '', operator: 'eq' },
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
get isViewable() {
|
|
145
|
+
return true
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
get isEditable() {
|
|
149
|
+
return false
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
get isCreatable() {
|
|
153
|
+
return false
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
get isDeletable() {
|
|
157
|
+
return false
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
get isExportable() {
|
|
161
|
+
return true
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
get exportFormats() {
|
|
165
|
+
return ['CSV', 'JSON']
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
get exportHeaders() {
|
|
169
|
+
return ['_id']
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
get isImportable() {
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
get isColumnSelectable() {
|
|
177
|
+
return true
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
get isGroupable() {
|
|
181
|
+
return true
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
get importFormats() {
|
|
185
|
+
return ['CSV', 'JSON']
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
get dialogFullscreen() {
|
|
189
|
+
return false
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
get tabs() {
|
|
193
|
+
return []
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
get menus() {
|
|
197
|
+
return []
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export default AuditCrud
|
|
204
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import AuditRoutes from './routes/index'
|
|
2
|
+
import AuditCrudPage from './pages/crud/AuditCrudPage.vue'
|
|
3
|
+
import AuditView from './components/AuditView.vue'
|
|
4
|
+
import AuditCrud from './cruds/AuditCrud'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
//ROUTES
|
|
9
|
+
AuditRoutes,
|
|
10
|
+
//PAGES
|
|
11
|
+
AuditCrudPage,
|
|
12
|
+
//COMPONENTS
|
|
13
|
+
AuditView,
|
|
14
|
+
//CRUD
|
|
15
|
+
AuditCrud,
|
|
16
|
+
//I18N
|
|
17
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
import AuditCrud from '../../cruds/AuditCrud'
|
|
4
|
+
import {Crud, useCrudStore} from "@drax/crud-vue";
|
|
5
|
+
import {formatDateTime} from "@drax/common-front"
|
|
6
|
+
import AuditView from "../../components/AuditView.vue";
|
|
7
|
+
|
|
8
|
+
const store = useCrudStore();
|
|
9
|
+
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<crud :entity="AuditCrud.instance">
|
|
14
|
+
|
|
15
|
+
<template v-slot:item.createdAt="{value}">
|
|
16
|
+
{{ formatDateTime(value) }}
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<template v-slot:form>
|
|
20
|
+
<audit-view :audit="store.form"></audit-view>
|
|
21
|
+
</template>
|
|
22
|
+
</crud>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<style scoped>
|
|
26
|
+
|
|
27
|
+
</style>
|
|
28
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
import AuditCrudPage from "../pages/crud/AuditCrudPage.vue";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const AuditCrudRoute = [
|
|
6
|
+
{
|
|
7
|
+
name: 'AuditCrudPage',
|
|
8
|
+
path: '/crud/audit',
|
|
9
|
+
component: AuditCrudPage,
|
|
10
|
+
meta: {
|
|
11
|
+
auth: true,
|
|
12
|
+
permission: 'audit:manage',
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
export default AuditCrudRoute
|
|
18
|
+
export { AuditCrudRoute }
|