/** * AuditLog model for Stack.Gallery Registry */ import * as plugins from '../plugins.ts'; import type { IAuditLog, TAuditAction, TAuditResourceType } from '../interfaces/audit.interfaces.ts'; import { db } from './db.ts'; @plugins.smartdata.Collection(() => db) export class AuditLog extends plugins.smartdata.SmartDataDbDoc implements IAuditLog { @plugins.smartdata.unI() public id: string = ''; @plugins.smartdata.svDb() @plugins.smartdata.index() public actorId?: string; @plugins.smartdata.svDb() @plugins.smartdata.index() public actorType: 'user' | 'api_token' | 'system' | 'anonymous' = 'anonymous'; @plugins.smartdata.svDb() public actorTokenId?: string; @plugins.smartdata.svDb() public actorIp?: string; @plugins.smartdata.svDb() public actorUserAgent?: string; @plugins.smartdata.svDb() @plugins.smartdata.index() public action: TAuditAction = 'USER_CREATED'; @plugins.smartdata.svDb() @plugins.smartdata.index() public resourceType: TAuditResourceType = 'user'; @plugins.smartdata.svDb() @plugins.smartdata.index() public resourceId?: string; @plugins.smartdata.svDb() public resourceName?: string; @plugins.smartdata.svDb() @plugins.smartdata.index() public organizationId?: string; @plugins.smartdata.svDb() @plugins.smartdata.index() public repositoryId?: string; @plugins.smartdata.svDb() public metadata: Record = {}; @plugins.smartdata.svDb() @plugins.smartdata.index() public success: boolean = true; @plugins.smartdata.svDb() public errorCode?: string; @plugins.smartdata.svDb() public errorMessage?: string; @plugins.smartdata.svDb() public durationMs?: number; @plugins.smartdata.svDb() @plugins.smartdata.index() public timestamp: Date = new Date(); /** * Create an audit log entry */ public static async log(data: { actorId?: string; actorType?: 'user' | 'api_token' | 'system' | 'anonymous'; actorTokenId?: string; actorIp?: string; actorUserAgent?: string; action: TAuditAction; resourceType: TAuditResourceType; resourceId?: string; resourceName?: string; organizationId?: string; repositoryId?: string; metadata?: Record; success?: boolean; errorCode?: string; errorMessage?: string; durationMs?: number; }): Promise { const log = new AuditLog(); log.id = await AuditLog.getNewId(); log.actorId = data.actorId; log.actorType = data.actorType || (data.actorId ? 'user' : 'anonymous'); log.actorTokenId = data.actorTokenId; log.actorIp = data.actorIp; log.actorUserAgent = data.actorUserAgent; log.action = data.action; log.resourceType = data.resourceType; log.resourceId = data.resourceId; log.resourceName = data.resourceName; log.organizationId = data.organizationId; log.repositoryId = data.repositoryId; log.metadata = data.metadata || {}; log.success = data.success ?? true; log.errorCode = data.errorCode; log.errorMessage = data.errorMessage; log.durationMs = data.durationMs; log.timestamp = new Date(); await log.save(); return log; } /** * Query audit logs with filters */ public static async query(filters: { actorId?: string; organizationId?: string; repositoryId?: string; resourceType?: TAuditResourceType; action?: TAuditAction[]; success?: boolean; startDate?: Date; endDate?: Date; offset?: number; limit?: number; }): Promise<{ logs: AuditLog[]; total: number }> { const query: Record = {}; if (filters.actorId) query.actorId = filters.actorId; if (filters.organizationId) query.organizationId = filters.organizationId; if (filters.repositoryId) query.repositoryId = filters.repositoryId; if (filters.resourceType) query.resourceType = filters.resourceType; if (filters.action) query.action = { $in: filters.action }; if (filters.success !== undefined) query.success = filters.success; if (filters.startDate || filters.endDate) { query.timestamp = {}; if (filters.startDate) (query.timestamp as Record).$gte = filters.startDate; if (filters.endDate) (query.timestamp as Record).$lte = filters.endDate; } // Get total count const allLogs = await AuditLog.getInstances(query); const total = allLogs.length; // Apply pagination const offset = filters.offset || 0; const limit = filters.limit || 100; const logs = allLogs.slice(offset, offset + limit); return { logs, total }; } /** * Lifecycle hook */ public async beforeSave(): Promise { if (!this.id) { this.id = await AuditLog.getNewId(); } } }