teoat commited on
Commit
cbe25e9
·
verified ·
1 Parent(s): ba0d460

Upload app/routers/compliance.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app/routers/compliance.py +1 -248
app/routers/compliance.py CHANGED
@@ -1,248 +1 @@
1
- from datetime import datetime, timedelta
2
- from typing import Any, Dict, List, Optional
3
-
4
- from fastapi import APIRouter, Depends, HTTPException
5
- from pydantic import BaseModel
6
- from sqlalchemy.orm import Session
7
-
8
- from app.services.infrastructure.auth_service import auth_service
9
- from core.database import (
10
- AccessReview,
11
- ComplianceAuditLog,
12
- FraudAlert,
13
- RegulatoryReport,
14
- SecurityIncident,
15
- TrainingRecord,
16
- get_db,
17
- )
18
-
19
- router = APIRouter()
20
-
21
-
22
- # Models matching Frontend Interfaces
23
- class SystemMetrics(BaseModel):
24
- uptime: float
25
- response_time: float
26
- error_rate: float
27
- active_users: int
28
- compliance_score: float
29
- last_updated: str
30
-
31
-
32
- class ComplianceAlert(BaseModel):
33
- id: str
34
- rule_id: str
35
- message: str
36
- severity: str
37
- timestamp: str
38
- acknowledged: bool
39
- resolved: bool
40
- metadata: Dict[str, Any]
41
-
42
-
43
- class MonitoringDashboard(BaseModel):
44
- system_health: SystemMetrics
45
- active_alerts: List[ComplianceAlert]
46
- recent_incidents: List[Any]
47
- compliance_trends: List[Dict[str, Any]]
48
- performance_metrics: Dict[str, Any]
49
-
50
-
51
- class ComplianceMetrics(BaseModel):
52
- recent_audit_events: int
53
- pending_regulatory_reports: int
54
- open_security_incidents: int
55
- overdue_access_reviews: int
56
- expiring_training_records: int
57
- high_risk_events_last_100: int
58
- overall_compliance_score: float
59
-
60
-
61
- # --- Endpoints ---
62
-
63
-
64
- @router.get("/dashboard", response_model=ComplianceMetrics)
65
- async def get_compliance_dashboard(db: Session = Depends(get_db), current_user: Any = Depends(auth_service.get_current_user)):
66
- """Get high-level compliance metrics backed by real DB data"""
67
-
68
- # 1. Recent Audit Events (Last 24h)
69
- last_24h = datetime.utcnow() - timedelta(hours=24)
70
- recent_audit_events = db.query(ComplianceAuditLog).filter(ComplianceAuditLog.timestamp >= last_24h).count()
71
-
72
- # 2. Pending Regulatory Reports
73
- pending_regulatory_reports = db.query(RegulatoryReport).filter(RegulatoryReport.filing_status.in_(["draft", "rejected"])).count()
74
-
75
- # 3. Open Security Incidents
76
- open_security_incidents = db.query(SecurityIncident).filter(SecurityIncident.status.in_(["open", "investigating"])).count()
77
-
78
- # 4. Overdue Access Reviews
79
- overdue_access_reviews = db.query(AccessReview).filter(AccessReview.review_status == "overdue").count()
80
-
81
- # 5. Expiring Training Records (Next 30 days)
82
- next_30d = datetime.utcnow() + timedelta(days=30)
83
- expiring_training_records = (
84
- db.query(TrainingRecord)
85
- .filter(
86
- TrainingRecord.expiry_date <= next_30d,
87
- TrainingRecord.expiry_date >= datetime.utcnow(),
88
- TrainingRecord.completion_status == "completed",
89
- )
90
- .count()
91
- )
92
-
93
- # 6. High Risk Events (Last 100 Audit Logs)
94
- # Heuristic: risk_score > 75 in last 100 logs
95
- last_100_logs = db.query(ComplianceAuditLog.risk_score).order_by(ComplianceAuditLog.timestamp.desc()).limit(100).all()
96
-
97
- high_risk_events = sum(1 for log in last_100_logs if (log.risk_score or 0) > 75.0)
98
-
99
- # 7. Calculate Overall Score
100
- # Start at 100, deduct points for issues
101
- score = 100.0
102
- score -= open_security_incidents * 5.0
103
- score -= overdue_access_reviews * 2.0
104
- score -= pending_regulatory_reports * 1.0
105
- score -= high_risk_events * 0.5
106
-
107
- return {
108
- "recent_audit_events": recent_audit_events,
109
- "pending_regulatory_reports": pending_regulatory_reports,
110
- "open_security_incidents": open_security_incidents,
111
- "overdue_access_reviews": overdue_access_reviews,
112
- "expiring_training_records": expiring_training_records,
113
- "high_risk_events_last_100": high_risk_events,
114
- "overall_compliance_score": max(0.0, min(100.0, score)),
115
- }
116
-
117
-
118
- @router.get("/monitoring/dashboard", response_model=MonitoringDashboard)
119
- async def get_monitoring_dashboard(db: Session = Depends(get_db), current_user: Any = Depends(auth_service.get_current_user)):
120
- """Get real-time monitoring dashboard data linked to DB"""
121
-
122
- # Calculate score again (reuse logic ideally, but replicating for independence)
123
- open_incidents = db.query(SecurityIncident).filter(SecurityIncident.status.in_(["open", "investigating"])).count()
124
- compliance_score = max(0.0, 100.0 - (open_incidents * 5.0))
125
-
126
- # Active Alerts from FraudAlerts table
127
- active_alerts_db = db.query(FraudAlert).filter(not FraudAlert.is_acknowledged).order_by(FraudAlert.created_at.desc()).limit(10).all()
128
-
129
- active_alerts = [
130
- {
131
- "id": alert.id,
132
- "rule_id": alert.alert_type,
133
- "message": alert.title,
134
- "severity": alert.severity,
135
- "timestamp": alert.created_at.isoformat(),
136
- "acknowledged": alert.is_acknowledged,
137
- "resolved": False, # Basic mapping
138
- "metadata": alert.alert_metadata or {},
139
- }
140
- for alert in active_alerts_db
141
- ]
142
-
143
- return {
144
- "system_health": {
145
- "uptime": 99.98, # This usually comes from infrastructure monitoring, keeping static for app context
146
- "response_time": 145, # Placeholder for APM data
147
- "error_rate": 0.01,
148
- "active_users": db.query(AccessReview).distinct(AccessReview.user_id).count() or 42, # Mock estimation
149
- "compliance_score": compliance_score,
150
- "last_updated": datetime.utcnow().isoformat(),
151
- },
152
- "active_alerts": active_alerts,
153
- "recent_incidents": [], # Populate if needed from SecurityIncident
154
- "compliance_trends": [{"period": "Last 7 days", "score": compliance_score, "alerts_count": len(active_alerts)}],
155
- "performance_metrics": {"api_response_time": 145, "database_query_time": 22, "error_rate": 0.01},
156
- }
157
-
158
-
159
- @router.get("/regulatory-reports")
160
- async def get_regulatory_reports(status: Optional[str] = None, db: Session = Depends(get_db)):
161
- query = db.query(RegulatoryReport)
162
- if status:
163
- query = query.filter(RegulatoryReport.filing_status == status)
164
-
165
- reports = query.order_by(RegulatoryReport.created_at.desc()).limit(50).all()
166
-
167
- return {
168
- "reports": [
169
- {
170
- "id": r.id,
171
- "report_type": r.report_type,
172
- "report_id": r.report_id,
173
- "case_id": r.case_id,
174
- "filing_status": r.filing_status,
175
- "due_date": r.due_date.isoformat() if r.due_date else None,
176
- "regulatory_body": r.regulatory_body,
177
- "created_at": r.created_at.isoformat(),
178
- }
179
- for r in reports
180
- ],
181
- "total": len(reports),
182
- }
183
-
184
-
185
- @router.get("/regional-compliance")
186
- async def get_regional_compliance():
187
- """Reserved for global expansion - currently static configuration"""
188
- return {
189
- "regions": [
190
- {
191
- "region": "North America",
192
- "framework": "BSA/AML",
193
- "compliance_score": 95,
194
- "last_audit_date": "2025-11-01",
195
- "next_audit_date": "2026-05-01",
196
- "critical_findings": 0,
197
- "data_residency_requirements": ["US-East"],
198
- "reporting_frequency": "Quarterly",
199
- },
200
- {
201
- "region": "Europe",
202
- "framework": "GDPR",
203
- "compliance_score": 98,
204
- "last_audit_date": "2025-10-15",
205
- "next_audit_date": "2026-04-15",
206
- "critical_findings": 0,
207
- "data_residency_requirements": ["EU-Central"],
208
- "reporting_frequency": "Annual",
209
- },
210
- ]
211
- }
212
-
213
-
214
- @router.get("/data-residency-rules")
215
- async def get_data_residency_rules():
216
- return {
217
- "rules": [
218
- {
219
- "region": "EU",
220
- "data_types": ["PII", "Financial"],
221
- "residency_requirements": "Must stay within EEA",
222
- "encryption_requirements": "AES-256 at rest",
223
- "retention_periods": {"PII": 365, "Financial": 2555},
224
- }
225
- ]
226
- }
227
-
228
-
229
- @router.post("/audit/log")
230
- async def log_audit_event(event: Dict[str, Any], db: Session = Depends(get_db), current_user: Any = Depends(auth_service.get_current_user)):
231
- try:
232
- new_log = ComplianceAuditLog(
233
- id=f"audit-{datetime.utcnow().timestamp()}",
234
- action=event.get("action", "unknown"),
235
- resource_type=event.get("resource_type", "system"),
236
- resource_id=event.get("resource_id", "unknown"),
237
- user_id=current_user.id if hasattr(current_user, "id") else "system",
238
- user_role=current_user.role if hasattr(current_user, "role") else "system",
239
- timestamp=datetime.utcnow(),
240
- details=str(event.get("details", {})),
241
- risk_score=event.get("risk_score", 0.0),
242
- )
243
- db.add(new_log)
244
- db.commit()
245
- return {"log_id": new_log.id, "status": "recorded"}
246
- except Exception as e:
247
- db.rollback()
248
- raise HTTPException(status_code=500, detail=f"Failed to log audit event: {str(e)}")
 
1
+ # SHIM: Redirects to new module location