diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 84378fd..622e292 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -263,12 +263,17 @@ model AuditLog { action String // e.g., "MATCH_OVERRIDE", "PROPOSAL_EXECUTE", "MANUAL_PAYOUT" targetType String // e.g., "MATCH", "PROPOSAL", "USER" targetId String - details Json? - ipAddress String? - createdAt DateTime @default(now()) + details Json? + requestId String? + snapshotBefore Json? + snapshotAfter Json? + ipAddress String? + userAgent String? + createdAt DateTime @default(now()) @@index([adminId]) @@index([action]) + @@index([requestId]) } enum RefundStatus { @@ -295,3 +300,4 @@ model RefundRequest { @@index([paymentId]) @@index([status]) } + diff --git a/server/src/controllers/admin.controller.ts b/server/src/controllers/admin.controller.ts index 2533cfe..53ff17c 100644 --- a/server/src/controllers/admin.controller.ts +++ b/server/src/controllers/admin.controller.ts @@ -24,6 +24,19 @@ export const resolveDispute = async (req: Request, res: Response): Promise resolution, winnerOverrideId }); + + // Detailed Audit + await AuditService.logAction({ + adminId: req.user!.id, + action: 'RESOLVE_DISPUTE', + targetType: 'DISPUTE', + targetId: id, + details: { status, resolution }, + requestId: req.auditContext?.requestId, + ipAddress: req.auditContext?.ipAddress, + userAgent: req.auditContext?.userAgent + }); + res.status(200).json(result); } catch (error) { res.status(400).json({ error: (error as Error).message }); @@ -61,14 +74,16 @@ export const replayPayment = async (req: Request, res: Response): Promise } }); - // Log the action + // Log the action with detailed context await AuditService.logAction({ adminId: req.user!.id, action: 'REPLAY_PAYMENT', targetType: 'PAYMENT', targetId: id, details: { previousStatus: payment.status }, - ipAddress: req.ip + requestId: req.auditContext?.requestId, + ipAddress: req.auditContext?.ipAddress, + userAgent: req.auditContext?.userAgent }); res.status(200).json({ message: 'Payment replay triggered successfully.' }); diff --git a/server/src/controllers/refund.controller.ts b/server/src/controllers/refund.controller.ts index da6d9fb..2f1b8aa 100644 --- a/server/src/controllers/refund.controller.ts +++ b/server/src/controllers/refund.controller.ts @@ -27,6 +27,10 @@ export const updateRefundStatus = async (req: Request, res: Response): Promise { + // Audit context can be used by controllers to simplify logAction calls + req.auditContext = { + requestId: req.requestId || 'unknown', + ipAddress: req.ip || 'unknown', + userAgent: req.header('user-agent') || 'unknown' + }; + next(); +}; + +declare global { + namespace Express { + interface Request { + auditContext?: { + requestId: string; + ipAddress: string; + userAgent: string; + }; + } + } +} diff --git a/server/src/routes/index.ts b/server/src/routes/index.ts index 22a5aad..799120a 100644 --- a/server/src/routes/index.ts +++ b/server/src/routes/index.ts @@ -8,10 +8,12 @@ import walletRoutes from './wallet.routes'; import matchRoutes from './match.routes'; import { publicRateLimiter } from '../middleware/rate-limit.middleware'; +import { auditMiddleware } from '../middleware/audit.middleware'; const router = Router(); router.use(publicRateLimiter); +router.use(auditMiddleware); router.use('/auth', authRoutes); router.use('/profiles', profileRoutes); router.use('/matches', matchRoutes); // Added diff --git a/server/src/services/audit.service.ts b/server/src/services/audit.service.ts index 337aee2..4b4a80d 100644 --- a/server/src/services/audit.service.ts +++ b/server/src/services/audit.service.ts @@ -11,6 +11,10 @@ export class AuditService { targetId: string; details?: any; ipAddress?: string; + userAgent?: string; + requestId?: string; + snapshotBefore?: any; + snapshotAfter?: any; }) { const prisma = getDatabaseClient(); return await prisma.auditLog.create({ @@ -21,6 +25,10 @@ export class AuditService { targetId: data.targetId, details: data.details || {}, ipAddress: data.ipAddress, + userAgent: data.userAgent, + requestId: data.requestId, + snapshotBefore: data.snapshotBefore || {}, + snapshotAfter: data.snapshotAfter || {}, }, }); }