move
This commit is contained in:
40
backend/app/routers/config.py
Normal file
40
backend/app/routers/config.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
Routes pour la configuration
|
||||
"""
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from backend.app.core.config import config_manager
|
||||
|
||||
router = APIRouter(prefix="/api/config", tags=["config"])
|
||||
|
||||
@router.get("/ui")
|
||||
async def get_ui_config():
|
||||
"""Récupérer la configuration UI"""
|
||||
config = config_manager.config
|
||||
return {
|
||||
"cell_size": config.ui.cell_size,
|
||||
"font_size": config.ui.font_size,
|
||||
"cell_gap": config.ui.cell_gap,
|
||||
"offline_transparency": config.ui.offline_transparency,
|
||||
"show_mac": config.ui.show_mac,
|
||||
"show_vendor": config.ui.show_vendor
|
||||
}
|
||||
|
||||
@router.post("/reload")
|
||||
async def reload_config():
|
||||
"""Recharger la configuration depuis le fichier config.yaml"""
|
||||
try:
|
||||
config = config_manager.reload_config()
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Configuration rechargée avec succès",
|
||||
"ui": {
|
||||
"cell_size": config.ui.cell_size,
|
||||
"font_size": config.ui.font_size,
|
||||
"cell_gap": config.ui.cell_gap,
|
||||
"offline_transparency": config.ui.offline_transparency,
|
||||
"show_mac": config.ui.show_mac,
|
||||
"show_vendor": config.ui.show_vendor
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Erreur rechargement config: {str(e)}")
|
||||
@@ -1,14 +1,17 @@
|
||||
"""
|
||||
Endpoints API pour la gestion des IPs
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import desc
|
||||
from typing import List, Optional
|
||||
from datetime import datetime, timedelta
|
||||
import xml.etree.ElementTree as ET
|
||||
import yaml
|
||||
|
||||
from backend.app.core.database import get_db
|
||||
from backend.app.models.ip import IP, IPHistory
|
||||
from backend.app.core.config import config_manager
|
||||
from pydantic import BaseModel
|
||||
|
||||
router = APIRouter(prefix="/api/ips", tags=["IPs"])
|
||||
@@ -21,6 +24,7 @@ class IPUpdate(BaseModel):
|
||||
known: Optional[bool] = None
|
||||
location: Optional[str] = None
|
||||
host: Optional[str] = None
|
||||
link: Optional[str] = None
|
||||
|
||||
|
||||
class IPResponse(BaseModel):
|
||||
@@ -36,6 +40,7 @@ class IPResponse(BaseModel):
|
||||
mac: Optional[str]
|
||||
vendor: Optional[str]
|
||||
hostname: Optional[str]
|
||||
link: Optional[str]
|
||||
open_ports: List[int]
|
||||
|
||||
class Config:
|
||||
@@ -214,3 +219,189 @@ async def get_stats(db: Session = Depends(get_db)):
|
||||
"known": known,
|
||||
"unknown": unknown
|
||||
}
|
||||
|
||||
|
||||
@router.get("/config/options")
|
||||
async def get_config_options():
|
||||
"""
|
||||
Récupère les options de configuration (locations, hosts, port_protocols, version)
|
||||
|
||||
Returns:
|
||||
Dictionnaire avec locations, hosts, port_protocols et version
|
||||
"""
|
||||
config = config_manager.config
|
||||
|
||||
# Récupérer les protocoles de ports depuis la config
|
||||
port_protocols = {}
|
||||
if hasattr(config.ports, 'protocols') and config.ports.protocols:
|
||||
port_protocols = config.ports.protocols
|
||||
|
||||
return {
|
||||
"locations": config.locations,
|
||||
"hosts": [{"name": h.name, "location": h.location} for h in config.hosts],
|
||||
"port_protocols": port_protocols,
|
||||
"version": config.app.version
|
||||
}
|
||||
|
||||
|
||||
@router.get("/config/content")
|
||||
async def get_config_content():
|
||||
"""
|
||||
Récupère le contenu brut du fichier config.yaml
|
||||
|
||||
Returns:
|
||||
Contenu du fichier YAML
|
||||
"""
|
||||
try:
|
||||
with open("./config.yaml", "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
return {"content": content}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Erreur lecture config: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/config/reload")
|
||||
async def reload_config():
|
||||
"""
|
||||
Recharge la configuration depuis config.yaml
|
||||
|
||||
Returns:
|
||||
Message de confirmation
|
||||
"""
|
||||
try:
|
||||
config_manager.reload_config()
|
||||
return {"message": "Configuration rechargée avec succès"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Erreur rechargement config: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/import/ipscan")
|
||||
async def import_ipscan(file: UploadFile = File(...), db: Session = Depends(get_db)):
|
||||
"""
|
||||
Importe les données depuis un fichier XML Angry IP Scanner
|
||||
|
||||
Args:
|
||||
file: Fichier XML uploadé
|
||||
db: Session de base de données
|
||||
|
||||
Returns:
|
||||
Statistiques d'import
|
||||
"""
|
||||
if not file.filename.endswith('.xml'):
|
||||
raise HTTPException(status_code=400, detail="Le fichier doit être un XML")
|
||||
|
||||
try:
|
||||
# Lire le contenu du fichier
|
||||
content = await file.read()
|
||||
|
||||
# Essayer de parser le XML avec récupération d'erreurs
|
||||
try:
|
||||
root = ET.fromstring(content)
|
||||
except ET.ParseError as e:
|
||||
# Si le parsing échoue, essayer de nettoyer le contenu
|
||||
import re
|
||||
content_str = content.decode('utf-8', errors='ignore')
|
||||
|
||||
# Supprimer les caractères de contrôle invalides (sauf tab, CR, LF)
|
||||
content_str = ''.join(char for char in content_str
|
||||
if ord(char) >= 32 or char in '\t\r\n')
|
||||
|
||||
try:
|
||||
root = ET.fromstring(content_str.encode('utf-8'))
|
||||
except ET.ParseError:
|
||||
raise HTTPException(status_code=400, detail=f"Fichier XML invalide même après nettoyage: {str(e)}")
|
||||
|
||||
imported = 0
|
||||
updated = 0
|
||||
errors = []
|
||||
|
||||
# Parser chaque host
|
||||
for host in root.findall('.//host'):
|
||||
try:
|
||||
# Extraire l'adresse IP
|
||||
ip_address = host.get('address')
|
||||
if not ip_address:
|
||||
continue
|
||||
|
||||
# Extraire les informations
|
||||
hostname = None
|
||||
mac = None
|
||||
vendor = None
|
||||
ports = []
|
||||
|
||||
for result in host.findall('result'):
|
||||
name = result.get('name')
|
||||
value = result.text.strip() if result.text else ""
|
||||
|
||||
# Nettoyer les valeurs [n/a]
|
||||
if value == "[n/a]":
|
||||
value = None
|
||||
|
||||
if name == "Nom d'hôte" and value:
|
||||
hostname = value
|
||||
elif name == "Adresse MAC" and value:
|
||||
mac = value
|
||||
elif name == "Constructeur MAC" and value:
|
||||
vendor = value
|
||||
elif name == "Ports" and value:
|
||||
# Parser les ports (format: "22,80,443")
|
||||
try:
|
||||
ports = [int(p.strip()) for p in value.split(',') if p.strip().isdigit()]
|
||||
except Exception as e:
|
||||
ports = []
|
||||
|
||||
# Vérifier si l'IP existe déjà
|
||||
existing_ip = db.query(IP).filter(IP.ip == ip_address).first()
|
||||
|
||||
if existing_ip:
|
||||
# Mettre à jour avec de nouvelles informations
|
||||
if hostname:
|
||||
if not existing_ip.hostname:
|
||||
existing_ip.hostname = hostname
|
||||
if not existing_ip.name:
|
||||
existing_ip.name = hostname
|
||||
if mac and not existing_ip.mac:
|
||||
existing_ip.mac = mac
|
||||
# Toujours mettre à jour vendor et ports depuis IPScan (plus complet et à jour)
|
||||
if vendor:
|
||||
existing_ip.vendor = vendor
|
||||
if ports:
|
||||
existing_ip.open_ports = ports
|
||||
existing_ip.last_status = "online"
|
||||
existing_ip.last_seen = datetime.utcnow()
|
||||
updated += 1
|
||||
else:
|
||||
# Créer une nouvelle entrée
|
||||
new_ip = IP(
|
||||
ip=ip_address,
|
||||
name=hostname,
|
||||
hostname=hostname,
|
||||
mac=mac,
|
||||
vendor=vendor,
|
||||
open_ports=ports or [],
|
||||
last_status="online",
|
||||
known=False,
|
||||
first_seen=datetime.utcnow(),
|
||||
last_seen=datetime.utcnow()
|
||||
)
|
||||
db.add(new_ip)
|
||||
imported += 1
|
||||
|
||||
except Exception as e:
|
||||
errors.append(f"Erreur pour {ip_address}: {str(e)}")
|
||||
continue
|
||||
|
||||
# Commit des changements
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"message": "Import terminé",
|
||||
"imported": imported,
|
||||
"updated": updated,
|
||||
"errors": errors[:10] # Limiter à 10 erreurs
|
||||
}
|
||||
|
||||
except ET.ParseError as e:
|
||||
raise HTTPException(status_code=400, detail=f"Fichier XML invalide: {str(e)}")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Erreur import: {str(e)}")
|
||||
|
||||
@@ -174,6 +174,72 @@ async def start_scan(background_tasks: BackgroundTasks, db: Session = Depends(ge
|
||||
}
|
||||
|
||||
|
||||
@router.post("/ports/{ip_address}")
|
||||
async def scan_ip_ports(ip_address: str, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Scanne les ports d'une IP spécifique
|
||||
|
||||
Args:
|
||||
ip_address: Adresse IP à scanner
|
||||
db: Session de base de données
|
||||
|
||||
Returns:
|
||||
Liste des ports ouverts
|
||||
"""
|
||||
try:
|
||||
# Récupérer la config
|
||||
config = config_manager.config
|
||||
|
||||
# Convertir les ports en liste d'entiers
|
||||
port_list = []
|
||||
for port_range in config.ports.ranges:
|
||||
if '-' in port_range:
|
||||
start, end = map(int, port_range.split('-'))
|
||||
port_list.extend(range(start, end + 1))
|
||||
else:
|
||||
port_list.append(int(port_range))
|
||||
|
||||
# Initialiser le scanner
|
||||
scanner = NetworkScanner(
|
||||
cidr=config.network.cidr,
|
||||
timeout=config.scan.timeout
|
||||
)
|
||||
|
||||
# Scanner les ports de cette IP
|
||||
print(f"[{datetime.now()}] Scan ports pour {ip_address}...")
|
||||
open_ports = await scanner.scan_ports(ip_address, port_list)
|
||||
print(f"[{datetime.now()}] Ports ouverts pour {ip_address}: {open_ports}")
|
||||
|
||||
# Mettre à jour la base de données
|
||||
ip_record = db.query(IP).filter(IP.ip == ip_address).first()
|
||||
if ip_record:
|
||||
ip_record.open_ports = open_ports
|
||||
ip_record.last_seen = datetime.utcnow()
|
||||
db.commit()
|
||||
|
||||
# Notifier via WebSocket
|
||||
await ws_manager.broadcast_ip_update({
|
||||
"ip": ip_address,
|
||||
"open_ports": open_ports
|
||||
})
|
||||
|
||||
return {
|
||||
"message": "Scan de ports terminé",
|
||||
"ip": ip_address,
|
||||
"open_ports": open_ports,
|
||||
"timestamp": datetime.utcnow()
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Erreur scan ports {ip_address}: {e}")
|
||||
return {
|
||||
"message": f"Erreur: {str(e)}",
|
||||
"ip": ip_address,
|
||||
"open_ports": [],
|
||||
"timestamp": datetime.utcnow()
|
||||
}
|
||||
|
||||
|
||||
@router.post("/cleanup-history")
|
||||
async def cleanup_history(hours: int = 24, db: Session = Depends(get_db)):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user