feat(phase-9): implement MVP polishing and closure

T-032: User management admin panel - backend users router with CRUD, frontend UsersPage with modals

T-033: Audit log viewer - backend audit router with filters/pagination, frontend AuditLogPage

T-034: Global error handling - ErrorBoundary, LoadingSpinner, ErrorMessage, Toast components

T-035: Backend tests - pytest setup with SQLite, tests for health/auth/techniques/tests

T-036: Documentation - Updated README with testing section, created docs/API.md
This commit is contained in:
2026-02-06 16:30:35 +01:00
parent cb447f3803
commit 174919da4e
27 changed files with 2539 additions and 17 deletions
+58
View File
@@ -0,0 +1,58 @@
import client from "./client";
export interface AuditLogOut {
id: string;
user_id: string | null;
username: string | null;
action: string;
entity_type: string | null;
entity_id: string | null;
timestamp: string;
details: Record<string, unknown> | null;
}
export interface AuditLogPage {
items: AuditLogOut[];
total: number;
offset: number;
limit: number;
}
export interface AuditLogFilters {
user_id?: string;
action?: string;
entity_type?: string;
start_date?: string;
end_date?: string;
offset?: number;
limit?: number;
}
/** Fetch paginated audit logs with filters. */
export async function getAuditLogs(filters?: AuditLogFilters): Promise<AuditLogPage> {
const params = new URLSearchParams();
if (filters?.user_id) params.append("user_id", filters.user_id);
if (filters?.action) params.append("action", filters.action);
if (filters?.entity_type) params.append("entity_type", filters.entity_type);
if (filters?.start_date) params.append("start_date", filters.start_date);
if (filters?.end_date) params.append("end_date", filters.end_date);
if (filters?.offset !== undefined) params.append("offset", String(filters.offset));
if (filters?.limit !== undefined) params.append("limit", String(filters.limit));
const { data } = await client.get<AuditLogPage>(
`/audit-logs${params.toString() ? `?${params}` : ""}`
);
return data;
}
/** Fetch list of distinct actions. */
export async function getAuditActions(): Promise<string[]> {
const { data } = await client.get<string[]>("/audit-logs/actions");
return data;
}
/** Fetch list of distinct entity types. */
export async function getAuditEntityTypes(): Promise<string[]> {
const { data } = await client.get<string[]>("/audit-logs/entity-types");
return data;
}