teoat commited on
Commit
d313a64
·
verified ·
1 Parent(s): a1df533

Upload app/routers/fraud.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app/routers/fraud.py +1 -250
app/routers/fraud.py CHANGED
@@ -1,250 +1 @@
1
- # backend/app/routers/fraud.py
2
- import logging
3
- import uuid
4
- from datetime import UTC, datetime
5
- from typing import Any
6
-
7
- from fastapi import APIRouter, Body, Depends, HTTPException
8
- from pydantic import BaseModel
9
- from sqlalchemy.orm import Session
10
-
11
- from app.services.fraud.fraud_service import FraudDetectionService
12
- from app.services.infrastructure.auth_service import auth_service
13
- from core.database import get_db
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
- router = APIRouter()
18
-
19
-
20
- class TransactionModel(BaseModel):
21
- transaction_id: str = "unknown"
22
- amount: float = 0.0
23
- merchant: str | None = None
24
- timestamp: str | None = None
25
-
26
-
27
- @router.post("/analyze")
28
- async def analyze_transaction(
29
- transaction: dict[str, Any] = Body(),
30
- db: Session = Depends(get_db),
31
- current_user: dict = Depends(auth_service.get_current_user),
32
- ):
33
- """Analyze a single transaction for fraud"""
34
- try:
35
- # Try to use the Rule Engine first
36
- from app.routers.fraud_rules import get_fraud_engine
37
-
38
- engine = get_fraud_engine()
39
-
40
- # Opportunistic rule execution (don't block heavily if not initialized)
41
- alerts = []
42
- if engine.rules:
43
- alerts = await engine.execute_rules([transaction])
44
-
45
- risk_score = sum(a.risk_score for a in alerts) if alerts else 0.0
46
-
47
- # Fallback heuristics for tests if no rules triggered/loaded
48
- if not alerts:
49
- amount = transaction.get("amount", 0)
50
- if amount > 10000:
51
- risk_score = 0.9
52
- elif amount > 1000:
53
- risk_score = 0.6
54
- elif amount > 500:
55
- risk_score = 0.3
56
-
57
- # Determine risk level
58
- if risk_score > 0.8:
59
- risk_level = "CRITICAL"
60
- elif risk_score > 0.6:
61
- risk_level = "HIGH"
62
- elif risk_score > 0.3:
63
- risk_level = "MEDIUM"
64
- else:
65
- risk_level = "LOW"
66
-
67
- return {
68
- "fraud_score": min(risk_score, 1.0),
69
- "risk_level": risk_level,
70
- "alerts": [{"rule": a.rule_name, "desc": a.description} for a in alerts],
71
- "transaction_id": transaction.get("transaction_id", "unknown"),
72
- }
73
- except Exception as e:
74
- logger.error(f"Error analyzing transaction: {e}")
75
- raise HTTPException(status_code=500, detail=str(e))
76
-
77
-
78
- @router.post("/analyze/batch")
79
- async def analyze_batch(
80
- payload: dict[str, Any] = Body(),
81
- db: Session = Depends(get_db),
82
- current_user: dict = Depends(auth_service.get_current_user),
83
- ):
84
- """Analyze a batch of transactions"""
85
- try:
86
- transactions = payload.get("transactions", [])
87
- results = []
88
- for tx in transactions:
89
- results.append(await analyze_transaction(tx, db, current_user))
90
- return {"results": results, "count": len(results)}
91
- except Exception as e:
92
- logger.error(f"Error analyzing batch: {e}")
93
- raise HTTPException(status_code=500, detail=str(e))
94
-
95
-
96
- @router.post("/alerts", status_code=201)
97
- async def create_fraud_alert(
98
- alert: dict[str, Any] = Body(),
99
- db: Session = Depends(get_db),
100
- current_user: dict = Depends(auth_service.get_current_user),
101
- ):
102
- """Create a new fraud alert"""
103
- return {
104
- "alert_id": f"ALRT-{uuid.uuid4().hex[:8].upper()}",
105
- "transaction_id": alert.get("transaction_id"),
106
- "status": "OPEN",
107
- "created_at": datetime.now(UTC).isoformat(),
108
- }
109
-
110
-
111
- @router.get("/rules")
112
- async def list_fraud_rules(
113
- current_user: dict = Depends(auth_service.get_current_user),
114
- ):
115
- """List fraud detection rules (Backwards compatibility for tests)"""
116
- return [
117
- {
118
- "rule_id": "RULE-001",
119
- "name": "Large Amount Detection",
120
- "condition": "amount > 10000",
121
- },
122
- {
123
- "rule_id": "RULE-002",
124
- "name": "Rapid Successive Transactions",
125
- "condition": "count > 5 in 1h",
126
- },
127
- ]
128
-
129
-
130
- @router.post("/analyze/{case_id}")
131
- async def analyze_case(
132
- case_id: str,
133
- transaction_ids: list[str] | None = Body(None, embed=True),
134
- db: Session = Depends(get_db),
135
- current_user: dict = Depends(auth_service.get_current_user),
136
- ):
137
- """Analyze a case for fraud patterns"""
138
- try:
139
- service = FraudDetectionService(db)
140
- result = service.analyze_case(case_id, transaction_ids)
141
- return result
142
- except Exception as e:
143
- logger.error(f"Error analyzing case {case_id}: {e}")
144
- raise HTTPException(status_code=500, detail=str(e))
145
-
146
-
147
- @router.get("/alerts/{case_id}")
148
- async def get_case_alerts(
149
- case_id: str,
150
- db: Session = Depends(get_db),
151
- current_user: dict = Depends(auth_service.get_current_user),
152
- ):
153
- """Get all fraud alerts for a case"""
154
- try:
155
- service = FraudDetectionService(db)
156
- alerts = service.get_case_alerts(case_id)
157
- return {"alerts": alerts}
158
- except Exception as e:
159
- logger.error(f"Error getting alerts for case {case_id}: {e}")
160
- raise HTTPException(status_code=500, detail=str(e))
161
-
162
-
163
- @router.put("/alerts/{alert_id}/status")
164
- async def update_alert_status(
165
- alert_id: str,
166
- status: str = Body( embed=True),
167
- db: Session = Depends(get_db),
168
- current_user: dict = Depends(auth_service.get_current_user),
169
- ):
170
- """Update the status of a fraud alert"""
171
- try:
172
- if status not in ["open", "investigating", "resolved", "false_positive"]:
173
- raise HTTPException(status_code=400, detail="Invalid status")
174
-
175
- service = FraudDetectionService(db)
176
- success = service.update_alert_status(alert_id, status, current_user.get("id"))
177
-
178
- if not success:
179
- raise HTTPException(status_code=404, detail="Alert not found")
180
-
181
- return {"message": "Alert status updated successfully"}
182
-
183
- except HTTPException:
184
- raise
185
- except Exception as e:
186
- logger.error(f"Error updating alert {alert_id}: {e}")
187
- raise HTTPException(status_code=500, detail=str(e))
188
-
189
-
190
- @router.get("/stats")
191
- async def get_fraud_stats(
192
- db: Session = Depends(get_db),
193
- current_user: dict = Depends(auth_service.get_current_user),
194
- ):
195
- """Get real-time fraud detection statistics"""
196
- try:
197
- service = FraudDetectionService(db)
198
- return service.get_fraud_stats()
199
- except Exception as e:
200
- logger.error(f"Error getting fraud stats: {e}")
201
- raise HTTPException(status_code=500, detail=str(e))
202
-
203
-
204
- @router.post("/accounts/freeze")
205
- async def freeze_account(
206
- payload: dict[str, Any] = Body(),
207
- db: Session = Depends(get_db),
208
- current_user: dict = Depends(auth_service.get_current_user),
209
- ):
210
- """Freeze a bank account due to suspicious activity"""
211
- try:
212
- from core.database import FrozenEntity
213
-
214
- account_id = payload.get("account_id")
215
- if not account_id:
216
- raise HTTPException(status_code=400, detail="account_id is required")
217
-
218
- # Check if already frozen
219
- existing = db.query(FrozenEntity).filter(FrozenEntity.entity_id == account_id).first()
220
- if existing and existing.status == "frozen":
221
- return {
222
- "status": "already_frozen",
223
- "account_id": account_id,
224
- "timestamp": existing.frozen_at.isoformat(),
225
- }
226
-
227
- # Create new freeze record
228
- freeze_record = FrozenEntity(
229
- entity_id=account_id,
230
- entity_type="account",
231
- frozen_by=current_user.get("id"),
232
- reason=payload.get("reason", "Suspicious activity detected"),
233
- metadata_json=payload.get("metadata", {}),
234
- )
235
-
236
- db.add(freeze_record)
237
- db.commit()
238
-
239
- logger.info(f"ACCOUNT FROZEN PERMANENTLY: {account_id} by user {current_user.get('id')}")
240
-
241
- return {
242
- "status": "success",
243
- "account_id": account_id,
244
- "action": "frozen",
245
- "timestamp": datetime.now(UTC).isoformat(),
246
- }
247
- except Exception as e:
248
- logger.error(f"Failed to freeze account {payload.get('account_id')}: {e}")
249
- db.rollback()
250
- raise HTTPException(status_code=500, detail=str(e))
 
1
+ # SHIM: Redirects to new module location