From af6dffe5347aa008d54f25cbaf38f70f93fb1f82 Mon Sep 17 00:00:00 2001 From: joshua Date: Fri, 28 Aug 2026 14:44:23 +0200 Subject: [PATCH 1/2] Log event creation to admin audit log, add back button, bump to 1.10.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createEvent never called logAdminAction, even though 1.10.0 already listed event_created as a filterable action on the audit-log page — only updateEvent/deleteEvent actually logged. Event creation is now logged the same way, at every return path including the legacy pre-migration retry branches. Also adds a "Back to dashboard" link to Admin -> Audit log, matching the existing back-link pattern on the Cashup page. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01CSWFWQsjTc9GyffPiXEDQT --- CHANGELOG.md | 7 +++++++ backend/package-lock.json | 4 ++-- backend/package.json | 2 +- backend/src/controllers/eventController.js | 15 +++++++++++++++ frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- .../src/app/dashboard/admin/audit-log/page.tsx | 3 ++- package-lock.json | 4 ++-- package.json | 2 +- 9 files changed, 33 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32a1c8c..9be170b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project follows [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [1.10.3] - 2026-08-28 + +### Fixed + +- Creating an event was never recorded in the admin audit log — 1.10.0 added `event_created` as a logged action on the frontend's filter list, but the backend's `createEvent` never actually called `logAdminAction`, only `updateEvent`/`deleteEvent` did. Event creation is now logged the same way. +- Added a "Back to dashboard" link to Admin → Audit log, matching the back-link pattern already used on the Cashup page. + ## [1.10.2] - 2026-08-28 ### Fixed diff --git a/backend/package-lock.json b/backend/package-lock.json index 47ab166..8968d6e 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "event-management-backend", - "version": "1.10.2", + "version": "1.10.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "event-management-backend", - "version": "1.10.2", + "version": "1.10.3", "hasInstallScript": true, "dependencies": { "@prisma/client": "^5.4.2", diff --git a/backend/package.json b/backend/package.json index 169fa7e..c6112bc 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "event-management-backend", - "version": "1.10.2", + "version": "1.10.3", "description": "Event Management System Backend", "main": "src/index.js", "scripts": { diff --git a/backend/src/controllers/eventController.js b/backend/src/controllers/eventController.js index f0dc00b..ebce80e 100644 --- a/backend/src/controllers/eventController.js +++ b/backend/src/controllers/eventController.js @@ -68,8 +68,19 @@ const createEvent = async (req, res) => { location: location || null, }; + const logEventCreate = (createdEvent) => logAdminAction({ + actorId: req.user?.id, + actorRole: req.user?.role, + action: 'event_created', + targetType: 'Event', + targetId: createdEvent.id, + metadata: { title: createdEvent.title }, + ip: getClientIp(req), + }); + try { const event = await prisma.event.create({ data }); + logEventCreate(event); // Automatically create a main ticket (event option) with the event price — contact-only // events have no bookable options, so there's nothing to auto-create for them. @@ -133,24 +144,28 @@ const createEvent = async (req, res) => { // @ts-ignore delete field and retry delete data.registrationDeadline; const event = await prisma.event.create({ data }); + logEventCreate(event); return res.status(201).json(event); } if (msg.includes('Unknown argument `goLiveAt`')) { // @ts-ignore delete field and retry delete data.goLiveAt; const event = await prisma.event.create({ data }); + logEventCreate(event); return res.status(201).json(event); } if (msg.includes('Unknown argument `createdById`')) { // @ts-ignore delete field and retry delete data.createdById; const event = await prisma.event.create({ data }); + logEventCreate(event); return res.status(201).json(event); } if (msg.includes('Unknown argument `redirectUrl`')) { // @ts-ignore delete field and retry delete data.redirectUrl; const event = await prisma.event.create({ data }); + logEventCreate(event); return res.status(201).json(event); } throw err; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index be1135f..8f95047 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "hope-events-frontend", - "version": "1.10.2", + "version": "1.10.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hope-events-frontend", - "version": "1.10.2", + "version": "1.10.3", "dependencies": { "@hookform/resolvers": "^5.2.1", "@radix-ui/react-accordion": "^1.2.11", diff --git a/frontend/package.json b/frontend/package.json index 3dfa383..9ac79f6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "hope-events-frontend", - "version": "1.10.2", + "version": "1.10.3", "private": true, "scripts": { "dev": "next dev --turbopack", diff --git a/frontend/src/app/dashboard/admin/audit-log/page.tsx b/frontend/src/app/dashboard/admin/audit-log/page.tsx index 13c8320..e4cc110 100644 --- a/frontend/src/app/dashboard/admin/audit-log/page.tsx +++ b/frontend/src/app/dashboard/admin/audit-log/page.tsx @@ -113,7 +113,8 @@ export default function AdminAuditLogPage() {
-

Admin Audit Log

+ +

Admin Audit Log

{total} action{total !== 1 ? "s" : ""} recorded

diff --git a/package-lock.json b/package-lock.json index 4176b9f..abbc376 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hope-events", - "version": "1.10.2", + "version": "1.10.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hope-events", - "version": "1.10.2", + "version": "1.10.3", "license": "ISC", "devDependencies": { "concurrently": "^9.2.1" diff --git a/package.json b/package.json index ac8ad79..e0ef910 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hope-events", - "version": "1.10.2", + "version": "1.10.3", "main": "index.js", "scripts": { "dev:backend": "cd backend && npm run dev", From 5b2183677d279ed33c82eb34937eb431618bc7e0 Mon Sep 17 00:00:00 2001 From: joshua Date: Fri, 28 Aug 2026 14:48:17 +0200 Subject: [PATCH 2/2] Also log staff cancellations via PUT /api/registrations/:id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registrations can be cancelled two ways: DELETE /:id (owner or admin, already logged registration_cancelled) and PUT /:id (staff+, status change endpoint) — the latter was silently unlogged. Since PUT /:id is staff-only, any transition into 'cancelled' there is inherently a staff-initiated cancellation, so it's now logged the same way. Audited all six categories promised in the 1.10.0 changelog entry (refunds, donation assign/unassign, manual registrations, staff-initiated cancellations, event create/update/delete, settings changes) against their actual logAdminAction call sites and route wiring — this was the only other gap found. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01CSWFWQsjTc9GyffPiXEDQT --- CHANGELOG.md | 1 + .../src/controllers/registrationController.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9be170b..a8cd13d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project follows [Semantic Versioning](https://semver.org/). ### Fixed - Creating an event was never recorded in the admin audit log — 1.10.0 added `event_created` as a logged action on the frontend's filter list, but the backend's `createEvent` never actually called `logAdminAction`, only `updateEvent`/`deleteEvent` did. Event creation is now logged the same way. +- Staff cancelling a registration via `PUT /api/registrations/:id` (the staff status-change endpoint, separate from the owner-facing `DELETE /:id` cancel route) wasn't logged at all — only the `DELETE` path logged `registration_cancelled`. Both paths now log it, verified against all six audit categories promised in 1.10.0 (refunds, donation assign/unassign, manual registrations, staff-initiated cancellations, event create/update/delete, settings changes) with no other gaps found. - Added a "Back to dashboard" link to Admin → Audit log, matching the back-link pattern already used on the Cashup page. ## [1.10.2] - 2026-08-28 diff --git a/backend/src/controllers/registrationController.js b/backend/src/controllers/registrationController.js index fafb112..3f22e84 100644 --- a/backend/src/controllers/registrationController.js +++ b/backend/src/controllers/registrationController.js @@ -626,6 +626,22 @@ const updateRegistrationStatus = async (req, res) => { } }); + // This is the staff-only status-change endpoint (separate from the owner-facing + // DELETE /:id cancel route), so any transition into 'cancelled' here is always a + // staff-initiated cancellation — log it the same way DELETE /:id does, so both + // paths land under the one 'registration_cancelled' filter in the audit log. + if (status === 'cancelled' && registration.status !== 'cancelled') { + logAdminAction({ + actorId: req.user.id, + actorRole: req.user.role, + action: 'registration_cancelled', + targetType: 'Registration', + targetId: req.params.id, + metadata: { registrationOwnerId: registration.userId, previousStatus: registration.status }, + ip: getClientIp(req), + }); + } + // Generate tickets and email them when status is manually set to 'paid' by staff if (status === 'paid') { (async () => {