@strapi/admin 5.52.3 → 5.53.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/dist/admin/admin/src/translations/en.json.js +18 -0
- package/dist/admin/admin/src/translations/en.json.js.map +1 -1
- package/dist/admin/admin/src/translations/en.json.mjs +18 -0
- package/dist/admin/admin/src/translations/en.json.mjs.map +1 -1
- package/dist/admin/admin/src/utils/getFetchClient.js +118 -43
- package/dist/admin/admin/src/utils/getFetchClient.js.map +1 -1
- package/dist/admin/admin/src/utils/getFetchClient.mjs +118 -45
- package/dist/admin/admin/src/utils/getFetchClient.mjs.map +1 -1
- package/dist/admin/ee/admin/src/constants.js +6 -0
- package/dist/admin/ee/admin/src/constants.js.map +1 -1
- package/dist/admin/ee/admin/src/constants.mjs +6 -0
- package/dist/admin/ee/admin/src/constants.mjs.map +1 -1
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js +31 -3
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js.map +1 -1
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs +33 -5
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs.map +1 -1
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js +177 -0
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js.map +1 -0
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs +175 -0
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs.map +1 -0
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js +173 -0
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js.map +1 -0
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs +151 -0
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs.map +1 -0
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js +10 -1
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js.map +1 -1
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs +10 -1
- package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs.map +1 -1
- package/dist/admin/index.js +2 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/shared/utils/audit-log-export.js +16 -0
- package/dist/admin/shared/utils/audit-log-export.js.map +1 -0
- package/dist/admin/shared/utils/audit-log-export.mjs +9 -0
- package/dist/admin/shared/utils/audit-log-export.mjs.map +1 -0
- package/dist/admin/src/utils/getFetchClient.d.ts +9 -2
- package/dist/admin/tests/store.d.ts +4 -0
- package/dist/admin/tests/utils.d.ts +4 -0
- package/dist/ee/admin/src/constants.d.ts +4 -0
- package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.d.ts +10 -0
- package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.d.ts +18 -0
- package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.d.ts +9 -0
- package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts +1 -0
- package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts.map +1 -1
- package/dist/ee/server/src/audit-logs/routes/audit-logs.d.ts.map +1 -1
- package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts +24 -3
- package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts.map +1 -1
- package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts +8 -2
- package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts.map +1 -1
- package/dist/ee/server/src/audit-logs/utils/csv.d.ts +5 -0
- package/dist/ee/server/src/audit-logs/utils/csv.d.ts.map +1 -0
- package/dist/ee/server/src/audit-logs/utils/export-token.d.ts +5 -0
- package/dist/ee/server/src/audit-logs/utils/export-token.d.ts.map +1 -0
- package/dist/ee/server/src/audit-logs/utils.d.ts +10 -0
- package/dist/ee/server/src/audit-logs/utils.d.ts.map +1 -0
- package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts +2 -0
- package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts.map +1 -1
- package/dist/ee/server/src/config/admin-actions.d.ts.map +1 -1
- package/dist/ee/server/src/index.d.ts +1 -0
- package/dist/ee/server/src/index.d.ts.map +1 -1
- package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js +50 -0
- package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js.map +1 -1
- package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs +51 -1
- package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs.map +1 -1
- package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js +13 -4
- package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js.map +1 -1
- package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs +13 -4
- package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs.map +1 -1
- package/dist/server/ee/server/src/audit-logs/services/audit-logs.js +201 -7
- package/dist/server/ee/server/src/audit-logs/services/audit-logs.js.map +1 -1
- package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs +201 -8
- package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs.map +1 -1
- package/dist/server/ee/server/src/audit-logs/services/lifecycles.js +119 -34
- package/dist/server/ee/server/src/audit-logs/services/lifecycles.js.map +1 -1
- package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs +119 -34
- package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs.map +1 -1
- package/dist/server/ee/server/src/audit-logs/utils/csv.js +33 -0
- package/dist/server/ee/server/src/audit-logs/utils/csv.js.map +1 -0
- package/dist/server/ee/server/src/audit-logs/utils/csv.mjs +29 -0
- package/dist/server/ee/server/src/audit-logs/utils/csv.mjs.map +1 -0
- package/dist/server/ee/server/src/audit-logs/utils/export-token.js +33 -0
- package/dist/server/ee/server/src/audit-logs/utils/export-token.js.map +1 -0
- package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs +29 -0
- package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs.map +1 -0
- package/dist/server/ee/server/src/audit-logs/utils.js +14 -0
- package/dist/server/ee/server/src/audit-logs/utils.js.map +1 -0
- package/dist/server/ee/server/src/audit-logs/utils.mjs +12 -0
- package/dist/server/ee/server/src/audit-logs/utils.mjs.map +1 -0
- package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js +11 -0
- package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js.map +1 -1
- package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs +11 -1
- package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs.map +1 -1
- package/dist/server/ee/server/src/config/admin-actions.js +8 -0
- package/dist/server/ee/server/src/config/admin-actions.js.map +1 -1
- package/dist/server/ee/server/src/config/admin-actions.mjs +8 -0
- package/dist/server/ee/server/src/config/admin-actions.mjs.map +1 -1
- package/dist/server/ee/server/src/index.js +10 -0
- package/dist/server/ee/server/src/index.js.map +1 -1
- package/dist/server/ee/server/src/index.mjs +10 -0
- package/dist/server/ee/server/src/index.mjs.map +1 -1
- package/dist/server/shared/utils/audit-log-export.js +22 -0
- package/dist/server/shared/utils/audit-log-export.js.map +1 -0
- package/dist/server/shared/utils/audit-log-export.mjs +12 -0
- package/dist/server/shared/utils/audit-log-export.mjs.map +1 -0
- package/dist/shared/contracts/audit-logs.d.ts +15 -1
- package/dist/shared/contracts/audit-logs.d.ts.map +1 -1
- package/dist/shared/utils/audit-log-export.d.ts +9 -0
- package/dist/shared/utils/audit-log-export.d.ts.map +1 -0
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-logs.js","sources":["../../../../../../../ee/server/src/audit-logs/routes/audit-logs.ts"],"sourcesContent":["import { enableFeatureMiddleware } from '../../routes/utils';\n\nconst getRouteConfig = () => ({\n middlewares: [enableFeatureMiddleware('audit-logs')],\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions
|
|
1
|
+
{"version":3,"file":"audit-logs.js","sources":["../../../../../../../ee/server/src/audit-logs/routes/audit-logs.ts"],"sourcesContent":["import { enableFeatureMiddleware } from '../../routes/utils';\n\nconst getRouteConfig = (actions: string[] = ['admin::audit-logs.read']) => ({\n middlewares: [enableFeatureMiddleware('audit-logs')],\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions,\n },\n },\n ],\n});\n\nexport default {\n type: 'admin',\n routes: [\n {\n method: 'GET',\n path: '/audit-logs',\n handler: 'audit-logs.findMany',\n config: getRouteConfig(),\n },\n {\n method: 'GET',\n path: '/audit-logs/users',\n handler: 'audit-logs.findManyUsers',\n config: getRouteConfig(),\n },\n {\n method: 'GET',\n path: '/audit-logs/export',\n handler: 'audit-logs.export',\n config: getRouteConfig(['admin::audit-logs.read', 'admin::audit-logs.export']),\n },\n {\n method: 'GET',\n path: '/audit-logs/:id',\n handler: 'audit-logs.findOne',\n config: getRouteConfig(),\n },\n ],\n};\n"],"names":["getRouteConfig","actions","middlewares","enableFeatureMiddleware","policies","name","config","type","routes","method","path","handler"],"mappings":";;;;AAEA,MAAMA,cAAAA,GAAiB,CAACC,OAAAA,GAAoB;AAAC,IAAA;AAAyB,CAAA,IAAM;QAC1EC,WAAAA,EAAa;YAACC,6BAAAA,CAAwB,YAAA;AAAc,SAAA;QACpDC,QAAAA,EAAU;AACR,YAAA,6BAAA;AACA,YAAA;gBACEC,IAAAA,EAAM,uBAAA;gBACNC,MAAAA,EAAQ;AACNL,oBAAAA;AACF;AACF;AACD;KACH,CAAA;AAEA,sBAAe;IACbM,IAAAA,EAAM,OAAA;IACNC,MAAAA,EAAQ;AACN,QAAA;YACEC,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,aAAA;YACNC,OAAAA,EAAS,qBAAA;YACTL,MAAAA,EAAQN,cAAAA;AACV,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,mBAAA;YACNC,OAAAA,EAAS,0BAAA;YACTL,MAAAA,EAAQN,cAAAA;AACV,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,oBAAA;YACNC,OAAAA,EAAS,mBAAA;AACTL,YAAAA,MAAAA,EAAQN,cAAAA,CAAe;AAAC,gBAAA,wBAAA;AAA0B,gBAAA;AAA2B,aAAA;AAC/E,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,iBAAA;YACNC,OAAAA,EAAS,oBAAA;YACTL,MAAAA,EAAQN,cAAAA;AACV;AACD;AACH,CAAA;;;;"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { enableFeatureMiddleware } from '../../routes/utils.mjs';
|
|
2
2
|
|
|
3
|
-
const getRouteConfig = (
|
|
3
|
+
const getRouteConfig = (actions = [
|
|
4
|
+
'admin::audit-logs.read'
|
|
5
|
+
])=>({
|
|
4
6
|
middlewares: [
|
|
5
7
|
enableFeatureMiddleware('audit-logs')
|
|
6
8
|
],
|
|
@@ -9,9 +11,7 @@ const getRouteConfig = ()=>({
|
|
|
9
11
|
{
|
|
10
12
|
name: 'admin::hasPermissions',
|
|
11
13
|
config: {
|
|
12
|
-
actions
|
|
13
|
-
'admin::audit-logs.read'
|
|
14
|
-
]
|
|
14
|
+
actions
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
]
|
|
@@ -31,6 +31,15 @@ var auditLogsRoutes = {
|
|
|
31
31
|
handler: 'audit-logs.findManyUsers',
|
|
32
32
|
config: getRouteConfig()
|
|
33
33
|
},
|
|
34
|
+
{
|
|
35
|
+
method: 'GET',
|
|
36
|
+
path: '/audit-logs/export',
|
|
37
|
+
handler: 'audit-logs.export',
|
|
38
|
+
config: getRouteConfig([
|
|
39
|
+
'admin::audit-logs.read',
|
|
40
|
+
'admin::audit-logs.export'
|
|
41
|
+
])
|
|
42
|
+
},
|
|
34
43
|
{
|
|
35
44
|
method: 'GET',
|
|
36
45
|
path: '/audit-logs/:id',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-logs.mjs","sources":["../../../../../../../ee/server/src/audit-logs/routes/audit-logs.ts"],"sourcesContent":["import { enableFeatureMiddleware } from '../../routes/utils';\n\nconst getRouteConfig = () => ({\n middlewares: [enableFeatureMiddleware('audit-logs')],\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions
|
|
1
|
+
{"version":3,"file":"audit-logs.mjs","sources":["../../../../../../../ee/server/src/audit-logs/routes/audit-logs.ts"],"sourcesContent":["import { enableFeatureMiddleware } from '../../routes/utils';\n\nconst getRouteConfig = (actions: string[] = ['admin::audit-logs.read']) => ({\n middlewares: [enableFeatureMiddleware('audit-logs')],\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions,\n },\n },\n ],\n});\n\nexport default {\n type: 'admin',\n routes: [\n {\n method: 'GET',\n path: '/audit-logs',\n handler: 'audit-logs.findMany',\n config: getRouteConfig(),\n },\n {\n method: 'GET',\n path: '/audit-logs/users',\n handler: 'audit-logs.findManyUsers',\n config: getRouteConfig(),\n },\n {\n method: 'GET',\n path: '/audit-logs/export',\n handler: 'audit-logs.export',\n config: getRouteConfig(['admin::audit-logs.read', 'admin::audit-logs.export']),\n },\n {\n method: 'GET',\n path: '/audit-logs/:id',\n handler: 'audit-logs.findOne',\n config: getRouteConfig(),\n },\n ],\n};\n"],"names":["getRouteConfig","actions","middlewares","enableFeatureMiddleware","policies","name","config","type","routes","method","path","handler"],"mappings":";;AAEA,MAAMA,cAAAA,GAAiB,CAACC,OAAAA,GAAoB;AAAC,IAAA;AAAyB,CAAA,IAAM;QAC1EC,WAAAA,EAAa;YAACC,uBAAAA,CAAwB,YAAA;AAAc,SAAA;QACpDC,QAAAA,EAAU;AACR,YAAA,6BAAA;AACA,YAAA;gBACEC,IAAAA,EAAM,uBAAA;gBACNC,MAAAA,EAAQ;AACNL,oBAAAA;AACF;AACF;AACD;KACH,CAAA;AAEA,sBAAe;IACbM,IAAAA,EAAM,OAAA;IACNC,MAAAA,EAAQ;AACN,QAAA;YACEC,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,aAAA;YACNC,OAAAA,EAAS,qBAAA;YACTL,MAAAA,EAAQN,cAAAA;AACV,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,mBAAA;YACNC,OAAAA,EAAS,0BAAA;YACTL,MAAAA,EAAQN,cAAAA;AACV,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,oBAAA;YACNC,OAAAA,EAAS,mBAAA;AACTL,YAAAA,MAAAA,EAAQN,cAAAA,CAAe;AAAC,gBAAA,wBAAA;AAA0B,gBAAA;AAA2B,aAAA;AAC/E,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,iBAAA;YACNC,OAAAA,EAAS,oBAAA;YACTL,MAAAA,EAAQN,cAAAA;AACV;AACD;AACH,CAAA;;;;"}
|
|
@@ -1,18 +1,93 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var stream = require('stream');
|
|
4
|
+
var auditLogExport = require('../../../../../shared/utils/audit-log-export.js');
|
|
5
|
+
var utils = require('../utils.js');
|
|
6
|
+
var csv = require('../utils/csv.js');
|
|
7
|
+
var exportToken = require('../utils/export-token.js');
|
|
8
|
+
|
|
9
|
+
const EXPORT_CSV_COLUMNS = [
|
|
10
|
+
'id',
|
|
11
|
+
'action',
|
|
12
|
+
'date',
|
|
13
|
+
'user_id',
|
|
14
|
+
'user_email',
|
|
15
|
+
'user_display_name',
|
|
16
|
+
'origin',
|
|
17
|
+
'payload'
|
|
18
|
+
];
|
|
19
|
+
const EXPORT_USER_COLUMNS = [
|
|
20
|
+
'id',
|
|
21
|
+
'email',
|
|
22
|
+
'username',
|
|
23
|
+
'firstname',
|
|
24
|
+
'lastname'
|
|
25
|
+
];
|
|
26
|
+
const EXPORT_BATCH_SIZE = 5000;
|
|
3
27
|
const getSanitizedUser = (user)=>{
|
|
4
|
-
let displayName = user.email;
|
|
5
|
-
if (user.username) {
|
|
6
|
-
displayName = user.username;
|
|
7
|
-
} else if (user.firstname && user.lastname) {
|
|
8
|
-
displayName = `${user.firstname} ${user.lastname}`;
|
|
9
|
-
}
|
|
10
28
|
return {
|
|
11
29
|
id: user.id,
|
|
12
30
|
email: user.email,
|
|
13
|
-
displayName
|
|
31
|
+
displayName: utils.getDisplayName(user)
|
|
14
32
|
};
|
|
15
33
|
};
|
|
34
|
+
const serializeCsvPayload = (payload)=>{
|
|
35
|
+
if (payload === null || payload === undefined) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return typeof payload === 'string' ? payload : JSON.stringify(payload);
|
|
39
|
+
};
|
|
40
|
+
const parseCsvPayload = (payload)=>{
|
|
41
|
+
if (typeof payload === 'string') {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(payload);
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return typeof payload === 'object' ? payload : null;
|
|
49
|
+
};
|
|
50
|
+
const getExportSecret = (strapi)=>{
|
|
51
|
+
const secret = strapi.config.get('admin.auth.secret');
|
|
52
|
+
if (typeof secret !== 'string' || secret.length === 0) {
|
|
53
|
+
throw new Error('The audit logs export requires admin.auth.secret to be set in config/admin.js.');
|
|
54
|
+
}
|
|
55
|
+
return secret;
|
|
56
|
+
};
|
|
57
|
+
const isTrustedExportContinuation = (strapi, query)=>{
|
|
58
|
+
return query.cursor !== undefined && query.until !== undefined && exportToken.verifyExportToken(getExportSecret(strapi), query.token, query.until, query.filters);
|
|
59
|
+
};
|
|
60
|
+
const buildExportWhere = (filtersWhere, untilId, afterId)=>({
|
|
61
|
+
$and: [
|
|
62
|
+
filtersWhere ?? {},
|
|
63
|
+
{
|
|
64
|
+
id: {
|
|
65
|
+
$lte: untilId
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
...afterId > 0 ? [
|
|
69
|
+
{
|
|
70
|
+
id: {
|
|
71
|
+
$gt: afterId
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
] : []
|
|
75
|
+
]
|
|
76
|
+
});
|
|
77
|
+
const toCsvLine = (row)=>{
|
|
78
|
+
const user = row.user ? getSanitizedUser(row.user) : null;
|
|
79
|
+
const { payload } = row;
|
|
80
|
+
return csv.serializeCsvLine([
|
|
81
|
+
row.id,
|
|
82
|
+
row.action,
|
|
83
|
+
row.date instanceof Date ? row.date.toISOString() : row.date,
|
|
84
|
+
user?.id ?? null,
|
|
85
|
+
user?.email ?? null,
|
|
86
|
+
user?.displayName ?? null,
|
|
87
|
+
parseCsvPayload(payload)?.origin ?? null,
|
|
88
|
+
serializeCsvPayload(payload)
|
|
89
|
+
]);
|
|
90
|
+
};
|
|
16
91
|
/**
|
|
17
92
|
* @description
|
|
18
93
|
* Manages audit logs interaction with the database. Accessible via strapi.get('audit-logs')
|
|
@@ -72,6 +147,124 @@ const getSanitizedUser = (user)=>{
|
|
|
72
147
|
pagination
|
|
73
148
|
};
|
|
74
149
|
},
|
|
150
|
+
async isExportTooLarge (query) {
|
|
151
|
+
const maxRows = strapi.config.get('admin.auditLogs.exportMaxRows', auditLogExport.AUDIT_LOGS_EXPORT_DEFAULT_MAX_ROWS);
|
|
152
|
+
const { where } = strapi.get('query-params').transform('admin::audit-log', {
|
|
153
|
+
filters: query.filters ?? undefined
|
|
154
|
+
});
|
|
155
|
+
const beyondCap = await strapi.db.query('admin::audit-log').findMany({
|
|
156
|
+
select: [
|
|
157
|
+
'id'
|
|
158
|
+
],
|
|
159
|
+
where: where ?? {},
|
|
160
|
+
orderBy: {
|
|
161
|
+
id: 'asc'
|
|
162
|
+
},
|
|
163
|
+
offset: maxRows,
|
|
164
|
+
limit: 1
|
|
165
|
+
});
|
|
166
|
+
return beyondCap.length > 0;
|
|
167
|
+
},
|
|
168
|
+
isTrustedContinuation (query) {
|
|
169
|
+
return isTrustedExportContinuation(strapi, query);
|
|
170
|
+
},
|
|
171
|
+
async createExportStream (query) {
|
|
172
|
+
getExportSecret(strapi);
|
|
173
|
+
const { cursor, pageSize, until, token, page: _page, sort: _sort, ...filtersQuery } = query;
|
|
174
|
+
const configuredPartSize = strapi.config.get('admin.auditLogs.exportPartRows', auditLogExport.AUDIT_LOGS_EXPORT_PART_ROWS);
|
|
175
|
+
const partSize = Math.max(1, Math.min(pageSize ?? configuredPartSize, auditLogExport.AUDIT_LOGS_EXPORT_PART_MAX_ROWS));
|
|
176
|
+
const { where: filtersWhere } = strapi.get('query-params').transform('admin::audit-log', filtersQuery);
|
|
177
|
+
const isNewExport = !isTrustedExportContinuation(strapi, query);
|
|
178
|
+
if (isNewExport) {
|
|
179
|
+
await strapi.eventHub.emit(auditLogExport.AUDIT_LOG_EXPORT_EVENT, {
|
|
180
|
+
filters: filtersQuery.filters ?? null
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
let frozenUntil = isNewExport ? undefined : until;
|
|
184
|
+
if (frozenUntil === undefined) {
|
|
185
|
+
const [lastRow] = await strapi.db.query('admin::audit-log').findMany({
|
|
186
|
+
select: [
|
|
187
|
+
'id'
|
|
188
|
+
],
|
|
189
|
+
orderBy: {
|
|
190
|
+
id: 'desc'
|
|
191
|
+
},
|
|
192
|
+
limit: 1
|
|
193
|
+
});
|
|
194
|
+
frozenUntil = lastRow?.id ?? 0;
|
|
195
|
+
}
|
|
196
|
+
const untilId = frozenUntil ?? 0;
|
|
197
|
+
const getWhere = (afterId)=>buildExportWhere(filtersWhere, untilId, afterId);
|
|
198
|
+
const boundary = await strapi.db.query('admin::audit-log').findMany({
|
|
199
|
+
select: [
|
|
200
|
+
'id'
|
|
201
|
+
],
|
|
202
|
+
where: getWhere(cursor ?? 0),
|
|
203
|
+
orderBy: {
|
|
204
|
+
id: 'asc'
|
|
205
|
+
},
|
|
206
|
+
offset: partSize - 1,
|
|
207
|
+
limit: 2
|
|
208
|
+
});
|
|
209
|
+
const nextCursor = boundary.length > 1 ? boundary[0].id : null;
|
|
210
|
+
const partUpperBound = nextCursor ?? untilId;
|
|
211
|
+
async function* csvChunks() {
|
|
212
|
+
if (isNewExport) {
|
|
213
|
+
yield csv.CSV_BOM + csv.serializeCsvLine(EXPORT_CSV_COLUMNS);
|
|
214
|
+
}
|
|
215
|
+
let lastId = cursor ?? 0;
|
|
216
|
+
let remaining = partSize;
|
|
217
|
+
while(remaining > 0){
|
|
218
|
+
const batchLimit = Math.min(EXPORT_BATCH_SIZE, remaining);
|
|
219
|
+
const rows = await strapi.db.query('admin::audit-log').findMany({
|
|
220
|
+
select: [
|
|
221
|
+
'id',
|
|
222
|
+
'action',
|
|
223
|
+
'date',
|
|
224
|
+
'payload'
|
|
225
|
+
],
|
|
226
|
+
populate: {
|
|
227
|
+
user: {
|
|
228
|
+
select: EXPORT_USER_COLUMNS
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
where: buildExportWhere(filtersWhere, partUpperBound, lastId),
|
|
232
|
+
orderBy: {
|
|
233
|
+
id: 'asc'
|
|
234
|
+
},
|
|
235
|
+
limit: batchLimit
|
|
236
|
+
});
|
|
237
|
+
if (rows.length === 0) {
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
yield rows.map(toCsvLine).join('');
|
|
241
|
+
lastId = rows[rows.length - 1].id;
|
|
242
|
+
remaining -= rows.length;
|
|
243
|
+
if (rows.length < batchLimit) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function* loggedCsvChunks() {
|
|
249
|
+
try {
|
|
250
|
+
yield* csvChunks();
|
|
251
|
+
} catch (error) {
|
|
252
|
+
strapi.log.error(`Audit logs export failed after the response started, the CSV part is truncated: ${error}`);
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
stream: stream.Readable.from(loggedCsvChunks(), {
|
|
258
|
+
objectMode: false,
|
|
259
|
+
highWaterMark: 64 * 1024
|
|
260
|
+
}),
|
|
261
|
+
nextCursor,
|
|
262
|
+
until: untilId,
|
|
263
|
+
isNewExport,
|
|
264
|
+
partSize,
|
|
265
|
+
exportToken: isNewExport ? exportToken.signExportToken(getExportSecret(strapi), untilId, filtersQuery.filters) : token
|
|
266
|
+
};
|
|
267
|
+
},
|
|
75
268
|
async findManyUsers (query) {
|
|
76
269
|
const { page = 1, pageSize = 10 } = query;
|
|
77
270
|
const userAttribute = strapi.db.metadata.get('admin::audit-log').attributes.user;
|
|
@@ -140,4 +333,5 @@ const getSanitizedUser = (user)=>{
|
|
|
140
333
|
};
|
|
141
334
|
|
|
142
335
|
exports.createAuditLogsService = createAuditLogsService;
|
|
336
|
+
exports.toCsvLine = toCsvLine;
|
|
143
337
|
//# sourceMappingURL=audit-logs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-logs.js","sources":["../../../../../../../ee/server/src/audit-logs/services/audit-logs.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\ninterface Event {\n action: string;\n date: Date;\n userId: string | number;\n payload: Record<string, unknown>;\n}\n\ninterface Log extends Omit<Event, 'userId'> {\n user: string | number;\n}\n\nconst getSanitizedUser = (user: any) => {\n let displayName = user.email;\n\n if (user.username) {\n displayName = user.username;\n } else if (user.firstname && user.lastname) {\n displayName = `${user.firstname} ${user.lastname}`;\n }\n\n return {\n id: user.id,\n email: user.email,\n displayName,\n };\n};\n\n/**\n * @description\n * Manages audit logs interaction with the database. Accessible via strapi.get('audit-logs')\n */\nconst createAuditLogsService = (strapi: Core.Strapi) => {\n return {\n async saveEvent(event: Event) {\n const { userId, ...rest } = event;\n\n const auditLog: Log = { ...rest, user: userId };\n\n // Save to database\n await strapi.db.query('admin::audit-log').create({ data: auditLog });\n\n return this;\n },\n\n async findMany(query: unknown) {\n // NOTE: We get the IDs first because sorting full rows runs MySQL/MariaDB out of sort memory\n // See: https://github.com/strapi/strapi/issues/27399\n const { results: logRows, pagination } = await strapi.db.query('admin::audit-log').findPage({\n ...strapi.get('query-params').transform('admin::audit-log', query),\n select: ['id'],\n });\n\n const ids = logRows.map((log) => log.id);\n const logs = ids.length\n ? await strapi.db.query('admin::audit-log').findMany({\n where: { id: { $in: ids } },\n populate: ['user'],\n select: ['action', 'date', 'payload'],\n })\n : [];\n const logsById = new Map(logs.map((log) => [log.id, log]));\n const results = ids.map((id) => logsById.get(id)).filter(Boolean);\n\n const sanitizedResults = results.map((result: any) => {\n const { user, ...rest } = result;\n return {\n ...rest,\n user: user ? getSanitizedUser(user) : null,\n };\n });\n\n return {\n results: sanitizedResults,\n pagination,\n };\n },\n\n async findManyUsers(query: { page?: number | string; pageSize?: number | string }) {\n const { page = 1, pageSize = 10 } = query;\n\n const userAttribute = strapi.db.metadata.get('admin::audit-log').attributes.user;\n\n if (\n userAttribute?.type !== 'relation' ||\n !('joinTable' in userAttribute) ||\n !userAttribute.joinTable ||\n !('inverseJoinColumn' in userAttribute.joinTable)\n ) {\n throw new Error('The audit log user relation is expected to use a join table');\n }\n\n const { joinTable } = userAttribute;\n\n const authorIds = await strapi.db\n .connection(joinTable.name)\n .distinct(joinTable.inverseJoinColumn.name)\n .pluck(joinTable.inverseJoinColumn.name);\n\n const { results, pagination } = await strapi.db.query('admin::user').findPage({\n select: ['id', 'email', 'username', 'firstname', 'lastname'],\n where: { id: { $in: authorIds } },\n orderBy: { id: 'asc' },\n page: Number(page),\n pageSize: Number(pageSize),\n });\n\n return {\n results: results.map(getSanitizedUser),\n pagination,\n };\n },\n\n async findOne(id: unknown) {\n const result: any = await strapi.db.query('admin::audit-log').findOne({\n where: { id },\n populate: ['user'],\n select: ['action', 'date', 'payload'],\n });\n\n if (!result) {\n return null;\n }\n\n const { user, ...rest } = result;\n return {\n ...rest,\n user: user ? getSanitizedUser(user) : null,\n };\n },\n\n deleteExpiredEvents(expirationDate: Date) {\n return strapi.db.query('admin::audit-log').deleteMany({\n where: {\n date: {\n $lt: expirationDate.toISOString(),\n },\n },\n });\n },\n };\n};\n\nexport { createAuditLogsService };\n"],"names":["getSanitizedUser","user","displayName","email","username","firstname","lastname","id","createAuditLogsService","strapi","saveEvent","event","userId","rest","auditLog","db","query","create","data","findMany","results","logRows","pagination","findPage","get","transform","select","ids","map","log","logs","length","where","$in","populate","logsById","Map","filter","Boolean","sanitizedResults","result","findManyUsers","page","pageSize","userAttribute","metadata","attributes","type","joinTable","Error","authorIds","connection","name","distinct","inverseJoinColumn","pluck","orderBy","Number","findOne","deleteExpiredEvents","expirationDate","deleteMany","date","$lt","toISOString"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"audit-logs.js","sources":["../../../../../../../ee/server/src/audit-logs/services/audit-logs.ts"],"sourcesContent":["import { Readable } from 'stream';\n\nimport type { Core } from '@strapi/types';\n\nimport {\n AUDIT_LOG_EXPORT_EVENT,\n AUDIT_LOGS_EXPORT_DEFAULT_MAX_ROWS,\n AUDIT_LOGS_EXPORT_PART_MAX_ROWS,\n AUDIT_LOGS_EXPORT_PART_ROWS,\n} from '../../../../../shared/utils/audit-log-export';\nimport { getDisplayName } from '../utils';\nimport { CSV_BOM, serializeCsvLine } from '../utils/csv';\nimport { signExportToken, verifyExportToken } from '../utils/export-token';\n\nconst EXPORT_CSV_COLUMNS = [\n 'id',\n 'action',\n 'date',\n 'user_id',\n 'user_email',\n 'user_display_name',\n 'origin',\n 'payload',\n];\nconst EXPORT_USER_COLUMNS = ['id', 'email', 'username', 'firstname', 'lastname'];\nconst EXPORT_BATCH_SIZE = 5000;\n\ninterface Event {\n action: string;\n date: Date;\n // Null for actions no person performed, such as a scheduled job\n userId: string | number | null;\n payload: Record<string, unknown>;\n}\n\ninterface Log extends Omit<Event, 'userId'> {\n user: string | number | null;\n}\n\nconst getSanitizedUser = (user: any) => {\n return {\n id: user.id,\n email: user.email,\n displayName: getDisplayName(user),\n };\n};\n\nconst serializeCsvPayload = (payload: unknown) => {\n if (payload === null || payload === undefined) {\n return null;\n }\n\n return typeof payload === 'string' ? payload : JSON.stringify(payload);\n};\n\nconst parseCsvPayload = (payload: unknown): Record<string, unknown> | null => {\n if (typeof payload === 'string') {\n try {\n return JSON.parse(payload);\n } catch {\n return null;\n }\n }\n\n return typeof payload === 'object' ? (payload as Record<string, unknown> | null) : null;\n};\n\ninterface ExportQuery {\n cursor?: number;\n pageSize?: number;\n until?: number;\n token?: string;\n [key: string]: unknown;\n}\n\nconst getExportSecret = (strapi: Core.Strapi): string => {\n const secret = strapi.config.get('admin.auth.secret');\n\n if (typeof secret !== 'string' || secret.length === 0) {\n throw new Error(\n 'The audit logs export requires admin.auth.secret to be set in config/admin.js.'\n );\n }\n\n return secret;\n};\n\nconst isTrustedExportContinuation = (strapi: Core.Strapi, query: ExportQuery) => {\n return (\n query.cursor !== undefined &&\n query.until !== undefined &&\n verifyExportToken(getExportSecret(strapi), query.token, query.until, query.filters)\n );\n};\n\nconst buildExportWhere = (filtersWhere: unknown, untilId: number, afterId: number) => ({\n $and: [\n filtersWhere ?? {},\n { id: { $lte: untilId } },\n ...(afterId > 0 ? [{ id: { $gt: afterId } }] : []),\n ],\n});\n\nconst toCsvLine = (row: any) => {\n const user = row.user ? getSanitizedUser(row.user) : null;\n const { payload } = row;\n\n return serializeCsvLine([\n row.id,\n row.action,\n row.date instanceof Date ? row.date.toISOString() : row.date,\n user?.id ?? null,\n user?.email ?? null,\n user?.displayName ?? null,\n parseCsvPayload(payload)?.origin ?? null,\n serializeCsvPayload(payload),\n ]);\n};\n\n/**\n * @description\n * Manages audit logs interaction with the database. Accessible via strapi.get('audit-logs')\n */\nconst createAuditLogsService = (strapi: Core.Strapi) => {\n return {\n async saveEvent(event: Event) {\n const { userId, ...rest } = event;\n\n const auditLog: Log = { ...rest, user: userId };\n\n // Save to database\n await strapi.db.query('admin::audit-log').create({ data: auditLog });\n\n return this;\n },\n\n async findMany(query: unknown) {\n // NOTE: We get the IDs first because sorting full rows runs MySQL/MariaDB out of sort memory\n // See: https://github.com/strapi/strapi/issues/27399\n const { results: logRows, pagination } = await strapi.db.query('admin::audit-log').findPage({\n ...strapi.get('query-params').transform('admin::audit-log', query),\n select: ['id'],\n });\n\n const ids = logRows.map((log) => log.id);\n const logs = ids.length\n ? await strapi.db.query('admin::audit-log').findMany({\n where: { id: { $in: ids } },\n populate: ['user'],\n select: ['action', 'date', 'payload'],\n })\n : [];\n const logsById = new Map(logs.map((log) => [log.id, log]));\n const results = ids.map((id) => logsById.get(id)).filter(Boolean);\n\n const sanitizedResults = results.map((result: any) => {\n const { user, ...rest } = result;\n return {\n ...rest,\n user: user ? getSanitizedUser(user) : null,\n };\n });\n\n return {\n results: sanitizedResults,\n pagination,\n };\n },\n\n async isExportTooLarge(query: { filters?: unknown }) {\n const maxRows = strapi.config.get(\n 'admin.auditLogs.exportMaxRows',\n AUDIT_LOGS_EXPORT_DEFAULT_MAX_ROWS\n );\n const { where } = strapi\n .get('query-params')\n .transform('admin::audit-log', { filters: query.filters ?? undefined });\n\n const beyondCap = await strapi.db.query('admin::audit-log').findMany({\n select: ['id'],\n where: where ?? {},\n orderBy: { id: 'asc' },\n offset: maxRows,\n limit: 1,\n });\n\n return beyondCap.length > 0;\n },\n\n isTrustedContinuation(query: ExportQuery) {\n return isTrustedExportContinuation(strapi, query);\n },\n\n async createExportStream(query: ExportQuery) {\n getExportSecret(strapi);\n\n const { cursor, pageSize, until, token, page: _page, sort: _sort, ...filtersQuery } = query;\n\n const configuredPartSize = strapi.config.get(\n 'admin.auditLogs.exportPartRows',\n AUDIT_LOGS_EXPORT_PART_ROWS\n );\n const partSize = Math.max(\n 1,\n Math.min(pageSize ?? configuredPartSize, AUDIT_LOGS_EXPORT_PART_MAX_ROWS)\n );\n\n const { where: filtersWhere } = strapi\n .get('query-params')\n .transform('admin::audit-log', filtersQuery);\n\n const isNewExport = !isTrustedExportContinuation(strapi, query);\n if (isNewExport) {\n await strapi.eventHub.emit(AUDIT_LOG_EXPORT_EVENT, {\n filters: filtersQuery.filters ?? null,\n });\n }\n\n let frozenUntil = isNewExport ? undefined : until;\n if (frozenUntil === undefined) {\n const [lastRow] = await strapi.db.query('admin::audit-log').findMany({\n select: ['id'],\n orderBy: { id: 'desc' },\n limit: 1,\n });\n\n frozenUntil = lastRow?.id ?? 0;\n }\n\n const untilId = frozenUntil ?? 0;\n const getWhere = (afterId: number) => buildExportWhere(filtersWhere, untilId, afterId);\n\n const boundary: Array<{ id: number }> = await strapi.db.query('admin::audit-log').findMany({\n select: ['id'],\n where: getWhere(cursor ?? 0),\n orderBy: { id: 'asc' },\n offset: partSize - 1,\n limit: 2,\n });\n\n const nextCursor = boundary.length > 1 ? boundary[0].id : null;\n\n const partUpperBound = nextCursor ?? untilId;\n\n async function* csvChunks() {\n if (isNewExport) {\n yield CSV_BOM + serializeCsvLine(EXPORT_CSV_COLUMNS);\n }\n\n let lastId = cursor ?? 0;\n let remaining = partSize;\n\n while (remaining > 0) {\n const batchLimit = Math.min(EXPORT_BATCH_SIZE, remaining);\n const rows: any[] = await strapi.db.query('admin::audit-log').findMany({\n select: ['id', 'action', 'date', 'payload'],\n populate: { user: { select: EXPORT_USER_COLUMNS } },\n where: buildExportWhere(filtersWhere, partUpperBound, lastId),\n orderBy: { id: 'asc' },\n limit: batchLimit,\n });\n\n if (rows.length === 0) {\n break;\n }\n\n yield rows.map(toCsvLine).join('');\n\n lastId = rows[rows.length - 1].id;\n remaining -= rows.length;\n\n if (rows.length < batchLimit) {\n break;\n }\n }\n }\n\n async function* loggedCsvChunks() {\n try {\n yield* csvChunks();\n } catch (error) {\n strapi.log.error(\n `Audit logs export failed after the response started, the CSV part is truncated: ${error}`\n );\n throw error;\n }\n }\n\n return {\n stream: Readable.from(loggedCsvChunks(), { objectMode: false, highWaterMark: 64 * 1024 }),\n nextCursor,\n until: untilId,\n isNewExport,\n partSize,\n exportToken: isNewExport\n ? signExportToken(getExportSecret(strapi), untilId, filtersQuery.filters)\n : (token as string),\n };\n },\n\n async findManyUsers(query: { page?: number | string; pageSize?: number | string }) {\n const { page = 1, pageSize = 10 } = query;\n\n const userAttribute = strapi.db.metadata.get('admin::audit-log').attributes.user;\n\n if (\n userAttribute?.type !== 'relation' ||\n !('joinTable' in userAttribute) ||\n !userAttribute.joinTable ||\n !('inverseJoinColumn' in userAttribute.joinTable)\n ) {\n throw new Error('The audit log user relation is expected to use a join table');\n }\n\n const { joinTable } = userAttribute;\n\n const authorIds = await strapi.db\n .connection(joinTable.name)\n .distinct(joinTable.inverseJoinColumn.name)\n .pluck(joinTable.inverseJoinColumn.name);\n\n const { results, pagination } = await strapi.db.query('admin::user').findPage({\n select: ['id', 'email', 'username', 'firstname', 'lastname'],\n where: { id: { $in: authorIds } },\n orderBy: { id: 'asc' },\n page: Number(page),\n pageSize: Number(pageSize),\n });\n\n return {\n results: results.map(getSanitizedUser),\n pagination,\n };\n },\n\n async findOne(id: unknown) {\n const result: any = await strapi.db.query('admin::audit-log').findOne({\n where: { id },\n populate: ['user'],\n select: ['action', 'date', 'payload'],\n });\n\n if (!result) {\n return null;\n }\n\n const { user, ...rest } = result;\n return {\n ...rest,\n user: user ? getSanitizedUser(user) : null,\n };\n },\n\n deleteExpiredEvents(expirationDate: Date) {\n return strapi.db.query('admin::audit-log').deleteMany({\n where: {\n date: {\n $lt: expirationDate.toISOString(),\n },\n },\n });\n },\n };\n};\n\nexport { createAuditLogsService, toCsvLine };\n"],"names":["EXPORT_CSV_COLUMNS","EXPORT_USER_COLUMNS","EXPORT_BATCH_SIZE","getSanitizedUser","user","id","email","displayName","getDisplayName","serializeCsvPayload","payload","undefined","JSON","stringify","parseCsvPayload","parse","getExportSecret","strapi","secret","config","get","length","Error","isTrustedExportContinuation","query","cursor","until","verifyExportToken","token","filters","buildExportWhere","filtersWhere","untilId","afterId","$and","$lte","$gt","toCsvLine","row","serializeCsvLine","action","date","Date","toISOString","origin","createAuditLogsService","saveEvent","event","userId","rest","auditLog","db","create","data","findMany","results","logRows","pagination","findPage","transform","select","ids","map","log","logs","where","$in","populate","logsById","Map","filter","Boolean","sanitizedResults","result","isExportTooLarge","maxRows","AUDIT_LOGS_EXPORT_DEFAULT_MAX_ROWS","beyondCap","orderBy","offset","limit","isTrustedContinuation","createExportStream","pageSize","page","_page","sort","_sort","filtersQuery","configuredPartSize","AUDIT_LOGS_EXPORT_PART_ROWS","partSize","Math","max","min","AUDIT_LOGS_EXPORT_PART_MAX_ROWS","isNewExport","eventHub","emit","AUDIT_LOG_EXPORT_EVENT","frozenUntil","lastRow","getWhere","boundary","nextCursor","partUpperBound","csvChunks","CSV_BOM","lastId","remaining","batchLimit","rows","join","loggedCsvChunks","error","stream","Readable","from","objectMode","highWaterMark","exportToken","signExportToken","findManyUsers","userAttribute","metadata","attributes","type","joinTable","authorIds","connection","name","distinct","inverseJoinColumn","pluck","Number","findOne","deleteExpiredEvents","expirationDate","deleteMany","$lt"],"mappings":"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"}
|
|
@@ -1,16 +1,91 @@
|
|
|
1
|
+
import { Readable } from 'stream';
|
|
2
|
+
import { AUDIT_LOGS_EXPORT_PART_ROWS, AUDIT_LOG_EXPORT_EVENT, AUDIT_LOGS_EXPORT_DEFAULT_MAX_ROWS, AUDIT_LOGS_EXPORT_PART_MAX_ROWS } from '../../../../../shared/utils/audit-log-export.mjs';
|
|
3
|
+
import { getDisplayName } from '../utils.mjs';
|
|
4
|
+
import { CSV_BOM, serializeCsvLine } from '../utils/csv.mjs';
|
|
5
|
+
import { signExportToken, verifyExportToken } from '../utils/export-token.mjs';
|
|
6
|
+
|
|
7
|
+
const EXPORT_CSV_COLUMNS = [
|
|
8
|
+
'id',
|
|
9
|
+
'action',
|
|
10
|
+
'date',
|
|
11
|
+
'user_id',
|
|
12
|
+
'user_email',
|
|
13
|
+
'user_display_name',
|
|
14
|
+
'origin',
|
|
15
|
+
'payload'
|
|
16
|
+
];
|
|
17
|
+
const EXPORT_USER_COLUMNS = [
|
|
18
|
+
'id',
|
|
19
|
+
'email',
|
|
20
|
+
'username',
|
|
21
|
+
'firstname',
|
|
22
|
+
'lastname'
|
|
23
|
+
];
|
|
24
|
+
const EXPORT_BATCH_SIZE = 5000;
|
|
1
25
|
const getSanitizedUser = (user)=>{
|
|
2
|
-
let displayName = user.email;
|
|
3
|
-
if (user.username) {
|
|
4
|
-
displayName = user.username;
|
|
5
|
-
} else if (user.firstname && user.lastname) {
|
|
6
|
-
displayName = `${user.firstname} ${user.lastname}`;
|
|
7
|
-
}
|
|
8
26
|
return {
|
|
9
27
|
id: user.id,
|
|
10
28
|
email: user.email,
|
|
11
|
-
displayName
|
|
29
|
+
displayName: getDisplayName(user)
|
|
12
30
|
};
|
|
13
31
|
};
|
|
32
|
+
const serializeCsvPayload = (payload)=>{
|
|
33
|
+
if (payload === null || payload === undefined) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return typeof payload === 'string' ? payload : JSON.stringify(payload);
|
|
37
|
+
};
|
|
38
|
+
const parseCsvPayload = (payload)=>{
|
|
39
|
+
if (typeof payload === 'string') {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(payload);
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return typeof payload === 'object' ? payload : null;
|
|
47
|
+
};
|
|
48
|
+
const getExportSecret = (strapi)=>{
|
|
49
|
+
const secret = strapi.config.get('admin.auth.secret');
|
|
50
|
+
if (typeof secret !== 'string' || secret.length === 0) {
|
|
51
|
+
throw new Error('The audit logs export requires admin.auth.secret to be set in config/admin.js.');
|
|
52
|
+
}
|
|
53
|
+
return secret;
|
|
54
|
+
};
|
|
55
|
+
const isTrustedExportContinuation = (strapi, query)=>{
|
|
56
|
+
return query.cursor !== undefined && query.until !== undefined && verifyExportToken(getExportSecret(strapi), query.token, query.until, query.filters);
|
|
57
|
+
};
|
|
58
|
+
const buildExportWhere = (filtersWhere, untilId, afterId)=>({
|
|
59
|
+
$and: [
|
|
60
|
+
filtersWhere ?? {},
|
|
61
|
+
{
|
|
62
|
+
id: {
|
|
63
|
+
$lte: untilId
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
...afterId > 0 ? [
|
|
67
|
+
{
|
|
68
|
+
id: {
|
|
69
|
+
$gt: afterId
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
] : []
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
const toCsvLine = (row)=>{
|
|
76
|
+
const user = row.user ? getSanitizedUser(row.user) : null;
|
|
77
|
+
const { payload } = row;
|
|
78
|
+
return serializeCsvLine([
|
|
79
|
+
row.id,
|
|
80
|
+
row.action,
|
|
81
|
+
row.date instanceof Date ? row.date.toISOString() : row.date,
|
|
82
|
+
user?.id ?? null,
|
|
83
|
+
user?.email ?? null,
|
|
84
|
+
user?.displayName ?? null,
|
|
85
|
+
parseCsvPayload(payload)?.origin ?? null,
|
|
86
|
+
serializeCsvPayload(payload)
|
|
87
|
+
]);
|
|
88
|
+
};
|
|
14
89
|
/**
|
|
15
90
|
* @description
|
|
16
91
|
* Manages audit logs interaction with the database. Accessible via strapi.get('audit-logs')
|
|
@@ -70,6 +145,124 @@ const getSanitizedUser = (user)=>{
|
|
|
70
145
|
pagination
|
|
71
146
|
};
|
|
72
147
|
},
|
|
148
|
+
async isExportTooLarge (query) {
|
|
149
|
+
const maxRows = strapi.config.get('admin.auditLogs.exportMaxRows', AUDIT_LOGS_EXPORT_DEFAULT_MAX_ROWS);
|
|
150
|
+
const { where } = strapi.get('query-params').transform('admin::audit-log', {
|
|
151
|
+
filters: query.filters ?? undefined
|
|
152
|
+
});
|
|
153
|
+
const beyondCap = await strapi.db.query('admin::audit-log').findMany({
|
|
154
|
+
select: [
|
|
155
|
+
'id'
|
|
156
|
+
],
|
|
157
|
+
where: where ?? {},
|
|
158
|
+
orderBy: {
|
|
159
|
+
id: 'asc'
|
|
160
|
+
},
|
|
161
|
+
offset: maxRows,
|
|
162
|
+
limit: 1
|
|
163
|
+
});
|
|
164
|
+
return beyondCap.length > 0;
|
|
165
|
+
},
|
|
166
|
+
isTrustedContinuation (query) {
|
|
167
|
+
return isTrustedExportContinuation(strapi, query);
|
|
168
|
+
},
|
|
169
|
+
async createExportStream (query) {
|
|
170
|
+
getExportSecret(strapi);
|
|
171
|
+
const { cursor, pageSize, until, token, page: _page, sort: _sort, ...filtersQuery } = query;
|
|
172
|
+
const configuredPartSize = strapi.config.get('admin.auditLogs.exportPartRows', AUDIT_LOGS_EXPORT_PART_ROWS);
|
|
173
|
+
const partSize = Math.max(1, Math.min(pageSize ?? configuredPartSize, AUDIT_LOGS_EXPORT_PART_MAX_ROWS));
|
|
174
|
+
const { where: filtersWhere } = strapi.get('query-params').transform('admin::audit-log', filtersQuery);
|
|
175
|
+
const isNewExport = !isTrustedExportContinuation(strapi, query);
|
|
176
|
+
if (isNewExport) {
|
|
177
|
+
await strapi.eventHub.emit(AUDIT_LOG_EXPORT_EVENT, {
|
|
178
|
+
filters: filtersQuery.filters ?? null
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
let frozenUntil = isNewExport ? undefined : until;
|
|
182
|
+
if (frozenUntil === undefined) {
|
|
183
|
+
const [lastRow] = await strapi.db.query('admin::audit-log').findMany({
|
|
184
|
+
select: [
|
|
185
|
+
'id'
|
|
186
|
+
],
|
|
187
|
+
orderBy: {
|
|
188
|
+
id: 'desc'
|
|
189
|
+
},
|
|
190
|
+
limit: 1
|
|
191
|
+
});
|
|
192
|
+
frozenUntil = lastRow?.id ?? 0;
|
|
193
|
+
}
|
|
194
|
+
const untilId = frozenUntil ?? 0;
|
|
195
|
+
const getWhere = (afterId)=>buildExportWhere(filtersWhere, untilId, afterId);
|
|
196
|
+
const boundary = await strapi.db.query('admin::audit-log').findMany({
|
|
197
|
+
select: [
|
|
198
|
+
'id'
|
|
199
|
+
],
|
|
200
|
+
where: getWhere(cursor ?? 0),
|
|
201
|
+
orderBy: {
|
|
202
|
+
id: 'asc'
|
|
203
|
+
},
|
|
204
|
+
offset: partSize - 1,
|
|
205
|
+
limit: 2
|
|
206
|
+
});
|
|
207
|
+
const nextCursor = boundary.length > 1 ? boundary[0].id : null;
|
|
208
|
+
const partUpperBound = nextCursor ?? untilId;
|
|
209
|
+
async function* csvChunks() {
|
|
210
|
+
if (isNewExport) {
|
|
211
|
+
yield CSV_BOM + serializeCsvLine(EXPORT_CSV_COLUMNS);
|
|
212
|
+
}
|
|
213
|
+
let lastId = cursor ?? 0;
|
|
214
|
+
let remaining = partSize;
|
|
215
|
+
while(remaining > 0){
|
|
216
|
+
const batchLimit = Math.min(EXPORT_BATCH_SIZE, remaining);
|
|
217
|
+
const rows = await strapi.db.query('admin::audit-log').findMany({
|
|
218
|
+
select: [
|
|
219
|
+
'id',
|
|
220
|
+
'action',
|
|
221
|
+
'date',
|
|
222
|
+
'payload'
|
|
223
|
+
],
|
|
224
|
+
populate: {
|
|
225
|
+
user: {
|
|
226
|
+
select: EXPORT_USER_COLUMNS
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
where: buildExportWhere(filtersWhere, partUpperBound, lastId),
|
|
230
|
+
orderBy: {
|
|
231
|
+
id: 'asc'
|
|
232
|
+
},
|
|
233
|
+
limit: batchLimit
|
|
234
|
+
});
|
|
235
|
+
if (rows.length === 0) {
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
yield rows.map(toCsvLine).join('');
|
|
239
|
+
lastId = rows[rows.length - 1].id;
|
|
240
|
+
remaining -= rows.length;
|
|
241
|
+
if (rows.length < batchLimit) {
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function* loggedCsvChunks() {
|
|
247
|
+
try {
|
|
248
|
+
yield* csvChunks();
|
|
249
|
+
} catch (error) {
|
|
250
|
+
strapi.log.error(`Audit logs export failed after the response started, the CSV part is truncated: ${error}`);
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
stream: Readable.from(loggedCsvChunks(), {
|
|
256
|
+
objectMode: false,
|
|
257
|
+
highWaterMark: 64 * 1024
|
|
258
|
+
}),
|
|
259
|
+
nextCursor,
|
|
260
|
+
until: untilId,
|
|
261
|
+
isNewExport,
|
|
262
|
+
partSize,
|
|
263
|
+
exportToken: isNewExport ? signExportToken(getExportSecret(strapi), untilId, filtersQuery.filters) : token
|
|
264
|
+
};
|
|
265
|
+
},
|
|
73
266
|
async findManyUsers (query) {
|
|
74
267
|
const { page = 1, pageSize = 10 } = query;
|
|
75
268
|
const userAttribute = strapi.db.metadata.get('admin::audit-log').attributes.user;
|
|
@@ -137,5 +330,5 @@ const getSanitizedUser = (user)=>{
|
|
|
137
330
|
};
|
|
138
331
|
};
|
|
139
332
|
|
|
140
|
-
export { createAuditLogsService };
|
|
333
|
+
export { createAuditLogsService, toCsvLine };
|
|
141
334
|
//# sourceMappingURL=audit-logs.mjs.map
|