mirror of
https://github.com/domfelipe/fraudshield.git
synced 2026-08-07 08:16:53 +00:00
feat: implement FraudShield MVP — backend API + frontend alerts
Backend (Python 3.12/FastAPI): - 5 models: Transaction, DetectionRule, FraudAlert, AuditLog, RuleSnapshot - Rule engine: 4 rule types (amount, location, time, pattern) - 11 API routes: health, auth, transactions, alerts - Sync ingestion pipeline with explanation generation - JWT auth with 3 roles (analyst, admin, senior) - Seed script with 5 default rules + 50 synthetic transactions Frontend (React 19/TypeScript/Tailwind): - Auth: login page, JWT token management, role-based routing - Alerts: queue with filters, detail panel, decision workflow - Layout: responsive sidebar, top bar, protected routes - Full TypeScript, zero ts-ignore, Tailwind CSS Build: Backend imports clean, Frontend tsc --noEmit passes, vite build produces 296KB production bundle.
This commit is contained in:
parent
14f2616037
commit
989202648d
60 changed files with 6943 additions and 0 deletions
113
frontend/src/api/client.ts
Normal file
113
frontend/src/api/client.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
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 {
|
||||
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 };
|
||||
}
|
||||
|
||||
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 };
|
||||
Loading…
Add table
Add a link
Reference in a new issue