@strapi/admin 5.52.2 → 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.
Files changed (132) hide show
  1. package/dist/admin/admin/src/components/Filters.js +9 -9
  2. package/dist/admin/admin/src/components/Filters.js.map +1 -1
  3. package/dist/admin/admin/src/components/Filters.mjs +10 -10
  4. package/dist/admin/admin/src/components/Filters.mjs.map +1 -1
  5. package/dist/admin/admin/src/components/Pagination.js +2 -2
  6. package/dist/admin/admin/src/components/Pagination.js.map +1 -1
  7. package/dist/admin/admin/src/components/Pagination.mjs +3 -3
  8. package/dist/admin/admin/src/components/Pagination.mjs.map +1 -1
  9. package/dist/admin/admin/src/components/SearchInput.js +7 -8
  10. package/dist/admin/admin/src/components/SearchInput.js.map +1 -1
  11. package/dist/admin/admin/src/components/SearchInput.mjs +8 -9
  12. package/dist/admin/admin/src/components/SearchInput.mjs.map +1 -1
  13. package/dist/admin/admin/src/components/Table.js +2 -2
  14. package/dist/admin/admin/src/components/Table.js.map +1 -1
  15. package/dist/admin/admin/src/components/Table.mjs +3 -3
  16. package/dist/admin/admin/src/components/Table.mjs.map +1 -1
  17. package/dist/admin/admin/src/hooks/useQueryParams.js +35 -1
  18. package/dist/admin/admin/src/hooks/useQueryParams.js.map +1 -1
  19. package/dist/admin/admin/src/hooks/useQueryParams.mjs +34 -2
  20. package/dist/admin/admin/src/hooks/useQueryParams.mjs.map +1 -1
  21. package/dist/admin/admin/src/translations/en.json.js +18 -0
  22. package/dist/admin/admin/src/translations/en.json.js.map +1 -1
  23. package/dist/admin/admin/src/translations/en.json.mjs +18 -0
  24. package/dist/admin/admin/src/translations/en.json.mjs.map +1 -1
  25. package/dist/admin/admin/src/utils/getFetchClient.js +118 -43
  26. package/dist/admin/admin/src/utils/getFetchClient.js.map +1 -1
  27. package/dist/admin/admin/src/utils/getFetchClient.mjs +118 -45
  28. package/dist/admin/admin/src/utils/getFetchClient.mjs.map +1 -1
  29. package/dist/admin/ee/admin/src/constants.js +6 -0
  30. package/dist/admin/ee/admin/src/constants.js.map +1 -1
  31. package/dist/admin/ee/admin/src/constants.mjs +6 -0
  32. package/dist/admin/ee/admin/src/constants.mjs.map +1 -1
  33. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js +40 -12
  34. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js.map +1 -1
  35. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs +43 -15
  36. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs.map +1 -1
  37. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js +177 -0
  38. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js.map +1 -0
  39. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs +175 -0
  40. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs.map +1 -0
  41. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js +173 -0
  42. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js.map +1 -0
  43. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs +151 -0
  44. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs.map +1 -0
  45. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js +10 -1
  46. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js.map +1 -1
  47. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs +10 -1
  48. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs.map +1 -1
  49. package/dist/admin/index.js +4 -0
  50. package/dist/admin/index.js.map +1 -1
  51. package/dist/admin/index.mjs +2 -2
  52. package/dist/admin/shared/utils/audit-log-export.js +16 -0
  53. package/dist/admin/shared/utils/audit-log-export.js.map +1 -0
  54. package/dist/admin/shared/utils/audit-log-export.mjs +9 -0
  55. package/dist/admin/shared/utils/audit-log-export.mjs.map +1 -0
  56. package/dist/admin/src/components/Filters.d.ts +1 -0
  57. package/dist/admin/src/hooks/useQueryParams.d.ts +13 -1
  58. package/dist/admin/src/index.d.ts +1 -1
  59. package/dist/admin/src/utils/getFetchClient.d.ts +9 -2
  60. package/dist/admin/tests/store.d.ts +4 -0
  61. package/dist/admin/tests/utils.d.ts +4 -0
  62. package/dist/ee/admin/src/constants.d.ts +4 -0
  63. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.d.ts +10 -0
  64. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.d.ts +18 -0
  65. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.d.ts +9 -0
  66. package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts +1 -0
  67. package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts.map +1 -1
  68. package/dist/ee/server/src/audit-logs/routes/audit-logs.d.ts.map +1 -1
  69. package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts +24 -3
  70. package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts.map +1 -1
  71. package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts +8 -2
  72. package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts.map +1 -1
  73. package/dist/ee/server/src/audit-logs/utils/csv.d.ts +5 -0
  74. package/dist/ee/server/src/audit-logs/utils/csv.d.ts.map +1 -0
  75. package/dist/ee/server/src/audit-logs/utils/export-token.d.ts +5 -0
  76. package/dist/ee/server/src/audit-logs/utils/export-token.d.ts.map +1 -0
  77. package/dist/ee/server/src/audit-logs/utils.d.ts +10 -0
  78. package/dist/ee/server/src/audit-logs/utils.d.ts.map +1 -0
  79. package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts +2 -0
  80. package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts.map +1 -1
  81. package/dist/ee/server/src/config/admin-actions.d.ts.map +1 -1
  82. package/dist/ee/server/src/index.d.ts +1 -0
  83. package/dist/ee/server/src/index.d.ts.map +1 -1
  84. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js +50 -0
  85. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js.map +1 -1
  86. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs +51 -1
  87. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs.map +1 -1
  88. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js +13 -4
  89. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js.map +1 -1
  90. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs +13 -4
  91. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs.map +1 -1
  92. package/dist/server/ee/server/src/audit-logs/services/audit-logs.js +201 -7
  93. package/dist/server/ee/server/src/audit-logs/services/audit-logs.js.map +1 -1
  94. package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs +201 -8
  95. package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs.map +1 -1
  96. package/dist/server/ee/server/src/audit-logs/services/lifecycles.js +119 -34
  97. package/dist/server/ee/server/src/audit-logs/services/lifecycles.js.map +1 -1
  98. package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs +119 -34
  99. package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs.map +1 -1
  100. package/dist/server/ee/server/src/audit-logs/utils/csv.js +33 -0
  101. package/dist/server/ee/server/src/audit-logs/utils/csv.js.map +1 -0
  102. package/dist/server/ee/server/src/audit-logs/utils/csv.mjs +29 -0
  103. package/dist/server/ee/server/src/audit-logs/utils/csv.mjs.map +1 -0
  104. package/dist/server/ee/server/src/audit-logs/utils/export-token.js +33 -0
  105. package/dist/server/ee/server/src/audit-logs/utils/export-token.js.map +1 -0
  106. package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs +29 -0
  107. package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs.map +1 -0
  108. package/dist/server/ee/server/src/audit-logs/utils.js +14 -0
  109. package/dist/server/ee/server/src/audit-logs/utils.js.map +1 -0
  110. package/dist/server/ee/server/src/audit-logs/utils.mjs +12 -0
  111. package/dist/server/ee/server/src/audit-logs/utils.mjs.map +1 -0
  112. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js +11 -0
  113. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js.map +1 -1
  114. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs +11 -1
  115. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs.map +1 -1
  116. package/dist/server/ee/server/src/config/admin-actions.js +8 -0
  117. package/dist/server/ee/server/src/config/admin-actions.js.map +1 -1
  118. package/dist/server/ee/server/src/config/admin-actions.mjs +8 -0
  119. package/dist/server/ee/server/src/config/admin-actions.mjs.map +1 -1
  120. package/dist/server/ee/server/src/index.js +10 -0
  121. package/dist/server/ee/server/src/index.js.map +1 -1
  122. package/dist/server/ee/server/src/index.mjs +10 -0
  123. package/dist/server/ee/server/src/index.mjs.map +1 -1
  124. package/dist/server/shared/utils/audit-log-export.js +22 -0
  125. package/dist/server/shared/utils/audit-log-export.js.map +1 -0
  126. package/dist/server/shared/utils/audit-log-export.mjs +12 -0
  127. package/dist/server/shared/utils/audit-log-export.mjs.map +1 -0
  128. package/dist/shared/contracts/audit-logs.d.ts +15 -1
  129. package/dist/shared/contracts/audit-logs.d.ts.map +1 -1
  130. package/dist/shared/utils/audit-log-export.d.ts +9 -0
  131. package/dist/shared/utils/audit-log-export.d.ts.map +1 -0
  132. package/package.json +7 -7
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"audit-logs.mjs","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.mjs","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,7 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ var utils = require('../utils.js');
4
+
3
5
  const DEFAULT_RETENTION_DAYS = 90;
4
- const defaultEvents = [
6
+ /**
7
+ * Events audited before the payload standard; their stored shape is frozen for
8
+ * compatibility. Do not add events here: new events come through registerEvent().
9
+ * TODO: migrate these to the standard.
10
+ */ const defaultEvents = [
5
11
  'entry.create',
6
12
  'entry.update',
7
13
  'entry.delete',
@@ -31,11 +37,11 @@ const defaultEvents = [
31
37
  'permission.update',
32
38
  'permission.delete'
33
39
  ];
34
- const getEventMap = (defaultEvents)=>{
35
- const getDefaultPayload = (...args)=>args[0];
36
- // Use the default payload for all default events
37
- return defaultEvents.reduce((acc, event)=>{
38
- acc[event] = getDefaultPayload;
40
+ const getEventMap = (events)=>{
41
+ return events.reduce((acc, event)=>{
42
+ acc[event] = {
43
+ kind: 'legacy'
44
+ };
39
45
  return acc;
40
46
  }, {});
41
47
  };
@@ -56,55 +62,134 @@ const getRetentionDays = (strapi)=>{
56
62
  };
57
63
  /**
58
64
  * @description
59
- * Manages the lifecycle of audit logs. Accessible via strapi.get('audit-logs-lifecycles')
65
+ * Manages the lifecycle of audit logs. Accessible via strapi.get('audit-logs-lifecycle')
60
66
  */ const createAuditLogsLifecycleService = (strapi)=>{
61
67
  // Manage internal service state privately
62
68
  const state = {};
63
69
  const auditLogsService = strapi.get('audit-logs');
64
- // NOTE: providers should be able to replace getEventMap to add or remove events
65
70
  const eventMap = getEventMap(defaultEvents);
66
- const processEvent = (name, ...args)=>{
71
+ /**
72
+ * The system origins accepted from the execution context; anything else needs a
73
+ * user. Keyed on the type, so extending SystemOrigin without gating it here fails
74
+ * to compile.
75
+ */ const SYSTEM_ORIGIN_FLAGS = {
76
+ scheduler: true
77
+ };
78
+ const SYSTEM_ORIGINS = Object.keys(SYSTEM_ORIGIN_FLAGS);
79
+ const processEvent = async (name, ...args)=>{
80
+ const registration = eventMap[name];
81
+ if (!registration) {
82
+ return null;
83
+ }
84
+ const rawEvent = args[0];
67
85
  const requestState = strapi.requestContext.get()?.state;
68
- // Only audit admin-authenticated actions, plus MCP actions flagged via auditSource.
69
- const isUsingAdminAuth = requestState?.route.info.type === 'admin';
86
+ const isUsingAdminAuth = requestState?.route?.info?.type === 'admin';
70
87
  const auditSource = requestState?.auditSource;
71
88
  const isMcpAdminAction = auditSource === 'mcp';
72
89
  const user = requestState?.user;
73
- if (!isUsingAdminAuth && !isMcpAdminAction || !user) {
90
+ const systemOrigin = auditSource && SYSTEM_ORIGINS.includes(auditSource) ? auditSource : undefined;
91
+ if (!systemOrigin && (!isUsingAdminAuth && !isMcpAdminAction || !user)) {
74
92
  return null;
75
93
  }
76
- const origin = auditSource ?? 'admin';
77
- const getPayload = eventMap[name];
78
- // Ignore the event if it's not in the map
79
- if (!getPayload) {
80
- return null;
94
+ const origin = systemOrigin ?? auditSource ?? 'admin-panel';
95
+ const date = new Date().toISOString();
96
+ // Scheduled actions have no user, so a null user is expected. The earlier audit
97
+ // entry that set the schedule records who did it.
98
+ const userId = systemOrigin ? null : user.id;
99
+ if (registration.kind === 'legacy') {
100
+ // TODO: What does this ignore in upload? Why would we want to ignore anything?
101
+ const ignoredUids = [
102
+ 'plugin::upload.file',
103
+ 'plugin::upload.folder'
104
+ ];
105
+ if (ignoredUids.includes(rawEvent?.uid)) {
106
+ return null;
107
+ }
108
+ return {
109
+ action: name,
110
+ date,
111
+ payload: {
112
+ ...rawEvent,
113
+ origin
114
+ },
115
+ userId
116
+ };
81
117
  }
82
- // Ignore some events based on payload
83
- // TODO: What does this ignore in upload? Why would we want to ignore anything?
84
- const ignoredUids = [
85
- 'plugin::upload.file',
86
- 'plugin::upload.folder'
87
- ];
88
- if (ignoredUids.includes(args[0]?.uid)) {
89
- return null;
118
+ const actor = systemOrigin ? {
119
+ type: 'system'
120
+ } : {
121
+ type: 'admin-user',
122
+ // We copy the user data into the row so the history stays unchanged if the
123
+ // user changes later
124
+ user: {
125
+ id: user.id,
126
+ email: user.email,
127
+ name: utils.getDisplayName(user)
128
+ }
129
+ };
130
+ let shape = null;
131
+ try {
132
+ shape = await registration.transform(...args);
133
+ } catch (error) {
134
+ // If the transformer fails, we still keep the audit event: the row is saved
135
+ // with the subscriber's fields only.
136
+ strapi.log.error(`Failed to build the audit log payload for ${name}`, {
137
+ error
138
+ });
90
139
  }
140
+ const { action: _action, date: _date, actor: _actor, origin: _origin, ...rest } = shape ?? {};
141
+ const payload = shape ? {
142
+ action: name,
143
+ date,
144
+ actor,
145
+ origin,
146
+ ...rest
147
+ } : {
148
+ action: name,
149
+ date,
150
+ actor,
151
+ origin
152
+ };
91
153
  return {
92
154
  action: name,
93
- date: new Date().toISOString(),
94
- payload: {
95
- ...getPayload(...args) || {},
96
- origin
97
- },
98
- userId: user.id
155
+ date,
156
+ payload,
157
+ userId
99
158
  };
100
159
  };
101
160
  const handleEvent = async (name, ...args)=>{
102
- const processedEvent = processEvent(name, ...args);
103
- if (processedEvent) {
104
- await auditLogsService.saveEvent(processedEvent);
161
+ try {
162
+ const processedEvent = await processEvent(name, ...args);
163
+ if (processedEvent) {
164
+ await auditLogsService.saveEvent(processedEvent);
165
+ }
166
+ } catch (error) {
167
+ // Logged, not propagated: most emitters fire and forget (the document service
168
+ // emits entry.* from an unawaited onCommit callback), and a rejection with no
169
+ // one awaiting it takes the process down. Audit logging is therefore fail-open:
170
+ // the entry is lost and only this line records it.
171
+ strapi.log.error(`Failed to save the audit log entry for ${name}`, {
172
+ error
173
+ });
105
174
  }
106
175
  };
107
176
  return {
177
+ /**
178
+ * Adds an event to the list of audited events. Plugins register their own from
179
+ * their bootstrap; the transformer builds the audited shape
180
+ * ({resource, details, outcome}) from the arguments passed to eventHub.emit().
181
+ */ registerEvent (name, transform) {
182
+ if (eventMap[name]?.kind === 'legacy') {
183
+ throw new Error(`Cannot register the audit log event "${name}": it is one of the built-in events, whose stored shape is frozen.`);
184
+ }
185
+ if (eventMap[name]) {
186
+ strapi.log.warn(`The audit log event "${name}" was already registered and is replaced.`);
187
+ }
188
+ eventMap[name] = {
189
+ kind: 'standard',
190
+ transform: transform
191
+ };
192
+ },
108
193
  async register () {
109
194
  // Handle license being enabled
110
195
  if (!state.eeEnableUnsubscribe) {