/** * Audit API handlers */ import type { IApiContext, IApiResponse } from '../router.ts'; import { PermissionService } from '../../services/permission.service.ts'; import { AuditLog } from '../../models/auditlog.ts'; import type { TAuditAction, TAuditResourceType } from '../../interfaces/audit.interfaces.ts'; export class AuditApi { private permissionService: PermissionService; constructor(permissionService: PermissionService) { this.permissionService = permissionService; } /** * GET /api/v1/audit */ public async query(ctx: IApiContext): Promise { if (!ctx.actor?.userId) { return { status: 401, body: { error: 'Authentication required' } }; } try { // Parse query parameters const organizationId = ctx.url.searchParams.get('organizationId') || undefined; const repositoryId = ctx.url.searchParams.get('repositoryId') || undefined; const resourceType = ctx.url.searchParams.get('resourceType') as TAuditResourceType | undefined; const actionsParam = ctx.url.searchParams.get('actions'); const actions = actionsParam ? (actionsParam.split(',') as TAuditAction[]) : undefined; const success = ctx.url.searchParams.has('success') ? ctx.url.searchParams.get('success') === 'true' : undefined; const startDateParam = ctx.url.searchParams.get('startDate'); const endDateParam = ctx.url.searchParams.get('endDate'); const startDate = startDateParam ? new Date(startDateParam) : undefined; const endDate = endDateParam ? new Date(endDateParam) : undefined; const limit = parseInt(ctx.url.searchParams.get('limit') || '100', 10); const offset = parseInt(ctx.url.searchParams.get('offset') || '0', 10); // Check permissions // Users can view audit logs for: // 1. Their own actions (actorId = userId) // 2. Organizations they manage // 3. System admins can view all let actorId: string | undefined; if (ctx.actor.user?.isSystemAdmin) { // System admins can see all actorId = ctx.url.searchParams.get('actorId') || undefined; } else if (organizationId) { // Check if user can manage this org const canManage = await this.permissionService.canManageOrganization( ctx.actor.userId, organizationId ); if (!canManage) { // User can only see their own actions in this org actorId = ctx.actor.userId; } } else { // Non-admins without org filter can only see their own actions actorId = ctx.actor.userId; } const result = await AuditLog.query({ actorId, organizationId, repositoryId, resourceType, action: actions, success, startDate, endDate, limit, offset, }); return { status: 200, body: { logs: result.logs.map((log) => ({ id: log.id, actorId: log.actorId, actorType: log.actorType, action: log.action, resourceType: log.resourceType, resourceId: log.resourceId, resourceName: log.resourceName, organizationId: log.organizationId, repositoryId: log.repositoryId, success: log.success, errorCode: log.errorCode, timestamp: log.timestamp, metadata: log.metadata, })), total: result.total, limit, offset, }, }; } catch (error) { console.error('[AuditApi] Query error:', error); return { status: 500, body: { error: 'Failed to query audit logs' } }; } } }