starting full refactor with FastAPI routes + HTMX and AlpineJS on client side

This commit is contained in:
Lorenzo Venerandi
2026-02-17 13:09:01 +01:00
parent 04823dab63
commit 5d38ea45a8
34 changed files with 4517 additions and 2 deletions

95
src/dependencies.py Normal file
View File

@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""
FastAPI dependency injection providers.
Replaces Handler class variables with proper DI.
"""
import os
from datetime import datetime
from fastapi import Request
from fastapi.templating import Jinja2Templates
from config import Config
from tracker import AccessTracker
from database import DatabaseManager, get_database
from logger import get_app_logger, get_access_logger, get_credential_logger
# Shared Jinja2 templates instance
_templates = None
def get_templates() -> Jinja2Templates:
"""Get shared Jinja2Templates instance with custom filters."""
global _templates
if _templates is None:
templates_dir = os.path.join(
os.path.dirname(__file__), "templates", "jinja2"
)
_templates = Jinja2Templates(directory=templates_dir)
_templates.env.filters["format_ts"] = _format_ts
return _templates
def _format_ts(value, time_only=False):
"""Custom Jinja2 filter for formatting ISO timestamps."""
if not value:
return "N/A"
if isinstance(value, str):
try:
value = datetime.fromisoformat(value)
except (ValueError, TypeError):
return value
if time_only:
return value.strftime("%H:%M:%S")
return value.strftime("%m/%d/%Y %H:%M:%S")
def get_tracker(request: Request) -> AccessTracker:
return request.app.state.tracker
def get_app_config(request: Request) -> Config:
return request.app.state.config
def get_db() -> DatabaseManager:
return get_database()
def get_client_ip(request: Request) -> str:
"""Extract client IP address from request, checking proxy headers first."""
forwarded_for = request.headers.get("X-Forwarded-For")
if forwarded_for:
return forwarded_for.split(",")[0].strip()
real_ip = request.headers.get("X-Real-IP")
if real_ip:
return real_ip.strip()
if request.client:
return request.client.host
return "0.0.0.0"
def build_raw_request(request: Request, body: str = "") -> str:
"""Build raw HTTP request string for forensic analysis."""
try:
raw = f"{request.method} {request.url.path}"
if request.url.query:
raw += f"?{request.url.query}"
raw += f" HTTP/1.1\r\n"
for header, value in request.headers.items():
raw += f"{header}: {value}\r\n"
raw += "\r\n"
if body:
raw += body
return raw
except Exception as e:
return f"{request.method} {request.url.path} (error building full request: {str(e)})"