Files
goodgo-platform/docs/audits/ADMIN_AUDIT_QUICK_FILES.md
Ho Ngoc Hai 59272e9321 chore(docs): consolidate 22 audit files from root into docs/audits/
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>
2026-04-10 23:16:00 +07:00

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.)