- Added SettingsComponent for user profile management, including display name and password change functionality. - Introduced TokensComponent for managing API tokens, including creation and revocation. - Created LayoutComponent for consistent application layout with navigation and user information. - Established main application structure in index.html and main.ts. - Integrated Tailwind CSS for styling and responsive design. - Configured TypeScript settings for strict type checking and module resolution.
172 lines
4.7 KiB
TypeScript
172 lines
4.7 KiB
TypeScript
/**
|
|
* AuditLog model for Stack.Gallery Registry
|
|
*/
|
|
|
|
import * as plugins from '../plugins.ts';
|
|
import type { IAuditLog, TAuditAction, TAuditResourceType } from '../interfaces/audit.interfaces.ts';
|
|
import { getDb } from './db.ts';
|
|
|
|
@plugins.smartdata.Collection(() => getDb())
|
|
export class AuditLog
|
|
extends plugins.smartdata.SmartDataDbDoc<AuditLog, AuditLog>
|
|
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<string, unknown> = {};
|
|
|
|
@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<string, unknown>;
|
|
success?: boolean;
|
|
errorCode?: string;
|
|
errorMessage?: string;
|
|
durationMs?: number;
|
|
}): Promise<AuditLog> {
|
|
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<string, unknown> = {};
|
|
|
|
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<string, unknown>).$gte = filters.startDate;
|
|
if (filters.endDate) (query.timestamp as Record<string, unknown>).$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<void> {
|
|
if (!this.id) {
|
|
this.id = await AuditLog.getNewId();
|
|
}
|
|
}
|
|
}
|