Root directory had accumulated audit/exploration markdown files cluttering the project root. Moved all audit-related files to docs/audits/ with a README.md index, and updated cross-references in K6_LOAD_TESTING_GUIDE.md and README_FRONTEND_DOCS.md. Co-Authored-By: Paperclip <noreply@paperclip.ing>
298 lines
9.0 KiB
Markdown
298 lines
9.0 KiB
Markdown
# Quick File Reference for Admin Module Audit Logging
|
|
|
|
## MUST READ FIRST (15 min total)
|
|
|
|
### 1. Main Controllers (Define what actions need audit)
|
|
- ✅ `apps/api/src/modules/admin/presentation/controllers/admin.controller.ts` (155 lines)
|
|
- User management: ban, update status, adjust subscription
|
|
- All endpoints have @CurrentUser() decorator to capture admin ID
|
|
|
|
- ✅ `apps/api/src/modules/admin/presentation/controllers/admin-moderation.controller.ts` (157 lines)
|
|
- Listing approval/rejection
|
|
- KYC approval/rejection
|
|
- Bulk moderation
|
|
|
|
### 2. Command Handlers (Where to hook audit logging)
|
|
Each command publishes a domain event. Audit logging listener should listen to these events.
|
|
|
|
**Ban User Flow:**
|
|
- Input: `apps/api/src/modules/admin/presentation/dto/ban-user.dto.ts`
|
|
- Command: `apps/api/src/modules/admin/application/commands/ban-user/ban-user.command.ts`
|
|
- Handler: `apps/api/src/modules/admin/application/commands/ban-user/ban-user.handler.ts` (70 lines)
|
|
- Line 62: `this.eventBus.publish(new UserBannedEvent(...))`
|
|
|
|
**Approve Listing Flow:**
|
|
- Input: `apps/api/src/modules/admin/presentation/dto/approve-listing.dto.ts`
|
|
- Command: `apps/api/src/modules/admin/application/commands/approve-listing/approve-listing.command.ts`
|
|
- Handler: `apps/api/src/modules/admin/application/commands/approve-listing/approve-listing.handler.ts` (52 lines)
|
|
- Line 42-44: `this.eventBus.publish(new ListingApprovedEvent(...))`
|
|
|
|
### 3. Domain Events (What information is published)
|
|
```
|
|
apps/api/src/modules/admin/domain/events/
|
|
├── user-banned.event.ts
|
|
├── user-unbanned.event.ts
|
|
├── listing-approved.event.ts
|
|
├── listing-rejected.event.ts
|
|
├── subscription-adjusted.event.ts
|
|
├── kyc-approved.event.ts
|
|
└── kyc-rejected.event.ts
|
|
```
|
|
|
|
Each event has:
|
|
- `eventName` (e.g., 'user.banned')
|
|
- `occurredAt` (timestamp)
|
|
- `aggregateId` (userId or listingId)
|
|
- `adminId` (the admin who performed the action)
|
|
- Additional context (reason, notes, etc.)
|
|
|
|
### 4. Existing Event Listener Pattern (Template)
|
|
- ✅ `apps/api/src/modules/admin/application/listeners/user-banned.listener.ts` (52 lines)
|
|
- Shows @OnEvent() decorator
|
|
- Shows how to access event data
|
|
- Shows side effects (deactivate listings, send notification)
|
|
- **THIS IS YOUR TEMPLATE FOR AUDIT LOGGING LISTENER**
|
|
|
|
### 5. Logger Service (Where to log)
|
|
- ✅ `apps/api/src/modules/shared/infrastructure/logger.service.ts` (65 lines)
|
|
- Pino-based structured logging
|
|
- Auto PII redaction
|
|
- Methods: log(), error(), warn(), debug(), verbose()
|
|
|
|
### 6. Exception Filter (For error logging)
|
|
- ✅ `apps/api/src/modules/shared/infrastructure/filters/global-exception.filter.ts` (145 lines)
|
|
- Catches all exceptions
|
|
- Logs with correlationId
|
|
- Could capture failed admin actions
|
|
|
|
---
|
|
|
|
## ARCHITECTURE REFERENCES
|
|
|
|
### Repository Pattern
|
|
- Domain Interface: `apps/api/src/modules/admin/domain/repositories/admin-query.repository.ts`
|
|
- Prisma Implementation: `apps/api/src/modules/admin/infrastructure/repositories/prisma-admin-query.repository.ts`
|
|
- **FOLLOW THIS PATTERN** for AuditLog repository
|
|
|
|
### Module Bootstrap
|
|
- `apps/api/src/modules/admin/admin.module.ts` (64 lines)
|
|
- Shows how to register repositories via DI
|
|
- Shows how to register listeners
|
|
- Shows how to import CQRS module
|
|
|
|
### Global App Setup
|
|
- `apps/api/src/app.module.ts` (100+ lines)
|
|
- Shows APP_FILTER, APP_GUARD, APP_INTERCEPTOR registration
|
|
- Shows CqrsModule.forRoot() setup
|
|
- Shows middleware configuration
|
|
|
|
---
|
|
|
|
## PRISMA SCHEMA
|
|
|
|
### Current Models (What we're auditing)
|
|
- `prisma/schema.prisma` (602 lines total)
|
|
|
|
**User Model** (lines 34-71):
|
|
- Fields to audit: isActive, kycStatus, role
|
|
|
|
**Listing Model** (lines 227-276):
|
|
- Fields to audit: status, moderationScore, moderationNotes
|
|
|
|
**NO AUDIT MODEL YET** - Opportunity to create from scratch
|
|
|
|
---
|
|
|
|
## EXACT ENDPOINTS TO AUDIT (From Controllers)
|
|
|
|
### AdminController Actions:
|
|
1. `PATCH /admin/users/status` - Update user active status
|
|
2. `POST /admin/users/ban` - Ban/unban user
|
|
3. `POST /admin/subscriptions/adjust` - Adjust subscription
|
|
|
|
### AdminModerationController Actions:
|
|
1. `POST /admin/moderation/approve` - Approve listing
|
|
2. `POST /admin/moderation/reject` - Reject listing
|
|
3. `POST /admin/moderation/bulk` - Bulk moderate listings
|
|
4. `POST /admin/kyc/approve` - Approve KYC
|
|
5. `POST /admin/kyc/reject` - Reject KYC
|
|
|
|
Each action:
|
|
- Already captures admin ID from JWT
|
|
- Already publishes a domain event
|
|
- Already has a command handler
|
|
- Needs: Audit logging listener to capture to database
|
|
|
|
---
|
|
|
|
## DEPENDENCIES ALREADY IMPORTED
|
|
|
|
### In AdminModule:
|
|
```typescript
|
|
// Already available:
|
|
- CqrsModule (from @nestjs/cqrs)
|
|
- AuthModule (auth guards/decorators)
|
|
- ListingsModule (for listing operations)
|
|
- SubscriptionsModule (for subscription operations)
|
|
|
|
// In providers:
|
|
- CommandHandlers (8 total)
|
|
- QueryHandlers (6 total)
|
|
- Event Listeners (2 existing + need to add AuditLoggingListener)
|
|
```
|
|
|
|
### From SharedModule:
|
|
- `PrismaService` (database)
|
|
- `LoggerService` (logging)
|
|
- Exception types (NotFoundException, ValidationException, etc.)
|
|
|
|
---
|
|
|
|
## IMPLEMENTATION CHECKLIST
|
|
|
|
### Phase 1: Database & Repository
|
|
- [ ] Create AuditLog Prisma model in schema.prisma
|
|
- [ ] Create IAuditLogRepository interface
|
|
- [ ] Create PrismaAuditLogRepository implementation
|
|
- [ ] Add to AdminModule providers
|
|
|
|
### Phase 2: Events & Listeners
|
|
- [ ] Create AuditEvent domain event (if needed as wrapper)
|
|
- [ ] Create AuditLoggingListener to @OnEvent() for all admin events
|
|
- [ ] Inject AuditLogRepository into listener
|
|
- [ ] Persist audit records on event
|
|
|
|
### Phase 3: Query & API
|
|
- [ ] Create GetAuditLogsQuery
|
|
- [ ] Create GetAuditLogsHandler
|
|
- [ ] Create IAuditLogQueryRepository method
|
|
- [ ] Add to QueryHandlers in module
|
|
|
|
### Phase 4: Controller Endpoint
|
|
- [ ] Add GET /admin/audit-logs endpoint
|
|
- [ ] Add filtering DTOs (dateRange, adminId, actionType, resourceId)
|
|
- [ ] Add pagination support
|
|
|
|
### Phase 5: Testing
|
|
- [ ] Unit tests for AuditLoggingListener
|
|
- [ ] Integration tests for audit persistence
|
|
- [ ] E2E tests for audit log retrieval
|
|
|
|
---
|
|
|
|
## CRITICAL PATTERNS TO FOLLOW
|
|
|
|
### 1. Command Pattern (Already Used)
|
|
```typescript
|
|
// DTOs validate input
|
|
// Commands encapsulate business intent
|
|
// Handlers execute + publish events
|
|
// Events trigger side effects via listeners
|
|
```
|
|
|
|
### 2. DDD Layer Structure
|
|
```
|
|
Presentation (DTO validation)
|
|
↓
|
|
Application (Command/Query execution + Event publishing)
|
|
↓
|
|
Domain (Event definitions, Repository interfaces)
|
|
↓
|
|
Infrastructure (Database implementation)
|
|
```
|
|
|
|
### 3. Dependency Injection
|
|
```typescript
|
|
// Always use Symbol for tokens:
|
|
export const AUDIT_LOG_REPOSITORY = Symbol('AUDIT_LOG_REPOSITORY');
|
|
|
|
// Register in module:
|
|
{ provide: AUDIT_LOG_REPOSITORY, useClass: PrismaAuditLogRepository }
|
|
|
|
// Inject in service:
|
|
constructor(
|
|
@Inject(AUDIT_LOG_REPOSITORY) private readonly auditRepo: IAuditLogRepository,
|
|
) {}
|
|
```
|
|
|
|
### 4. Event Listener Pattern
|
|
```typescript
|
|
@Injectable()
|
|
export class AuditLoggingListener {
|
|
constructor(
|
|
@Inject(AUDIT_LOG_REPOSITORY) private readonly auditRepo: IAuditLogRepository,
|
|
private readonly logger: LoggerService,
|
|
) {}
|
|
|
|
@OnEvent('user.banned', { async: true })
|
|
async handleUserBanned(event: UserBannedEvent): Promise<void> {
|
|
// Extract data from event
|
|
// Persist to database
|
|
// Log if successful/failed
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## WHERE TO ADD CODE
|
|
|
|
```
|
|
apps/api/src/modules/admin/
|
|
├── domain/
|
|
│ ├── events/
|
|
│ │ └── audit-logged.event.ts (NEW - optional wrapper)
|
|
│ └── repositories/
|
|
│ ├── audit-log.repository.ts (NEW - interface)
|
|
│ └── index.ts (update exports)
|
|
│
|
|
├── application/
|
|
│ ├── queries/
|
|
│ │ ├── get-audit-logs/ (NEW)
|
|
│ │ │ ├── get-audit-logs.query.ts
|
|
│ │ │ └── get-audit-logs.handler.ts
|
|
│ │ └── index.ts (update exports)
|
|
│ │
|
|
│ └── listeners/
|
|
│ ├── audit-logging.listener.ts (NEW)
|
|
│ └── index.ts (update if needed)
|
|
│
|
|
├── infrastructure/
|
|
│ └── repositories/
|
|
│ ├── prisma-audit-log.repository.ts (NEW)
|
|
│ └── index.ts (update exports)
|
|
│
|
|
└── presentation/
|
|
├── controllers/
|
|
│ ├── admin.controller.ts (ADD ENDPOINT)
|
|
│ └── admin-moderation.controller.ts (UPDATE if needed)
|
|
│
|
|
└── dto/
|
|
├── get-audit-logs-query.dto.ts (NEW)
|
|
└── index.ts (update exports)
|
|
|
|
prisma/
|
|
└── schema.prisma (ADD AuditLog MODEL)
|
|
```
|
|
|
|
---
|
|
|
|
## EVENTS TO LISTEN TO
|
|
|
|
1. 'user.banned' - from UserBannedEvent
|
|
2. 'user.unbanned' - from UserUnbannedEvent
|
|
3. 'listing.approved' - from ListingApprovedEvent
|
|
4. 'listing.rejected' - from ListingRejectedEvent
|
|
5. 'kyc.approved' - from KycApprovedEvent
|
|
6. 'kyc.rejected' - from KycRejectedEvent
|
|
7. 'subscription.adjusted' - from SubscriptionAdjustedEvent
|
|
8. 'user.deactivated' - (if exists in auth module)
|
|
|
|
Each gets logged with:
|
|
- Admin ID (from event)
|
|
- Resource ID (aggregateId from event)
|
|
- Resource Type (derived from eventName)
|
|
- Timestamp (from event.occurredAt)
|
|
- Additional context (reason, notes, etc.)
|
|
|