Skip to content
Merged
12 changes: 9 additions & 3 deletions server/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -295,3 +300,4 @@ model RefundRequest {
@@index([paymentId])
@@index([status])
}

19 changes: 17 additions & 2 deletions server/src/controllers/admin.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ export const resolveDispute = async (req: Request, res: Response): Promise<void>
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 });
Expand Down Expand Up @@ -61,14 +74,16 @@ export const replayPayment = async (req: Request, res: Response): Promise<void>
}
});

// 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.' });
Expand Down
4 changes: 4 additions & 0 deletions server/src/controllers/refund.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export const updateRefundStatus = async (req: Request, res: Response): Promise<v
operatorNotes
);

// Detailed Audit (Service already logs some, but we can enrich here if needed)
// Actually, the service should probably take the audit context.
// Let's update RefundService better later.

res.status(200).json(updatedRequest);
} catch (error) {
res.status(400).json({ error: (error as Error).message });
Expand Down
26 changes: 26 additions & 0 deletions server/src/middleware/audit.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Request, Response, NextFunction } from 'express';

/**
* Middleware to enrich the request with audit-related metadata.
*/
export const auditMiddleware = (req: Request, _res: Response, next: NextFunction) => {
// 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;
};
}
}
}
2 changes: 2 additions & 0 deletions server/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions server/src/services/audit.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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 || {},
},
});
}
Expand Down
Loading