Files
ipwatch/backend/app/main.py
2026-02-07 18:53:18 +01:00

224 lines
7.0 KiB
Python
Executable File

"""
Application FastAPI principale pour IPWatch
Point d'entrée du backend
"""
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from contextlib import asynccontextmanager
from pathlib import Path
from backend.app.core.config import config_manager
from backend.app.core.database import init_database, get_db
from backend.app.routers import ips_router, scan_router, websocket_router
from backend.app.routers import architecture as architecture_router
from backend.app.routers import config as config_router
from backend.app.routers import system as system_router
from backend.app.routers import tracking as tracking_router
from backend.app.routers import opnsense as opnsense_router
from backend.app.routers import services as services_router
from backend.app.services.scheduler import scan_scheduler
from backend.app.routers.scan import perform_scan
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Gestionnaire du cycle de vie de l'application
Initialise et nettoie les ressources
"""
# Startup
print("=== Démarrage IPWatch ===")
# 1. Charger la configuration
try:
config = config_manager.load_config("./config.yaml")
print(f"✓ Configuration chargée: {config.network.cidr}")
except Exception as e:
print(f"✗ Erreur chargement config: {e}")
raise
# 2. Initialiser la base de données
try:
init_database(config.database.path)
print(f"✓ Base de données initialisée: {config.database.path}")
except Exception as e:
print(f"✗ Erreur initialisation DB: {e}")
raise
# 3. Démarrer le scheduler
try:
scan_scheduler.start()
# Créer une session DB pour les scans planifiés
from backend.app.core.database import SessionLocal
async def scheduled_scan():
"""Wrapper pour scan planifié avec DB session"""
db = SessionLocal()
try:
await perform_scan(db)
finally:
db.close()
# Configurer les tâches périodiques
scan_scheduler.add_ping_scan_job(
scheduled_scan,
interval_seconds=config.scan.ping_interval
)
scan_scheduler.add_port_scan_job(
scheduled_scan,
interval_seconds=config.scan.port_scan_interval
)
# Tâche de nettoyage historique
async def cleanup_history():
"""Nettoie l'historique ancien"""
from backend.app.models.ip import IPHistory
from datetime import datetime, timedelta
db = SessionLocal()
try:
cutoff = datetime.utcnow() - timedelta(hours=config.history.retention_hours)
deleted = db.query(IPHistory).filter(IPHistory.timestamp < cutoff).delete()
db.commit()
print(f"Nettoyage historique: {deleted} entrées supprimées")
finally:
db.close()
scan_scheduler.add_cleanup_job(cleanup_history, interval_hours=1)
print("✓ Scheduler démarré")
except Exception as e:
print(f"✗ Erreur démarrage scheduler: {e}")
print("=== IPWatch prêt ===\n")
yield
# Shutdown
print("\n=== Arrêt IPWatch ===")
scan_scheduler.stop()
print("✓ Scheduler arrêté")
# Créer l'application FastAPI
app = FastAPI(
title="IPWatch API",
description="API backend pour IPWatch - Scanner réseau temps réel",
version="1.0.0",
lifespan=lifespan
)
# Configuration CORS pour le frontend
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # À restreindre en production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Enregistrer les routers API
app.include_router(ips_router)
app.include_router(scan_router)
app.include_router(websocket_router)
app.include_router(config_router.router)
app.include_router(system_router.router)
app.include_router(tracking_router.router)
app.include_router(architecture_router.router)
app.include_router(opnsense_router.router)
app.include_router(services_router.router)
# Servir les ressources d'architecture
architecture_dir = Path("./architecture")
architecture_dir.mkdir(parents=True, exist_ok=True)
app.mount("/architecture", StaticFiles(directory=str(architecture_dir)), name="architecture")
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {
"status": "healthy",
"scheduler": scan_scheduler.is_running
}
# Servir les fichiers statiques du frontend
frontend_dist = Path(__file__).parent.parent.parent / "frontend" / "dist"
if frontend_dist.exists():
# Monter les assets statiques
app.mount("/assets", StaticFiles(directory=str(frontend_dist / "assets")), name="assets")
# Monter les icônes partagées
icons_dir = Path("./data/icons")
icons_dir.mkdir(parents=True, exist_ok=True)
app.mount("/icons", StaticFiles(directory=str(icons_dir)), name="icons")
# Servir les fichiers statiques à la racine (favicon, manifest, etc.)
@app.get("/favicon.ico")
async def serve_favicon():
favicon_path = frontend_dist / "favicon.ico"
if favicon_path.exists():
return FileResponse(favicon_path)
return {"error": "Favicon non trouvée"}
# Route racine pour servir index.html
@app.get("/")
async def serve_frontend():
"""Servir le frontend Vue"""
index_file = frontend_dist / "index.html"
if index_file.exists():
return FileResponse(index_file)
return {
"name": "IPWatch API",
"version": "1.0.0",
"status": "running",
"error": "Frontend non trouvé"
}
# Catch-all pour le routing Vue (SPA)
@app.get("/{full_path:path}")
async def catch_all(full_path: str):
"""Catch-all pour le routing Vue Router"""
# Ne pas intercepter les routes API
if full_path.startswith("api/") or full_path.startswith("ws"):
return {"error": "Not found"}
# Servir les fichiers statiques à la racine si présents
if ".." not in full_path:
candidate = (frontend_dist / full_path).resolve()
if frontend_dist in candidate.parents and candidate.is_file():
return FileResponse(candidate)
# Servir index.html pour toutes les autres routes
index_file = frontend_dist / "index.html"
if index_file.exists():
return FileResponse(index_file)
return {"error": "Frontend non trouvé"}
else:
@app.get("/")
async def root():
"""Endpoint racine (mode développement sans frontend)"""
return {
"name": "IPWatch API",
"version": "1.0.0",
"status": "running",
"note": "Frontend non buildé - utilisez le mode dev"
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"backend.app.main:app",
host="0.0.0.0",
port=8080,
reload=True
)