Files
ipwatch/backend/app/routers/tracking.py
2026-02-07 16:57:37 +01:00

228 lines
6.4 KiB
Python

"""
Endpoints API pour le suivi d'équipements (Wake-on-LAN, shutdown, etc.)
"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import datetime
from pydantic import BaseModel
from backend.app.core.database import get_db
from backend.app.models.ip import IP
router = APIRouter(prefix="/api/tracking", tags=["Tracking"])
# Schémas Pydantic
class IPTrackingResponse(BaseModel):
"""Schéma de réponse pour les IPs suivies"""
ip: str
name: Optional[str]
known: bool
tracked: bool
location: Optional[str]
host: Optional[str]
last_status: Optional[str]
mac: Optional[str]
vendor: Optional[str]
hostname: Optional[str]
link: Optional[str]
last_seen: Optional[datetime]
open_ports: List[int]
class Config:
from_attributes = True
class WOLResponse(BaseModel):
"""Réponse après envoi Wake-on-LAN"""
message: str
ip: str
mac: str
success: bool
class ShutdownResponse(BaseModel):
"""Réponse après commande d'arrêt"""
message: str
ip: str
success: bool
@router.get("/", response_model=List[IPTrackingResponse])
async def get_tracked_ips(db: Session = Depends(get_db)):
"""
Récupère toutes les IPs marquées comme suivies
Retourne la liste des équipements avec leur état actuel
"""
tracked_ips = db.query(IP).filter(IP.tracked == True).order_by(IP.name, IP.ip).all()
return tracked_ips
@router.post("/wol/{ip_address}", response_model=WOLResponse)
async def wake_on_lan(ip_address: str, db: Session = Depends(get_db)):
"""
Envoie un paquet Magic Packet Wake-on-LAN à l'équipement
Nécessite que l'IP ait une adresse MAC enregistrée
"""
# Récupérer l'IP depuis la base
ip_obj = db.query(IP).filter(IP.ip == ip_address).first()
if not ip_obj:
raise HTTPException(
status_code=404,
detail=f"IP {ip_address} non trouvée dans la base de données"
)
if not ip_obj.mac:
raise HTTPException(
status_code=400,
detail=f"Adresse MAC manquante pour {ip_address}. Impossible d'envoyer le paquet WOL."
)
try:
# Importer la bibliothèque wakeonlan
from wakeonlan import send_magic_packet
# Envoyer le paquet Magic Packet
send_magic_packet(ip_obj.mac)
return WOLResponse(
message=f"Paquet Wake-on-LAN envoyé avec succès",
ip=ip_address,
mac=ip_obj.mac,
success=True
)
except ImportError:
raise HTTPException(
status_code=500,
detail="La bibliothèque 'wakeonlan' n'est pas installée. Exécutez: pip install wakeonlan"
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Erreur lors de l'envoi du paquet WOL: {str(e)}"
)
@router.post("/shutdown/{ip_address}", response_model=ShutdownResponse)
async def shutdown_device(ip_address: str, db: Session = Depends(get_db)):
"""
Envoie une commande shutdown via MQTT à l'équipement
"""
# Récupérer l'IP depuis la base
ip_obj = db.query(IP).filter(IP.ip == ip_address).first()
if not ip_obj:
raise HTTPException(
status_code=404,
detail=f"IP {ip_address} non trouvée dans la base de données"
)
if ip_obj.last_status != "online":
raise HTTPException(
status_code=400,
detail=f"L'équipement {ip_address} est déjà hors ligne"
)
try:
from backend.app.services.mqtt_client import send_mqtt_command
# Envoyer commande shutdown via MQTT
success = send_mqtt_command(ip_address, "shutdown")
if success:
return ShutdownResponse(
message=f"Commande shutdown envoyée à {ip_address} via MQTT",
ip=ip_address,
success=True
)
else:
raise HTTPException(
status_code=500,
detail="Échec de l'envoi de la commande MQTT"
)
except ImportError:
raise HTTPException(
status_code=500,
detail="Le service MQTT n'est pas configuré. Consultez mqtt/docs/README.md"
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Erreur lors de l'envoi de la commande: {str(e)}"
)
@router.post("/reboot/{ip_address}", response_model=ShutdownResponse)
async def reboot_device(ip_address: str, db: Session = Depends(get_db)):
"""
Envoie une commande reboot via MQTT à l'équipement
"""
# Récupérer l'IP depuis la base
ip_obj = db.query(IP).filter(IP.ip == ip_address).first()
if not ip_obj:
raise HTTPException(
status_code=404,
detail=f"IP {ip_address} non trouvée"
)
if ip_obj.last_status != "online":
raise HTTPException(
status_code=400,
detail=f"L'équipement {ip_address} est hors ligne"
)
try:
from backend.app.services.mqtt_client import send_mqtt_command
# Envoyer commande reboot via MQTT
success = send_mqtt_command(ip_address, "reboot")
if success:
return ShutdownResponse(
message=f"Commande reboot envoyée à {ip_address} via MQTT",
ip=ip_address,
success=True
)
else:
raise HTTPException(
status_code=500,
detail="Échec de l'envoi de la commande MQTT"
)
except ImportError:
raise HTTPException(
status_code=500,
detail="Le service MQTT n'est pas configuré"
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Erreur: {str(e)}"
)
@router.patch("/{ip_address}/toggle", response_model=IPTrackingResponse)
async def toggle_tracking(ip_address: str, db: Session = Depends(get_db)):
"""
Bascule l'état de suivi d'une IP (tracked true/false)
"""
ip_obj = db.query(IP).filter(IP.ip == ip_address).first()
if not ip_obj:
raise HTTPException(
status_code=404,
detail=f"IP {ip_address} non trouvée"
)
# Inverser l'état tracked
ip_obj.tracked = not ip_obj.tracked
db.commit()
db.refresh(ip_obj)
return ip_obj