Skip to content

feat(audit): add admin audit log for sensitive actions#34

Open
kimanicode wants to merge 1 commit into
MyFanss:mainfrom
kimanicode:feat/admin-audit-log
Open

feat(audit): add admin audit log for sensitive actions#34
kimanicode wants to merge 1 commit into
MyFanss:mainfrom
kimanicode:feat/admin-audit-log

Conversation

@kimanicode

Copy link
Copy Markdown

Summary

Adds an append-only audit trail for sensitive admin/user actions (role changes, user deletions, failed logins), exposed via a paginated, filterable admin-only endpoint.

Closes #25

What's included

  • AuditLog entity + migrationactorId, action, targetType, targetId,
    metadata (jsonb), ipAddress, createdAt. Indexes on createdAt, action, actorId.
  • AuditService — injectable log() method, used across modules. Strips
    passwords/tokens/secrets from metadata before persisting; no update/delete methods exist
    on the service or entity.
  • GET /admin/audit-logs — admin-only (via new AdminGuard), paginated, filterable by
    action, actorId, and date range. No PATCH/PUT/DELETE routes — logs are immutable.
  • Integration:
    • New PATCH /users/:id/role endpoint logs USER_ROLE_CHANGED with before/after role
      in metadata
    • User deletion logs USER_DELETED
    • Failed login attempts log USER_LOGIN_FAILED (admin-visible only, never returned to
      the end user) with ipAddress captured
  • docs/audit-log.md — schema, action list, endpoint usage, redaction policy
  • Tests — unit tests for AuditService and AuditController, e2e tests for admin-only
    access and immutability

Deviations from the issue spec

  • No pre-existing admin guard in the repo — added AdminGuard in src/audit/admin.guard.ts.
    It looks up the user's role from the DB since the JWT payload doesn't carry role. Happy to
    move this somewhere more central (e.g. src/common/guards/) if that's preferred — flagging
    for review.
  • No pre-existing role-change endpoint — added PATCH /users/:id/role as a new admin-only
    route, since the existing updateUser() is unguarded and a poor fit for audit-logged role
    changes. Open to feedback on the route shape.
  • actorId is nullable — the issue lists it as part of the core fields, but also implies
    USER_LOGIN_FAILED may have no known actor (e.g. unknown/invalid username). Made it
    nullable to support that case.
  • Pagination is simple offset-based (page/limit), not the cursor-based pagination used
    elsewhere for users. Audit logs are append-only and queried by filters/date range rather
    than infinite-scroll, so keyset pagination didn't seem worth the added complexity — reused
    the existing PaginatedResponseDto wrapper either way.

Testing

  • ✅ Unit tests: 10 suites / 77 tests passing (including all pre-existing)
  • tsc --noEmit — no type errors
  • ✅ Lint passes
  • ⚠️ E2E (test/audit.e2e-spec.ts) — written and included, but could not run locally
    due to no Postgres test DB available in this sandbox (my_fans_test on localhost). Please
    run in CI or let me know if you'd like me to set up a local DB and re-verify before merge.

Checklist (against acceptance criteria)

  • Role change creates audit entry with before/after role in metadata
  • Non-admin cannot read audit logs (403)
  • Audit entries are immutable (no update/delete endpoints)
  • Pagination works on audit list
  • Sensitive fields redacted in metadata
  • 8+ tests (12 across unit + e2e)

- Add AuditLog entity, migration, service, and admin-only controller
- Log USER_ROLE_CHANGED, USER_DELETED, USER_LOGIN_FAILED events
- Add GET /admin/audit-logs with pagination and filtering by action,
  actorId, and date range
- Add AdminGuard for audit endpoints (role lookup from DB)
- Add PATCH /users/:id/role endpoint, wired to audit logging
- Redact sensitive fields (passwords, tokens) from logged metadata
- Add indexes on createdAt, action, actorId
- Add docs/audit-log.md
- Add unit and e2e tests for service, controller, and admin-only access

Closes MyFanss#25
@aji70

aji70 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

@kimanicode Please fix the CI to pass

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Build Admin Audit Log for Sensitive Actions

2 participants