102 lines
2.4 KiB
TypeScript
102 lines
2.4 KiB
TypeScript
import { db } from '@/lib/db';
|
|
import { activityLogs } from '@/lib/db/schema';
|
|
import { NextRequest } from 'next/server';
|
|
|
|
export interface ActivityLogData {
|
|
userId?: string | null;
|
|
action: string;
|
|
entityType: string;
|
|
entityId?: string;
|
|
details?: any;
|
|
request?: NextRequest;
|
|
}
|
|
|
|
export async function logActivity({ userId, action, entityType, entityId, details, request }: ActivityLogData) {
|
|
try {
|
|
// Extract IP and User Agent from request if provided
|
|
let ipAddress: string | null = null;
|
|
let userAgent: string | null = null;
|
|
|
|
if (request) {
|
|
// Try to get real IP address
|
|
ipAddress =
|
|
request.headers.get('x-forwarded-for')?.split(',')[0] ||
|
|
request.headers.get('x-real-ip') ||
|
|
request.headers.get('cf-connecting-ip') ||
|
|
'127.0.0.1';
|
|
|
|
userAgent = request.headers.get('user-agent');
|
|
}
|
|
|
|
await db.insert(activityLogs).values({
|
|
id: crypto.randomUUID(),
|
|
userId,
|
|
action,
|
|
entityType,
|
|
entityId,
|
|
details: details ? JSON.stringify(details) : null,
|
|
ipAddress,
|
|
userAgent,
|
|
createdAt: new Date(),
|
|
});
|
|
|
|
console.log(
|
|
`Activity logged: ${action} on ${entityType}${entityId ? ` (${entityId})` : ''} by user ${
|
|
userId || 'anonymous'
|
|
}`
|
|
);
|
|
} catch (error) {
|
|
console.error('Failed to log activity:', error);
|
|
// Don't throw error to avoid breaking the main request
|
|
}
|
|
}
|
|
|
|
// Predefined action types for consistency
|
|
export const ACTIONS = {
|
|
// User actions
|
|
USER_LOGIN: 'login',
|
|
USER_LOGOUT: 'logout',
|
|
USER_REGISTER: 'register',
|
|
USER_CREATE: 'create_user',
|
|
USER_UPDATE: 'update_user',
|
|
USER_DELETE: 'delete_user',
|
|
|
|
// Booking actions
|
|
BOOKING_CREATE: 'create_booking',
|
|
BOOKING_UPDATE: 'update_booking',
|
|
BOOKING_CANCEL: 'cancel_booking',
|
|
BOOKING_DELETE: 'delete_booking',
|
|
|
|
// Court actions
|
|
COURT_CREATE: 'create_court',
|
|
COURT_UPDATE: 'update_court',
|
|
COURT_DELETE: 'delete_court',
|
|
|
|
// Announcement actions
|
|
ANNOUNCEMENT_CREATE: 'create_announcement',
|
|
ANNOUNCEMENT_UPDATE: 'update_announcement',
|
|
ANNOUNCEMENT_DELETE: 'delete_announcement',
|
|
|
|
// Settings actions
|
|
SETTINGS_UPDATE: 'update_settings',
|
|
|
|
// Time slot actions
|
|
TIME_SLOT_CREATE: 'create_time_slot',
|
|
TIME_SLOT_UPDATE: 'update_time_slot',
|
|
TIME_SLOT_DELETE: 'delete_time_slot',
|
|
|
|
// System actions
|
|
SYSTEM_START: 'system_start',
|
|
SYSTEM_ERROR: 'system_error',
|
|
} as const;
|
|
|
|
export const ENTITY_TYPES = {
|
|
USER: 'user',
|
|
BOOKING: 'booking',
|
|
COURT: 'court',
|
|
ANNOUNCEMENT: 'announcement',
|
|
SETTINGS: 'settings',
|
|
TIME_SLOT: 'time_slot',
|
|
SYSTEM: 'system',
|
|
} as const;
|