Cybersecurity-Projects/PROJECTS/advanced/ai-threat-detection/backend/app/factory.py

129 lines
3.7 KiB
Python

"""
©AngelaMos | 2026
factory.py
"""
import asyncio
import logging
import time
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlmodel import SQLModel
from app.config import settings
from app.core.alerts.dispatcher import AlertDispatcher
from app.core.detection.rules import RuleEngine
from app.core.enrichment.geoip import GeoIPService
from app.core.ingestion.pipeline import Pipeline
from app.core.ingestion.tailer import LogTailer
from app.core.redis_manager import redis_manager
from app.models import model_metadata as _model_metadata_reg # noqa: F401
from app.models import threat_event as _threat_event_reg # noqa: F401
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
"""
Manage application startup and shutdown lifecycle.
"""
app.state.startup_time = time.monotonic()
app.state.pipeline_running = False
engine = create_async_engine(settings.database_url)
app.state.db_engine = engine
app.state.session_factory = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
logger.info("Database tables verified")
await redis_manager.connect()
geoip = GeoIPService(settings.geoip_db_path)
redis_client = redis_manager.client
assert redis_client is not None
dispatcher = AlertDispatcher(
redis_client=redis_client,
session_factory=app.state.session_factory,
)
pipeline = Pipeline(
redis_client=redis_client, # type: ignore[arg-type]
rule_engine=RuleEngine(),
geoip=geoip,
on_result=dispatcher.dispatch,
raw_queue_size=settings.raw_queue_size,
parsed_queue_size=settings.parsed_queue_size,
feature_queue_size=settings.feature_queue_size,
alert_queue_size=settings.alert_queue_size,
)
await pipeline.start()
tailer = None
log_dir = Path(settings.nginx_log_path).resolve().parent
if log_dir.is_dir():
loop = asyncio.get_running_loop()
tailer = LogTailer(settings.nginx_log_path, pipeline.raw_queue, loop)
tailer.start()
else:
logger.warning("Log directory %s not found — tailer disabled", log_dir)
app.state.pipeline = pipeline
app.state.tailer = tailer
app.state.geoip = geoip
app.state.pipeline_running = True
logger.info("AngelusVigil started — pipeline active")
yield
app.state.pipeline_running = False
if tailer is not None:
tailer.stop()
await pipeline.stop()
geoip.close()
await redis_manager.disconnect()
await engine.dispose()
logger.info("AngelusVigil shut down cleanly")
def create_app() -> FastAPI:
"""
Build and configure the AngelusVigil FastAPI application.
"""
app = FastAPI(
title=settings.app_name,
version="0.1.0",
lifespan=lifespan,
)
app.state.startup_time = time.monotonic()
app.state.pipeline_running = False
from app.api.health import router as health_router
from app.api.models_api import router as models_router
from app.api.stats import router as stats_router
from app.api.threats import router as threats_router
from app.api.websocket import router as ws_router
app.include_router(health_router)
app.include_router(threats_router)
app.include_router(stats_router)
app.include_router(models_router)
app.include_router(ws_router)
return app