@drax/audit-back 0.38.0 → 0.39.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.
@@ -3,6 +3,7 @@ import uniqueValidator from 'mongoose-unique-validator';
3
3
  import mongoosePaginate from 'mongoose-paginate-v2';
4
4
  const AuditSchema = new mongoose.Schema({
5
5
  entity: { type: String, required: true, index: true, unique: false },
6
+ resourceId: { type: String, required: false, index: true, unique: false },
6
7
  user: {
7
8
  id: { type: String, required: true, index: true, unique: false },
8
9
  username: { type: String, required: true, index: false, unique: false },
@@ -13,15 +14,19 @@ const AuditSchema = new mongoose.Schema({
13
14
  userAgent: { type: String, required: false, index: false, unique: false },
14
15
  changes: [{
15
16
  field: { type: String, required: true, index: false, unique: false },
16
- old: { type: String, required: false, index: false, unique: false },
17
- new: { type: String, required: false, index: false, unique: false }
17
+ old: { type: mongoose.Schema.Types.Mixed, required: false, index: false, unique: false },
18
+ new: { type: mongoose.Schema.Types.Mixed, required: false, index: false, unique: false }
18
19
  }],
19
20
  sessionId: { type: String, required: false, index: true, unique: false },
20
21
  requestId: { type: String, required: false, index: true, unique: false },
21
22
  detail: { type: String, required: false, index: false, unique: false },
22
23
  tenant: {
23
24
  id: { type: String, required: false, index: true, unique: false },
24
- name: { type: String, required: false, index: false, unique: false }
25
+ name: { type: String, required: false, index: true, unique: false }
26
+ },
27
+ apiKey: {
28
+ id: { type: String, required: false, index: true, unique: false },
29
+ name: { type: String, required: false, index: true, unique: false }
25
30
  }
26
31
  }, { timestamps: true });
27
32
  AuditSchema.plugin(uniqueValidator, { message: 'validation.unique' });
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  const AuditBaseSchema = z.object({
3
3
  entity: z.string().min(1, 'validation.required'),
4
+ resourceId: z.string().optional().nullable(),
4
5
  user: z.object({
5
6
  id: z.string().min(1, 'validation.required'),
6
7
  username: z.string().min(1, 'validation.required'),
@@ -11,8 +12,8 @@ const AuditBaseSchema = z.object({
11
12
  userAgent: z.string().optional().nullable(),
12
13
  changes: z.array(z.object({
13
14
  field: z.string().min(1, 'validation.required'),
14
- old: z.string().optional().nullable(),
15
- new: z.string().optional().nullable()
15
+ old: z.any().optional().nullable(),
16
+ new: z.any().optional().nullable()
16
17
  })).optional(),
17
18
  sessionId: z.string().optional().nullable(),
18
19
  requestId: z.string().optional().nullable(),
@@ -20,6 +21,10 @@ const AuditBaseSchema = z.object({
20
21
  tenant: z.object({
21
22
  id: z.string().optional().nullable(),
22
23
  name: z.string().optional().nullable()
24
+ }).optional().nullable(),
25
+ apiKey: z.object({
26
+ id: z.string().optional().nullable(),
27
+ name: z.string().optional().nullable()
23
28
  }).optional().nullable()
24
29
  });
25
30
  const AuditSchema = AuditBaseSchema
@@ -6,11 +6,12 @@ async function RegisterCrudEvent(crudEventData) {
6
6
  if (!preItem) {
7
7
  return changes;
8
8
  }
9
+ const ignoredFields = ['_id', 'createdAt', 'updatedAt', 'createdBy', '$__', '$isNew'];
9
10
  // Si no hay postItem (eliminación), registrar todos los campos como eliminados
10
11
  if (!postItem) {
11
12
  Object.keys(preItem).forEach(key => {
12
13
  // Ignorar campos internos de MongoDB y timestamps
13
- if (!key.startsWith('_') && key !== 'createdAt' && key !== 'updatedAt') {
14
+ if (!key.startsWith('_') && !ignoredFields.includes(key)) {
14
15
  changes.push({
15
16
  field: key,
16
17
  old: preItem[key],
@@ -20,17 +21,49 @@ async function RegisterCrudEvent(crudEventData) {
20
21
  });
21
22
  return changes;
22
23
  }
24
+ // Función para comparar valores teniendo en cuenta ObjectId y populated
25
+ function areValuesEqual(oldValue, newValue) {
26
+ // Si son exactamente iguales
27
+ if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
28
+ return true;
29
+ }
30
+ // Comparar referencias (ObjectId vs objeto poblado)
31
+ const oldId = extractId(oldValue);
32
+ const newId = extractId(newValue);
33
+ if (oldId && newId) {
34
+ return oldId.toString() === newId.toString();
35
+ }
36
+ // Si uno es array y el otro también, comparar elementos
37
+ if (Array.isArray(oldValue) && Array.isArray(newValue)) {
38
+ if (oldValue.length !== newValue.length) {
39
+ return false;
40
+ }
41
+ return oldValue.every((item, index) => areValuesEqual(item, newValue[index]));
42
+ }
43
+ return false;
44
+ }
45
+ function extractId(value) {
46
+ // Si es un ObjectId directo
47
+ if (value && typeof value === 'object' && value._id) {
48
+ return value._id;
49
+ }
50
+ // Si es directamente un ObjectId
51
+ if (value && typeof value === 'object' && value.toString && value.constructor.name === 'ObjectId') {
52
+ return value;
53
+ }
54
+ return null;
55
+ }
23
56
  // Obtener todas las claves únicas de ambos objetos
24
57
  const allKeys = new Set([...Object.keys(preItem), ...Object.keys(postItem)]);
25
58
  allKeys.forEach(key => {
26
59
  // Ignorar campos internos de MongoDB y timestamps
27
- if (key.startsWith('_') || key === 'createdAt' || key === 'updatedAt') {
60
+ if (key.startsWith('_') || ignoredFields.includes(key)) {
28
61
  return;
29
62
  }
30
63
  const oldValue = preItem[key];
31
64
  const newValue = postItem[key];
32
- // Comparar valores
33
- if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
65
+ // Comparar valores usando la nueva función
66
+ if (!areValuesEqual(oldValue, newValue)) {
34
67
  changes.push({
35
68
  field: key,
36
69
  old: oldValue,
@@ -43,23 +76,28 @@ async function RegisterCrudEvent(crudEventData) {
43
76
  let changes = diff(crudEventData.preItem, crudEventData.postItem);
44
77
  let data = {
45
78
  action: crudEventData.action,
79
+ resourceId: crudEventData.resourceId || crudEventData.postItem?._id?.toString() || crudEventData.preItem?._id?.toString(),
46
80
  changes: changes,
47
81
  createdAt: crudEventData.timestamp,
48
- detail: "",
82
+ detail: crudEventData.detail,
49
83
  entity: crudEventData.entity,
50
84
  tenant: {
51
- id: crudEventData.user.tenant.id,
52
- name: crudEventData.user.tenant.name
85
+ id: crudEventData.user?.tenant?.id,
86
+ name: crudEventData.user?.tenant?.name
53
87
  },
54
88
  user: {
55
89
  id: crudEventData.user.id,
56
90
  username: crudEventData.user.username,
57
- rolName: crudEventData.user.role.name,
91
+ rolName: crudEventData.user.role?.name,
92
+ },
93
+ apiKey: {
94
+ id: crudEventData.user?.apiKey?.id,
95
+ name: crudEventData.user?.apiKey?.name
58
96
  },
59
97
  ip: crudEventData.ip,
60
98
  userAgent: crudEventData.userAgent,
61
99
  sessionId: crudEventData.user.session,
62
- requestId: crudEventData.requestId
100
+ requestId: crudEventData.requestId,
63
101
  };
64
102
  return await AuditServiceFactory.instance.create(data);
65
103
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.38.0",
6
+ "version": "0.39.0",
7
7
  "description": "Audit backend",
8
8
  "main": "dist/index.js",
9
9
  "types": "types/index.d.ts",
@@ -22,8 +22,8 @@
22
22
  "author": "Cristian Incarnato & Drax Team",
23
23
  "license": "ISC",
24
24
  "dependencies": {
25
- "@drax/crud-back": "^0.38.0",
26
- "@drax/crud-share": "^0.38.0",
25
+ "@drax/crud-back": "^0.39.0",
26
+ "@drax/crud-share": "^0.39.0",
27
27
  "mongoose": "^8.6.3",
28
28
  "mongoose-paginate-v2": "^1.8.3"
29
29
  },
@@ -39,5 +39,5 @@
39
39
  "tsc-alias": "^1.8.10",
40
40
  "typescript": "^5.6.2"
41
41
  },
42
- "gitHead": "43c90f3c12165e7527edefbc80dd327a59236dd5"
42
+ "gitHead": "b019c40f954cf60e4ff61c53e27d5bafaea6f16c"
43
43
  }
@@ -6,6 +6,7 @@ import type {IAudit} from '@drax/audit-share'
6
6
 
7
7
  const AuditSchema = new mongoose.Schema<IAudit>({
8
8
  entity: {type: String, required: true, index: true, unique: false},
9
+ resourceId: {type: String, required: false, index: true, unique: false},
9
10
  user: {
10
11
  id: {type: String, required: true, index: true, unique: false},
11
12
  username: {type: String, required: true, index: false, unique: false},
@@ -16,15 +17,19 @@ const AuditSchema = new mongoose.Schema<IAudit>({
16
17
  userAgent: {type: String, required: false, index: false, unique: false},
17
18
  changes: [{
18
19
  field: {type: String, required: true, index: false, unique: false},
19
- old: {type: String, required: false, index: false, unique: false},
20
- new: {type: String, required: false, index: false, unique: false}
20
+ old: {type: mongoose.Schema.Types.Mixed, required: false, index: false, unique: false},
21
+ new: {type: mongoose.Schema.Types.Mixed, required: false, index: false, unique: false}
21
22
  }],
22
23
  sessionId: {type: String, required: false, index: true, unique: false},
23
24
  requestId: {type: String, required: false, index: true, unique: false},
24
25
  detail: {type: String, required: false, index: false, unique: false},
25
26
  tenant: {
26
27
  id: {type: String, required: false, index: true, unique: false},
27
- name: {type: String, required: false, index: false, unique: false}
28
+ name: {type: String, required: false, index: true, unique: false}
29
+ },
30
+ apiKey: {
31
+ id: {type: String, required: false, index: true, unique: false},
32
+ name: {type: String, required: false, index: true, unique: false}
28
33
  }
29
34
  }, {timestamps: true});
30
35
 
@@ -3,6 +3,7 @@ import {z} from 'zod';
3
3
 
4
4
  const AuditBaseSchema = z.object({
5
5
  entity: z.string().min(1, 'validation.required'),
6
+ resourceId: z.string().optional().nullable(),
6
7
  user: z.object({
7
8
  id: z.string().min(1, 'validation.required'),
8
9
  username: z.string().min(1, 'validation.required'),
@@ -14,8 +15,8 @@ const AuditBaseSchema = z.object({
14
15
  changes: z.array(
15
16
  z.object({
16
17
  field: z.string().min(1, 'validation.required'),
17
- old: z.string().optional().nullable(),
18
- new: z.string().optional().nullable()
18
+ old: z.any().optional().nullable(),
19
+ new: z.any().optional().nullable()
19
20
  })
20
21
  ).optional(),
21
22
  sessionId: z.string().optional().nullable(),
@@ -24,6 +25,10 @@ const AuditBaseSchema = z.object({
24
25
  tenant: z.object({
25
26
  id: z.string().optional().nullable(),
26
27
  name: z.string().optional().nullable()
28
+ }).optional().nullable(),
29
+ apiKey: z.object({
30
+ id: z.string().optional().nullable(),
31
+ name: z.string().optional().nullable()
27
32
  }).optional().nullable()
28
33
  });
29
34
 
@@ -18,11 +18,13 @@ async function RegisterCrudEvent(crudEventData: IDraxCrudEvent){
18
18
  return changes;
19
19
  }
20
20
 
21
+ const ignoredFields = ['_id', 'createdAt', 'updatedAt', 'createdBy','$__','$isNew'];
22
+
21
23
  // Si no hay postItem (eliminación), registrar todos los campos como eliminados
22
24
  if (!postItem) {
23
25
  Object.keys(preItem).forEach(key => {
24
26
  // Ignorar campos internos de MongoDB y timestamps
25
- if (!key.startsWith('_') && key !== 'createdAt' && key !== 'updatedAt') {
27
+ if (!key.startsWith('_') && !ignoredFields.includes(key)) {
26
28
  changes.push({
27
29
  field: key,
28
30
  old: preItem[key],
@@ -33,20 +35,60 @@ async function RegisterCrudEvent(crudEventData: IDraxCrudEvent){
33
35
  return changes;
34
36
  }
35
37
 
38
+ // Función para comparar valores teniendo en cuenta ObjectId y populated
39
+ function areValuesEqual(oldValue: any, newValue: any): boolean {
40
+ // Si son exactamente iguales
41
+ if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
42
+ return true;
43
+ }
44
+
45
+ // Comparar referencias (ObjectId vs objeto poblado)
46
+ const oldId = extractId(oldValue);
47
+ const newId = extractId(newValue);
48
+
49
+ if (oldId && newId) {
50
+ return oldId.toString() === newId.toString();
51
+ }
52
+
53
+ // Si uno es array y el otro también, comparar elementos
54
+ if (Array.isArray(oldValue) && Array.isArray(newValue)) {
55
+ if (oldValue.length !== newValue.length) {
56
+ return false;
57
+ }
58
+ return oldValue.every((item, index) =>
59
+ areValuesEqual(item, newValue[index])
60
+ );
61
+ }
62
+
63
+ return false;
64
+ }
65
+
66
+ function extractId(value: any): any {
67
+ // Si es un ObjectId directo
68
+ if (value && typeof value === 'object' && value._id) {
69
+ return value._id;
70
+ }
71
+ // Si es directamente un ObjectId
72
+ if (value && typeof value === 'object' && value.toString && value.constructor.name === 'ObjectId') {
73
+ return value;
74
+ }
75
+ return null;
76
+ }
77
+
36
78
  // Obtener todas las claves únicas de ambos objetos
37
79
  const allKeys = new Set([...Object.keys(preItem), ...Object.keys(postItem)]);
38
80
 
39
81
  allKeys.forEach(key => {
40
82
  // Ignorar campos internos de MongoDB y timestamps
41
- if (key.startsWith('_') || key === 'createdAt' || key === 'updatedAt') {
83
+ if (key.startsWith('_') || ignoredFields.includes(key)) {
42
84
  return;
43
85
  }
44
86
 
45
87
  const oldValue = preItem[key];
46
88
  const newValue = postItem[key];
47
89
 
48
- // Comparar valores
49
- if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
90
+ // Comparar valores usando la nueva función
91
+ if (!areValuesEqual(oldValue, newValue)) {
50
92
  changes.push({
51
93
  field: key,
52
94
  old: oldValue,
@@ -62,23 +104,28 @@ async function RegisterCrudEvent(crudEventData: IDraxCrudEvent){
62
104
 
63
105
  let data: IAuditBase = {
64
106
  action: crudEventData.action,
107
+ resourceId: crudEventData.resourceId || crudEventData.postItem?._id?.toString() || crudEventData.preItem?._id?.toString(),
65
108
  changes: changes,
66
109
  createdAt: crudEventData.timestamp,
67
- detail: "",
110
+ detail: crudEventData.detail,
68
111
  entity: crudEventData.entity,
69
112
  tenant: {
70
- id: crudEventData.user.tenant.id,
71
- name: crudEventData.user.tenant.name
113
+ id: crudEventData.user?.tenant?.id,
114
+ name: crudEventData.user?.tenant?.name
72
115
  },
73
116
  user: {
74
117
  id: crudEventData.user.id,
75
118
  username: crudEventData.user.username,
76
- rolName: crudEventData.user.role.name,
119
+ rolName: crudEventData.user.role?.name,
120
+ },
121
+ apiKey: {
122
+ id: crudEventData.user?.apiKey?.id,
123
+ name: crudEventData.user?.apiKey?.name
77
124
  },
78
125
  ip: crudEventData.ip,
79
126
  userAgent: crudEventData.userAgent,
80
127
  sessionId: crudEventData.user.session,
81
- requestId: crudEventData.requestId
128
+ requestId: crudEventData.requestId,
82
129
  }
83
130
 
84
131
  return await AuditServiceFactory.instance.create(data)