System & Audit Logs
Enterprise applications need a reliable way to trace who did what, and when. DBS has a built-in audit logging system powered by the auditLogger utility and the AuditLog table in PostgreSQL.
Architecture
API Route Handler
βββ auditLogger.log({ userId, action, details })
βββ prisma.auditLog.create(...)
βββ AuditLog table (PostgreSQL)
βββ GET /api/logs
βββ /dashboard/logs (UI)The auditLogger Utility
// src/lib/audit-logger.ts
import { auditLogger } from '@/lib/audit-logger';
interface AuditLogEntry {
userId: string; // Who performed the action
action: string; // What they did (e.g., 'user.update')
targetId?: string; // ID of the affected resource
targetType?: string; // Type of the affected resource (e.g., 'User')
details?: Record<string, unknown>; // Additional structured metadata
ipAddress?: string; // Client IP address
}Writing an Audit Log Entry
Call auditLogger.log() inside any API route after a critical state change:
User Update
// src/app/api/users/[id]/route.ts
import { auditLogger } from '@/lib/audit-logger';
import { apiGuard } from '@/lib/api-guard';
export async function PUT(req: Request, { params }) {
const guard = await apiGuard('user.update');
if (guard.error) return guard.error;
const { session } = guard;
const body = await req.json();
const updatedUser = await prisma.user.update({
where: { id: params.id },
data: body,
});
await auditLogger.log({
userId: session.user.id,
action: 'user.update',
targetId: params.id,
targetType: 'User',
details: { fields: Object.keys(body) },
ipAddress: req.headers.get('x-forwarded-for') ?? undefined,
});
return Response.json(updatedUser);
}Audit Log API
| Method | Endpoint | Permission | Description |
|---|---|---|---|
GET | /api/logs | log.read | Fetch paginated audit log entries |
GET | /api/logs?userId=... | log.read | Filter by user |
GET | /api/logs?action=... | log.read | Filter by action type |
GET | /api/logs?from=...&to=... | log.read | Filter by date range |
The Audit Log Viewer
The Logs page at /dashboard/logs is accessible only to users with the log.read permission. It provides:
- A paginated table showing all audit entries
- Search by user, action, or target
- Date range filter
- Click an entry to expand the
detailsJSON payload
Standard Action Naming Convention
To keep logs consistent and queryable, follow this naming pattern:
resource.action| Action | Description |
|---|---|
user.create | A new user was registered |
user.update | User profile was updated |
user.delete | User was deleted |
user.role.assign | Role assigned to a user |
auth.login | Successful login |
auth.logout | User logged out |
auth.2fa.enabled | 2FA was enabled |
team.create | New team created |
team.member.add | Member added to team |
settings.update | System setting changed |
Log entries are append-only by design. There are no delete or update operations on the AuditLog table, ensuring an immutable audit trail.
Last updated on