@strapi/admin 5.52.3 → 5.54.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.
Files changed (196) hide show
  1. package/dist/admin/admin/src/App.js +1 -1
  2. package/dist/admin/admin/src/App.js.map +1 -1
  3. package/dist/admin/admin/src/App.mjs +1 -1
  4. package/dist/admin/admin/src/App.mjs.map +1 -1
  5. package/dist/admin/admin/src/components/DismissibleBanner.js +60 -0
  6. package/dist/admin/admin/src/components/DismissibleBanner.js.map +1 -0
  7. package/dist/admin/admin/src/components/DismissibleBanner.mjs +58 -0
  8. package/dist/admin/admin/src/components/DismissibleBanner.mjs.map +1 -0
  9. package/dist/admin/admin/src/components/Form.js +11 -2
  10. package/dist/admin/admin/src/components/Form.js.map +1 -1
  11. package/dist/admin/admin/src/components/Form.mjs +11 -2
  12. package/dist/admin/admin/src/components/Form.mjs.map +1 -1
  13. package/dist/admin/admin/src/components/FormInputs/DateTime.js +1 -1
  14. package/dist/admin/admin/src/components/FormInputs/DateTime.js.map +1 -1
  15. package/dist/admin/admin/src/components/FormInputs/DateTime.mjs +1 -1
  16. package/dist/admin/admin/src/components/FormInputs/DateTime.mjs.map +1 -1
  17. package/dist/admin/admin/src/components/FormInputs/Time.js +1 -1
  18. package/dist/admin/admin/src/components/FormInputs/Time.js.map +1 -1
  19. package/dist/admin/admin/src/components/FormInputs/Time.mjs +1 -1
  20. package/dist/admin/admin/src/components/FormInputs/Time.mjs.map +1 -1
  21. package/dist/admin/admin/src/components/MainNav/NavUser.js +1 -2
  22. package/dist/admin/admin/src/components/MainNav/NavUser.js.map +1 -1
  23. package/dist/admin/admin/src/components/MainNav/NavUser.mjs +1 -2
  24. package/dist/admin/admin/src/components/MainNav/NavUser.mjs.map +1 -1
  25. package/dist/admin/admin/src/components/MediaLibraryBanner.js +85 -0
  26. package/dist/admin/admin/src/components/MediaLibraryBanner.js.map +1 -0
  27. package/dist/admin/admin/src/components/MediaLibraryBanner.mjs +83 -0
  28. package/dist/admin/admin/src/components/MediaLibraryBanner.mjs.map +1 -0
  29. package/dist/admin/admin/src/components/Providers.js +6 -6
  30. package/dist/admin/admin/src/components/Providers.js.map +1 -1
  31. package/dist/admin/admin/src/components/Providers.mjs +6 -6
  32. package/dist/admin/admin/src/components/Providers.mjs.map +1 -1
  33. package/dist/admin/admin/src/components/UpsellBanner.js +56 -86
  34. package/dist/admin/admin/src/components/UpsellBanner.js.map +1 -1
  35. package/dist/admin/admin/src/components/UpsellBanner.mjs +58 -88
  36. package/dist/admin/admin/src/components/UpsellBanner.mjs.map +1 -1
  37. package/dist/admin/admin/src/core/store/configure.js +14 -2
  38. package/dist/admin/admin/src/core/store/configure.js.map +1 -1
  39. package/dist/admin/admin/src/core/store/configure.mjs +14 -2
  40. package/dist/admin/admin/src/core/store/configure.mjs.map +1 -1
  41. package/dist/admin/admin/src/core/store/hooks.mjs +1 -1
  42. package/dist/admin/admin/src/features/Auth.js +162 -46
  43. package/dist/admin/admin/src/features/Auth.js.map +1 -1
  44. package/dist/admin/admin/src/features/Auth.mjs +164 -48
  45. package/dist/admin/admin/src/features/Auth.mjs.map +1 -1
  46. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.js +19 -0
  47. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.js.map +1 -0
  48. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.mjs +17 -0
  49. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.mjs.map +1 -0
  50. package/dist/admin/admin/src/hooks/{useIdleSessionLogout.js → useSessionKeepalive.js} +34 -27
  51. package/dist/admin/admin/src/hooks/useSessionKeepalive.js.map +1 -0
  52. package/dist/admin/admin/src/hooks/{useIdleSessionLogout.mjs → useSessionKeepalive.mjs} +34 -27
  53. package/dist/admin/admin/src/hooks/useSessionKeepalive.mjs.map +1 -0
  54. package/dist/admin/admin/src/layouts/AuthenticatedLayout.js +2 -0
  55. package/dist/admin/admin/src/layouts/AuthenticatedLayout.js.map +1 -1
  56. package/dist/admin/admin/src/layouts/AuthenticatedLayout.mjs +2 -0
  57. package/dist/admin/admin/src/layouts/AuthenticatedLayout.mjs.map +1 -1
  58. package/dist/admin/admin/src/pages/SessionsPage.js +2 -5
  59. package/dist/admin/admin/src/pages/SessionsPage.js.map +1 -1
  60. package/dist/admin/admin/src/pages/SessionsPage.mjs +2 -5
  61. package/dist/admin/admin/src/pages/SessionsPage.mjs.map +1 -1
  62. package/dist/admin/admin/src/render.js +5 -0
  63. package/dist/admin/admin/src/render.js.map +1 -1
  64. package/dist/admin/admin/src/render.mjs +5 -0
  65. package/dist/admin/admin/src/render.mjs.map +1 -1
  66. package/dist/admin/admin/src/translations/en.json.js +30 -0
  67. package/dist/admin/admin/src/translations/en.json.js.map +1 -1
  68. package/dist/admin/admin/src/translations/en.json.mjs +30 -0
  69. package/dist/admin/admin/src/translations/en.json.mjs.map +1 -1
  70. package/dist/admin/admin/src/utils/baseQuery.js +3 -13
  71. package/dist/admin/admin/src/utils/baseQuery.js.map +1 -1
  72. package/dist/admin/admin/src/utils/baseQuery.mjs +3 -13
  73. package/dist/admin/admin/src/utils/baseQuery.mjs.map +1 -1
  74. package/dist/admin/admin/src/utils/getFetchClient.js +152 -48
  75. package/dist/admin/admin/src/utils/getFetchClient.js.map +1 -1
  76. package/dist/admin/admin/src/utils/getFetchClient.mjs +151 -50
  77. package/dist/admin/admin/src/utils/getFetchClient.mjs.map +1 -1
  78. package/dist/admin/admin/src/utils/unsavedChangesRegistry.js +25 -0
  79. package/dist/admin/admin/src/utils/unsavedChangesRegistry.js.map +1 -0
  80. package/dist/admin/admin/src/utils/unsavedChangesRegistry.mjs +22 -0
  81. package/dist/admin/admin/src/utils/unsavedChangesRegistry.mjs.map +1 -0
  82. package/dist/admin/admin/tests/utils.js +14 -14
  83. package/dist/admin/admin/tests/utils.js.map +1 -1
  84. package/dist/admin/admin/tests/utils.mjs +14 -14
  85. package/dist/admin/admin/tests/utils.mjs.map +1 -1
  86. package/dist/admin/ee/admin/src/constants.js +6 -0
  87. package/dist/admin/ee/admin/src/constants.js.map +1 -1
  88. package/dist/admin/ee/admin/src/constants.mjs +6 -0
  89. package/dist/admin/ee/admin/src/constants.mjs.map +1 -1
  90. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js +31 -3
  91. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js.map +1 -1
  92. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs +33 -5
  93. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs.map +1 -1
  94. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js +177 -0
  95. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js.map +1 -0
  96. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs +175 -0
  97. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs.map +1 -0
  98. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js +173 -0
  99. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js.map +1 -0
  100. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs +151 -0
  101. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs.map +1 -0
  102. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js +14 -1
  103. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js.map +1 -1
  104. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs +14 -1
  105. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs.map +1 -1
  106. package/dist/admin/index.js +3 -0
  107. package/dist/admin/index.js.map +1 -1
  108. package/dist/admin/index.mjs +1 -1
  109. package/dist/admin/shared/utils/audit-log-export.js +16 -0
  110. package/dist/admin/shared/utils/audit-log-export.js.map +1 -0
  111. package/dist/admin/shared/utils/audit-log-export.mjs +9 -0
  112. package/dist/admin/shared/utils/audit-log-export.mjs.map +1 -0
  113. package/dist/admin/src/components/DismissibleBanner.d.ts +8 -0
  114. package/dist/admin/src/components/MediaLibraryBanner.d.ts +2 -0
  115. package/dist/admin/src/hooks/useRegisterUnsavedChanges.d.ts +6 -0
  116. package/dist/admin/src/hooks/useSessionKeepalive.d.ts +88 -0
  117. package/dist/admin/src/utils/getFetchClient.d.ts +22 -7
  118. package/dist/admin/src/utils/unsavedChangesRegistry.d.ts +11 -0
  119. package/dist/admin/tests/store.d.ts +4 -0
  120. package/dist/admin/tests/utils.d.ts +4 -0
  121. package/dist/ee/admin/src/constants.d.ts +4 -0
  122. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.d.ts +10 -0
  123. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.d.ts +18 -0
  124. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.d.ts +13 -0
  125. package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts +1 -0
  126. package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts.map +1 -1
  127. package/dist/ee/server/src/audit-logs/routes/audit-logs.d.ts.map +1 -1
  128. package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts +24 -3
  129. package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts.map +1 -1
  130. package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts +8 -2
  131. package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts.map +1 -1
  132. package/dist/ee/server/src/audit-logs/utils/csv.d.ts +5 -0
  133. package/dist/ee/server/src/audit-logs/utils/csv.d.ts.map +1 -0
  134. package/dist/ee/server/src/audit-logs/utils/export-token.d.ts +5 -0
  135. package/dist/ee/server/src/audit-logs/utils/export-token.d.ts.map +1 -0
  136. package/dist/ee/server/src/audit-logs/utils.d.ts +10 -0
  137. package/dist/ee/server/src/audit-logs/utils.d.ts.map +1 -0
  138. package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts +2 -0
  139. package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts.map +1 -1
  140. package/dist/ee/server/src/config/admin-actions.d.ts.map +1 -1
  141. package/dist/ee/server/src/index.d.ts +1 -0
  142. package/dist/ee/server/src/index.d.ts.map +1 -1
  143. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js +50 -0
  144. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js.map +1 -1
  145. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs +51 -1
  146. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs.map +1 -1
  147. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js +13 -4
  148. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js.map +1 -1
  149. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs +13 -4
  150. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs.map +1 -1
  151. package/dist/server/ee/server/src/audit-logs/services/audit-logs.js +201 -7
  152. package/dist/server/ee/server/src/audit-logs/services/audit-logs.js.map +1 -1
  153. package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs +201 -8
  154. package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs.map +1 -1
  155. package/dist/server/ee/server/src/audit-logs/services/lifecycles.js +119 -34
  156. package/dist/server/ee/server/src/audit-logs/services/lifecycles.js.map +1 -1
  157. package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs +119 -34
  158. package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs.map +1 -1
  159. package/dist/server/ee/server/src/audit-logs/utils/csv.js +33 -0
  160. package/dist/server/ee/server/src/audit-logs/utils/csv.js.map +1 -0
  161. package/dist/server/ee/server/src/audit-logs/utils/csv.mjs +29 -0
  162. package/dist/server/ee/server/src/audit-logs/utils/csv.mjs.map +1 -0
  163. package/dist/server/ee/server/src/audit-logs/utils/export-token.js +33 -0
  164. package/dist/server/ee/server/src/audit-logs/utils/export-token.js.map +1 -0
  165. package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs +29 -0
  166. package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs.map +1 -0
  167. package/dist/server/ee/server/src/audit-logs/utils.js +14 -0
  168. package/dist/server/ee/server/src/audit-logs/utils.js.map +1 -0
  169. package/dist/server/ee/server/src/audit-logs/utils.mjs +12 -0
  170. package/dist/server/ee/server/src/audit-logs/utils.mjs.map +1 -0
  171. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js +11 -0
  172. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js.map +1 -1
  173. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs +11 -1
  174. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs.map +1 -1
  175. package/dist/server/ee/server/src/config/admin-actions.js +8 -0
  176. package/dist/server/ee/server/src/config/admin-actions.js.map +1 -1
  177. package/dist/server/ee/server/src/config/admin-actions.mjs +8 -0
  178. package/dist/server/ee/server/src/config/admin-actions.mjs.map +1 -1
  179. package/dist/server/ee/server/src/index.js +10 -0
  180. package/dist/server/ee/server/src/index.js.map +1 -1
  181. package/dist/server/ee/server/src/index.mjs +10 -0
  182. package/dist/server/ee/server/src/index.mjs.map +1 -1
  183. package/dist/server/shared/utils/audit-log-export.js +22 -0
  184. package/dist/server/shared/utils/audit-log-export.js.map +1 -0
  185. package/dist/server/shared/utils/audit-log-export.mjs +12 -0
  186. package/dist/server/shared/utils/audit-log-export.mjs.map +1 -0
  187. package/dist/shared/contracts/audit-logs.d.ts +15 -1
  188. package/dist/shared/contracts/audit-logs.d.ts.map +1 -1
  189. package/dist/shared/contracts/shared.d.ts +1 -0
  190. package/dist/shared/contracts/shared.d.ts.map +1 -1
  191. package/dist/shared/utils/audit-log-export.d.ts +9 -0
  192. package/dist/shared/utils/audit-log-export.d.ts.map +1 -0
  193. package/package.json +7 -7
  194. package/dist/admin/admin/src/hooks/useIdleSessionLogout.js.map +0 -1
  195. package/dist/admin/admin/src/hooks/useIdleSessionLogout.mjs.map +0 -1
  196. package/dist/admin/src/hooks/useIdleSessionLogout.d.ts +0 -87
@@ -2,7 +2,9 @@
2
2
 
3
3
  var utils = require('../../routes/utils.js');
4
4
 
5
- const getRouteConfig = ()=>({
5
+ const getRouteConfig = (actions = [
6
+ 'admin::audit-logs.read'
7
+ ])=>({
6
8
  middlewares: [
7
9
  utils.enableFeatureMiddleware('audit-logs')
8
10
  ],
@@ -11,9 +13,7 @@ const getRouteConfig = ()=>({
11
13
  {
12
14
  name: 'admin::hasPermissions',
13
15
  config: {
14
- actions: [
15
- 'admin::audit-logs.read'
16
- ]
16
+ actions
17
17
  }
18
18
  }
19
19
  ]
@@ -33,6 +33,15 @@ var auditLogsRoutes = {
33
33
  handler: 'audit-logs.findManyUsers',
34
34
  config: getRouteConfig()
35
35
  },
36
+ {
37
+ method: 'GET',
38
+ path: '/audit-logs/export',
39
+ handler: 'audit-logs.export',
40
+ config: getRouteConfig([
41
+ 'admin::audit-logs.read',
42
+ 'admin::audit-logs.export'
43
+ ])
44
+ },
36
45
  {
37
46
  method: 'GET',
38
47
  path: '/audit-logs/:id',
@@ -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: ['admin::audit-logs.read'],\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/:id',\n handler: 'audit-logs.findOne',\n config: getRouteConfig(),\n },\n ],\n};\n"],"names":["getRouteConfig","middlewares","enableFeatureMiddleware","policies","name","config","actions","type","routes","method","path","handler"],"mappings":";;;;AAEA,MAAMA,cAAAA,GAAiB,KAAO;QAC5BC,WAAAA,EAAa;YAACC,6BAAAA,CAAwB,YAAA;AAAc,SAAA;QACpDC,QAAAA,EAAU;AACR,YAAA,6BAAA;AACA,YAAA;gBACEC,IAAAA,EAAM,uBAAA;gBACNC,MAAAA,EAAQ;oBACNC,OAAAA,EAAS;AAAC,wBAAA;AAAyB;AACrC;AACF;AACD;KACH,CAAA;AAEA,sBAAe;IACbC,IAAAA,EAAM,OAAA;IACNC,MAAAA,EAAQ;AACN,QAAA;YACEC,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,aAAA;YACNC,OAAAA,EAAS,qBAAA;YACTN,MAAAA,EAAQL,cAAAA;AACV,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,mBAAA;YACNC,OAAAA,EAAS,0BAAA;YACTN,MAAAA,EAAQL,cAAAA;AACV,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,iBAAA;YACNC,OAAAA,EAAS,oBAAA;YACTN,MAAAA,EAAQL,cAAAA;AACV;AACD;AACH,CAAA;;;;"}
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: ['admin::audit-logs.read'],\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/:id',\n handler: 'audit-logs.findOne',\n config: getRouteConfig(),\n },\n ],\n};\n"],"names":["getRouteConfig","middlewares","enableFeatureMiddleware","policies","name","config","actions","type","routes","method","path","handler"],"mappings":";;AAEA,MAAMA,cAAAA,GAAiB,KAAO;QAC5BC,WAAAA,EAAa;YAACC,uBAAAA,CAAwB,YAAA;AAAc,SAAA;QACpDC,QAAAA,EAAU;AACR,YAAA,6BAAA;AACA,YAAA;gBACEC,IAAAA,EAAM,uBAAA;gBACNC,MAAAA,EAAQ;oBACNC,OAAAA,EAAS;AAAC,wBAAA;AAAyB;AACrC;AACF;AACD;KACH,CAAA;AAEA,sBAAe;IACbC,IAAAA,EAAM,OAAA;IACNC,MAAAA,EAAQ;AACN,QAAA;YACEC,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,aAAA;YACNC,OAAAA,EAAS,qBAAA;YACTN,MAAAA,EAAQL,cAAAA;AACV,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,mBAAA;YACNC,OAAAA,EAAS,0BAAA;YACTN,MAAAA,EAAQL,cAAAA;AACV,SAAA;AACA,QAAA;YACES,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,iBAAA;YACNC,OAAAA,EAAS,oBAAA;YACTN,MAAAA,EAAQL,cAAAA;AACV;AACD;AACH,CAAA;;;;"}
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