From bb7b8ee2279b01e5d1066231b47b113859b677df Mon Sep 17 00:00:00 2001 From: Simon Gardner Date: Wed, 14 Jan 2026 09:12:02 +0000 Subject: [PATCH] [web] Add audit log entries for project-history-version-restore and project-history-version-download GitOrigin-RevId: 8cfe1fee733aa886cd6518d352abd95bd9da6f77 --- .../Features/History/HistoryController.mjs | 51 +++++++++++++++++++ .../Project/ProjectAuditLogHandler.mjs | 2 + .../src/History/HistoryController.test.mjs | 9 ++++ 3 files changed, 62 insertions(+) diff --git a/services/web/app/src/Features/History/HistoryController.mjs b/services/web/app/src/Features/History/HistoryController.mjs index e0cf991bdf..71fd824cdd 100644 --- a/services/web/app/src/Features/History/HistoryController.mjs +++ b/services/web/app/src/Features/History/HistoryController.mjs @@ -26,6 +26,7 @@ import RestoreManager from './RestoreManager.mjs' import { prepareZipAttachment } from '../../infrastructure/Response.mjs' import Features from '../../infrastructure/Features.mjs' import { z, zz, validateReq } from '../../infrastructure/Validation.mjs' +import ProjectAuditLogHandler from '../Project/ProjectAuditLogHandler.mjs' // Number of seconds after which the browser should send a request to revalidate // blobs @@ -192,6 +193,19 @@ async function restoreFileFromV2(req, res, next) { pathname ) + ProjectAuditLogHandler.addEntryIfManagedInBackground( + projectId, + 'project-history-version-restored', + userId, + req.ip, + { + version, + scope: 'file', + pathname, + restoredEntityId: entity._id, + } + ) + res.json({ type: entity.type, id: entity._id, @@ -211,6 +225,19 @@ async function revertFile(req, res, next) { {} ) + ProjectAuditLogHandler.addEntryIfManagedInBackground( + projectId, + 'project-history-version-restored', + userId, + req.ip, + { + version, + scope: 'file', + pathname, + restoredEntityId: entity._id, + } + ) + res.json({ type: entity.type, id: entity._id, @@ -228,6 +255,18 @@ async function revertProject(req, res, next) { version ) + ProjectAuditLogHandler.addEntryIfManagedInBackground( + projectId, + 'project-history-version-restored', + userId, + req.ip, + { + version, + scope: 'project', + restoredEntities: reverted, + } + ) + res.json(reverted) } @@ -346,6 +385,7 @@ async function deleteLabel(req, res, next) { async function downloadZipOfVersion(req, res, next) { const { project_id: projectId, version } = req.params + const userId = SessionManager.getLoggedInUserId(req.session) const project = await ProjectDetailsHandler.promises.getDetails(projectId) const v1Id = @@ -366,6 +406,17 @@ async function downloadZipOfVersion(req, res, next) { req, res ) + + ProjectAuditLogHandler.addEntryIfManagedInBackground( + projectId, + 'project-history-version-downloaded', + userId, + req.ip, + { + version, + projectName: project.name, + } + ) } async function _pipeHistoryZipToResponse(v1ProjectId, version, name, req, res) { diff --git a/services/web/app/src/Features/Project/ProjectAuditLogHandler.mjs b/services/web/app/src/Features/Project/ProjectAuditLogHandler.mjs index 6783df19fc..fb34a5a2fa 100644 --- a/services/web/app/src/Features/Project/ProjectAuditLogHandler.mjs +++ b/services/web/app/src/Features/Project/ProjectAuditLogHandler.mjs @@ -15,6 +15,8 @@ const MANAGED_GROUP_PROJECT_EVENTS = [ 'project-untrashed', 'project-restored', 'project-cloned', + 'project-history-version-restored', + 'project-history-version-downloaded', 'transfer-ownership', 'project-downloaded', ] diff --git a/services/web/test/unit/src/History/HistoryController.test.mjs b/services/web/test/unit/src/History/HistoryController.test.mjs index 6d409ca120..4796d2ddaf 100644 --- a/services/web/test/unit/src/History/HistoryController.test.mjs +++ b/services/web/test/unit/src/History/HistoryController.test.mjs @@ -103,6 +103,15 @@ describe('HistoryController', function () { }) ) + vi.doMock( + '../../../../app/src/Features/Project/ProjectAuditLogHandler.mjs', + () => ({ + default: (ctx.ProjectAuditLogHandler = { + addEntryIfManagedInBackground: sinon.stub(), + }), + }) + ) + vi.doMock('../../../../app/src/Features/User/UserGetter.mjs', () => ({ default: (ctx.UserGetter = {}), }))