mirror of
https://github.com/domfelipe/fraudshield.git
synced 2026-08-07 05:56:51 +00:00
US3 - Rule Management: - Backend: CRUD API + activate/deactivate + test against 90d history - Frontend: RuleList sidebar, RuleForm (create/edit), RuleTestResults - Weight/threshold sliders, condition type selector, audit logging US4 - Dashboard: - Backend: GET /dashboard/metrics (24h/7d/30d/90d), CSV export - Frontend: MetricCards, alerts-by-hour bar chart, status pie chart, top rules horizontal bar chart via Recharts Stack: 20 API routes total. Frontend tsc clean, vite build passes.
131 lines
3.4 KiB
TypeScript
131 lines
3.4 KiB
TypeScript
const API_BASE = '/api/v1';
|
|
|
|
function getAuthHeaders(): Record<string, string> {
|
|
const token = localStorage.getItem('fraudshield_token');
|
|
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
if (token) headers['Authorization'] = `Bearer ${token}`;
|
|
return headers;
|
|
}
|
|
|
|
async function apiFetch<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|
const res = await fetch(`${API_BASE}${path}`, {
|
|
...options,
|
|
headers: { ...getAuthHeaders(), ...((options.headers as Record<string, string>) || {}) },
|
|
});
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({ detail: res.statusText }));
|
|
throw new Error(err.detail || `HTTP ${res.status}`);
|
|
}
|
|
return res.json();
|
|
}
|
|
|
|
export interface AlertFilters {
|
|
status?: string;
|
|
min_score?: number;
|
|
from_date?: string;
|
|
to_date?: string;
|
|
triggered_rule?: string;
|
|
limit?: number;
|
|
offset?: number;
|
|
}
|
|
|
|
export interface TriggeredRule {
|
|
rule_id: string;
|
|
rule_name: string;
|
|
score: number;
|
|
explanation: string;
|
|
}
|
|
|
|
export interface AlertSummary {
|
|
id: string;
|
|
score: number;
|
|
status: string;
|
|
amount: number;
|
|
merchant_name: string;
|
|
top_rule: string;
|
|
created_at: string;
|
|
}
|
|
|
|
export interface AlertDetail {
|
|
id: string;
|
|
transaction_id: string;
|
|
score: number;
|
|
triggered_rules: TriggeredRule[];
|
|
explanation: string;
|
|
status: string;
|
|
assigned_to?: string | null;
|
|
decision?: string | null;
|
|
decided_by?: string | null;
|
|
decided_at?: string | null;
|
|
justification?: string | null;
|
|
created_at: string;
|
|
amount: number;
|
|
merchant_name: string;
|
|
merchant_category: string;
|
|
channel: string;
|
|
}
|
|
|
|
export interface AlertListResponse {
|
|
items: AlertSummary[];
|
|
total: number;
|
|
limit: number;
|
|
offset: number;
|
|
}
|
|
|
|
export interface AlertDecisionRequest {
|
|
decision: string;
|
|
justification?: string;
|
|
notes?: string;
|
|
}
|
|
|
|
export interface TransactionAccepted {
|
|
transaction_id: string;
|
|
status: string;
|
|
trace_id: string;
|
|
}
|
|
|
|
export interface DashboardMetrics {
|
|
period: string;
|
|
total_transactions: number;
|
|
total_alerts: number;
|
|
fraud_rate_pct: number;
|
|
false_positive_rate_pct: number;
|
|
alerts_by_status: { pending: number; confirmed: number; false_positive: number; escalated: number };
|
|
alerts_by_hour: { hour: number; count: number }[];
|
|
top_triggering_rules: { rule_name: string; alert_count: number }[];
|
|
score_distribution: { low: number; medium: number; high: number; critical: number };
|
|
}
|
|
|
|
export interface DetectionRule {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
condition: string;
|
|
weight: number;
|
|
threshold: number;
|
|
is_active: boolean;
|
|
version: number;
|
|
created_by: string;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export function listAlerts(params: AlertFilters = {}): Promise<AlertListResponse> {
|
|
const qs = new URLSearchParams();
|
|
Object.entries(params).forEach(([k, v]) => { if (v !== undefined) qs.set(k, String(v)); });
|
|
return apiFetch<AlertListResponse>(`/alerts?${qs.toString()}`);
|
|
}
|
|
|
|
export function getAlert(id: string): Promise<AlertDetail> {
|
|
return apiFetch<AlertDetail>(`/alerts/${id}`);
|
|
}
|
|
|
|
export function decideAlert(id: string, data: AlertDecisionRequest): Promise<AlertDetail> {
|
|
return apiFetch<AlertDetail>(`/alerts/${id}/decide`, { method: 'POST', body: JSON.stringify(data) });
|
|
}
|
|
|
|
export function getDashboardMetrics(period = '24h'): Promise<DashboardMetrics> {
|
|
return apiFetch<DashboardMetrics>(`/dashboard/metrics?period=${period}`);
|
|
}
|
|
|
|
export { getAuthHeaders };
|