Skip to content

Commit f28fadf

Browse files
authored
Merge pull request #42 from gapilongo/bug_fixesBEFE
refactoring
2 parents 9d753be + 81384ce commit f28fadf

28 files changed

Lines changed: 5218 additions & 1039 deletions

Backup/main.py

Lines changed: 833 additions & 0 deletions
Large diffs are not rendered by default.

src/lg_sotf/api/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""LG-SOTF API package."""
2+
3+
from .app import app, create_app
4+
5+
__all__ = ["app", "create_app"]

src/lg_sotf/api/app.py

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
"""FastAPI application factory for LG-SOTF API."""
2+
3+
import asyncio
4+
import logging
5+
from typing import Optional
6+
7+
from fastapi import FastAPI
8+
from fastapi.middleware.cors import CORSMiddleware
9+
10+
from lg_sotf.app_initializer import LG_SOTFApplication
11+
from lg_sotf.api.utils.websocket import WebSocketManager
12+
from lg_sotf.api.routers import (
13+
alerts,
14+
correlations,
15+
dashboard,
16+
escalations,
17+
ingestion,
18+
metrics,
19+
websocket,
20+
)
21+
22+
logger = logging.getLogger(__name__)
23+
24+
25+
def create_app(
26+
config_path: str = "configs/development.yaml",
27+
setup_signal_handlers: bool = False,
28+
) -> FastAPI:
29+
"""Create and configure the FastAPI application.
30+
31+
Args:
32+
config_path: Path to configuration file
33+
setup_signal_handlers: Whether to setup signal handlers (False for uvicorn)
34+
35+
Returns:
36+
Configured FastAPI application instance
37+
"""
38+
# Configure logging
39+
logging.basicConfig(
40+
level=logging.INFO,
41+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
42+
)
43+
44+
# Create FastAPI app
45+
app = FastAPI(
46+
title="LG-SOTF Dashboard API",
47+
description="Production-grade SOC Dashboard API",
48+
version="1.0.0",
49+
docs_url="/api/docs",
50+
redoc_url="/api/redoc"
51+
)
52+
53+
# Add CORS middleware
54+
app.add_middleware(
55+
CORSMiddleware,
56+
allow_origins=["*"],
57+
allow_credentials=True,
58+
allow_methods=["*"],
59+
allow_headers=["*"],
60+
)
61+
62+
# Create application instances (will be initialized in startup event)
63+
lg_sotf_app = LG_SOTFApplication(
64+
config_path=config_path,
65+
setup_signal_handlers=setup_signal_handlers
66+
)
67+
ws_manager = WebSocketManager()
68+
69+
# Store in app state for dependency injection
70+
app.state.lg_sotf_app = lg_sotf_app
71+
app.state.ws_manager = ws_manager
72+
73+
# Track background tasks for proper shutdown
74+
app.state.background_tasks = []
75+
76+
# Register routers
77+
app.include_router(metrics.router)
78+
app.include_router(alerts.router)
79+
app.include_router(ingestion.router)
80+
app.include_router(dashboard.router)
81+
app.include_router(correlations.router)
82+
app.include_router(escalations.router)
83+
app.include_router(websocket.router)
84+
85+
# Startup event handler
86+
@app.on_event("startup")
87+
async def startup():
88+
"""Initialize application on startup."""
89+
logger.info("Starting LG-SOTF API server...")
90+
91+
# Initialize LG-SOTF application
92+
await lg_sotf_app.initialize()
93+
logger.info("LG-SOTF application initialized")
94+
95+
# Start background tasks
96+
_start_background_tasks(app)
97+
logger.info("Background tasks started")
98+
99+
logger.info("✅ LG-SOTF API server ready")
100+
101+
# Shutdown event handler
102+
@app.on_event("shutdown")
103+
async def shutdown():
104+
"""Cleanup on shutdown."""
105+
logger.info("🛑 Shutting down API server...")
106+
107+
# Close all WebSocket connections
108+
if ws_manager.active_connections:
109+
logger.info(f"Closing {len(ws_manager.active_connections)} WebSocket connections...")
110+
connections = list(ws_manager.active_connections.values())
111+
for ws in connections:
112+
try:
113+
await ws.close()
114+
except Exception as e:
115+
logger.error(f"Error closing WebSocket: {e}")
116+
ws_manager.active_connections.clear()
117+
logger.info("✓ WebSocket connections closed")
118+
119+
# Cancel background tasks
120+
if app.state.background_tasks:
121+
logger.info(f"Cancelling {len(app.state.background_tasks)} background tasks...")
122+
for task in app.state.background_tasks:
123+
if not task.done():
124+
task.cancel()
125+
126+
# Wait for tasks to complete with timeout
127+
try:
128+
await asyncio.wait_for(
129+
asyncio.gather(*app.state.background_tasks, return_exceptions=True),
130+
timeout=5.0
131+
)
132+
logger.info("✓ Background tasks cancelled")
133+
except asyncio.TimeoutError:
134+
logger.warning("⚠ Some background tasks did not complete within timeout")
135+
136+
# Shutdown LG-SOTF application
137+
await lg_sotf_app.shutdown()
138+
logger.info("✅ Shutdown complete")
139+
140+
return app
141+
142+
143+
def _start_background_tasks(app: FastAPI):
144+
"""Start background monitoring and update tasks."""
145+
ws_manager: WebSocketManager = app.state.ws_manager
146+
lg_sotf_app: LG_SOTFApplication = app.state.lg_sotf_app
147+
148+
async def metrics_updater():
149+
"""Periodically collect and broadcast system metrics."""
150+
while True:
151+
try:
152+
await asyncio.sleep(10)
153+
154+
# Import here to avoid circular dependency
155+
from lg_sotf.api.routers.metrics import _collect_system_metrics
156+
157+
metrics = await _collect_system_metrics(lg_sotf_app)
158+
159+
await ws_manager.broadcast({
160+
"type": "system_metrics",
161+
"data": metrics.model_dump()
162+
}, "system_metrics")
163+
164+
except asyncio.CancelledError:
165+
logger.info("Metrics updater cancelled")
166+
break
167+
except Exception as e:
168+
logger.error(f"Metrics updater error: {e}")
169+
170+
async def ingestion_monitor():
171+
"""Monitor ingestion activity and broadcast updates."""
172+
while True:
173+
try:
174+
await asyncio.sleep(5) # Check every 5 seconds
175+
176+
if not lg_sotf_app.workflow_engine:
177+
continue
178+
179+
ingestion_agent = (
180+
lg_sotf_app.workflow_engine.agents.get("ingestion_instance") or
181+
lg_sotf_app.workflow_engine.agents.get("ingestion")
182+
)
183+
184+
if not ingestion_agent:
185+
continue
186+
187+
# Broadcast ingestion stats
188+
await ws_manager.broadcast({
189+
"type": "ingestion_stats",
190+
"data": {
191+
"total_ingested": ingestion_agent.ingestion_stats["total_ingested"],
192+
"total_deduplicated": ingestion_agent.ingestion_stats["total_deduplicated"],
193+
"total_errors": ingestion_agent.ingestion_stats["total_errors"],
194+
"by_source": dict(ingestion_agent.ingestion_stats["by_source"]),
195+
"enabled_sources": ingestion_agent.enabled_sources,
196+
"last_poll": lg_sotf_app._last_ingestion_poll.isoformat() if lg_sotf_app._last_ingestion_poll else None
197+
}
198+
}, "ingestion_updates")
199+
200+
except asyncio.CancelledError:
201+
logger.info("Ingestion monitor cancelled")
202+
break
203+
except Exception as e:
204+
logger.error(f"Ingestion monitor error: {e}")
205+
206+
# Create and track background tasks
207+
app.state.background_tasks.append(asyncio.create_task(ws_manager.heartbeat_loop()))
208+
app.state.background_tasks.append(asyncio.create_task(metrics_updater()))
209+
app.state.background_tasks.append(asyncio.create_task(ingestion_monitor()))
210+
211+
212+
# Create app instance for uvicorn
213+
app = create_app()

src/lg_sotf/api/dependencies.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Dependency injection for FastAPI routes."""
2+
3+
from fastapi import Depends, Request
4+
5+
from lg_sotf.app_initializer import LG_SOTFApplication
6+
from lg_sotf.api.utils.websocket import WebSocketManager
7+
8+
9+
def get_lg_sotf_app(request: Request) -> LG_SOTFApplication:
10+
"""Get the LG-SOTF application instance.
11+
12+
Args:
13+
request: FastAPI request object
14+
15+
Returns:
16+
LG-SOTF application instance from app state
17+
"""
18+
return request.app.state.lg_sotf_app
19+
20+
21+
def get_websocket_manager(request: Request) -> WebSocketManager:
22+
"""Get the WebSocket manager instance.
23+
24+
Args:
25+
request: FastAPI request object
26+
27+
Returns:
28+
WebSocket manager from app state
29+
"""
30+
return request.app.state.ws_manager

src/lg_sotf/api/models/__init__.py

Whitespace-only changes.

src/lg_sotf/api/models/alerts.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""Alert-related Pydantic models."""
2+
3+
from typing import Any, Dict, Optional
4+
from pydantic import BaseModel
5+
6+
7+
class AlertRequest(BaseModel):
8+
"""Request model for processing a new alert."""
9+
alert_data: Dict[str, Any]
10+
priority: Optional[str] = "normal"
11+
12+
13+
class AlertResponse(BaseModel):
14+
"""Response model for alert processing."""
15+
alert_id: str
16+
status: str
17+
workflow_instance_id: str
18+
processing_started: bool
19+
estimated_completion: Optional[str] = None
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Ingestion-related Pydantic models."""
2+
from typing import Any, Dict, List, Optional
3+
from pydantic import BaseModel
4+
5+
class IngestionStatusResponse(BaseModel):
6+
is_active: bool
7+
last_poll_time: Optional[str]
8+
next_poll_time: Optional[str]
9+
polling_interval: int
10+
sources_enabled: List[str]
11+
sources_stats: Dict[str, Dict[str, int]]
12+
total_ingested: int
13+
total_deduplicated: int
14+
total_errors: int
15+
16+
class IngestionControlRequest(BaseModel):
17+
action: str
18+
sources: Optional[List[str]] = None
19+
20+
class SourceConfigRequest(BaseModel):
21+
source_name: str
22+
enabled: bool
23+
config: Optional[Dict[str, Any]] = None

src/lg_sotf/api/models/metrics.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Metrics and health-related Pydantic models."""
2+
from typing import Any, Dict, List, Optional
3+
from pydantic import BaseModel
4+
5+
class MetricsResponse(BaseModel):
6+
timestamp: str
7+
alerts_processed_today: int
8+
alerts_in_progress: int
9+
average_processing_time: float
10+
success_rate: float
11+
agent_health: Dict[str, bool]
12+
system_health: bool
13+
14+
class DashboardStatsResponse(BaseModel):
15+
total_alerts_today: int
16+
high_priority_alerts: int
17+
alerts_by_status: Dict[str, int]
18+
alerts_by_severity: Dict[str, int]
19+
top_threat_indicators: List[Dict[str, Any]]
20+
recent_escalations: List[Dict[str, Any]]
21+
processing_time_avg: float
22+
23+
class AgentStatusResponse(BaseModel):
24+
agent_name: str
25+
status: str
26+
last_execution: Optional[str]
27+
success_rate: float
28+
average_execution_time: float
29+
error_count: int

src/lg_sotf/api/models/websocket.py

Whitespace-only changes.

0 commit comments

Comments
 (0)