feat: Enhance project and scenario creation with monitoring metrics
Some checks failed
CI / lint (push) Failing after 1m14s
CI / test (push) Has been skipped
CI / build (push) Has been skipped

- Added monitoring metrics for project creation success and error handling in `ProjectRepository`.
- Implemented similar monitoring for scenario creation in `ScenarioRepository`.
- Refactored `run_monte_carlo` function in `simulation.py` to include timing and success/error metrics.
- Introduced new CSS styles for headers, alerts, and navigation buttons in `main.css` and `projects.css`.
- Created a new JavaScript file for navigation logic to handle chevron buttons.
- Updated HTML templates to include new navigation buttons and improved styling for buttons.
- Added tests for reporting service and routes to ensure proper functionality and access control.
- Removed unused imports and optimized existing test files for better clarity and performance.
This commit is contained in:
2025-11-12 10:36:24 +01:00
parent f68321cd04
commit ce9c174b53
61 changed files with 2124 additions and 308 deletions

View File

@@ -9,6 +9,7 @@ from starlette.types import ASGIApp
from config.settings import Settings, get_settings
from models import User
from monitoring.metrics import ACTIVE_CONNECTIONS
from services.exceptions import EntityNotFoundError
from services.security import (
JWTSettings,
@@ -45,6 +46,8 @@ class _ResolutionResult:
class AuthSessionMiddleware(BaseHTTPMiddleware):
"""Resolve authenticated users from session cookies and refresh tokens."""
_active_sessions: int = 0
def __init__(
self,
app: ASGIApp,
@@ -61,9 +64,23 @@ class AuthSessionMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
resolved = self._resolve_session(request)
response = await call_next(request)
self._apply_session(response, resolved)
return response
# Track active sessions for authenticated users
if resolved.session.user and resolved.session.user.is_active:
AuthSessionMiddleware._active_sessions += 1
ACTIVE_CONNECTIONS.set(AuthSessionMiddleware._active_sessions)
try:
response = await call_next(request)
return response
finally:
# Decrement on response
if resolved.session.user and resolved.session.user.is_active:
AuthSessionMiddleware._active_sessions = max(
0, AuthSessionMiddleware._active_sessions - 1)
ACTIVE_CONNECTIONS.set(AuthSessionMiddleware._active_sessions)
self._apply_session(response, resolved)
def _resolve_session(self, request: Request) -> _ResolutionResult:
settings = self._settings_provider()

58
middleware/metrics.py Normal file
View File

@@ -0,0 +1,58 @@
from __future__ import annotations
import time
from typing import Callable
from fastapi import BackgroundTasks, Request, Response
from starlette.middleware.base import BaseHTTPMiddleware
from monitoring.metrics import observe_request
from services.metrics import get_metrics_service
class MetricsMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next: Callable[[Request], Response]) -> Response:
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
observe_request(
method=request.method,
endpoint=request.url.path,
status=response.status_code,
seconds=process_time,
)
# Store in database asynchronously
background_tasks = getattr(request.state, "background_tasks", None)
if background_tasks:
background_tasks.add_task(
store_request_metric,
method=request.method,
endpoint=request.url.path,
status_code=response.status_code,
duration_seconds=process_time,
)
return response
async def store_request_metric(
method: str, endpoint: str, status_code: int, duration_seconds: float
) -> None:
"""Store request metric in database."""
try:
service = get_metrics_service()
service.store_metric(
metric_name="http_request",
value=duration_seconds,
labels={"method": method, "endpoint": endpoint,
"status": status_code},
endpoint=endpoint,
method=method,
status_code=status_code,
duration_seconds=duration_seconds,
)
except Exception:
# Log error but don't fail the request
pass