"""Host API endpoints.""" import logging from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from sqlalchemy import or_ from app.database import get_db from app.models import Host, Service from app.schemas import HostResponse, HostDetailResponse, ServiceResponse, NetworkStatistics from app.services.topology_service import TopologyService logger = logging.getLogger(__name__) router = APIRouter() @router.get("", response_model=List[HostResponse]) def list_hosts( status: Optional[str] = Query(None, description="Filter by status (online/offline)"), limit: int = Query(100, ge=1, le=1000), offset: int = Query(0, ge=0), search: Optional[str] = Query(None, description="Search by IP or hostname"), db: Session = Depends(get_db) ): """ List discovered hosts. Args: status: Filter by host status limit: Maximum number of hosts to return offset: Number of hosts to skip search: Search query db: Database session Returns: List of hosts """ query = db.query(Host) # Apply filters if status: query = query.filter(Host.status == status) if search: search_pattern = f"%{search}%" query = query.filter( or_( Host.ip_address.like(search_pattern), Host.hostname.like(search_pattern) ) ) # Order by last seen query = query.order_by(Host.last_seen.desc()) # Apply pagination hosts = query.limit(limit).offset(offset).all() return hosts @router.get("/statistics", response_model=NetworkStatistics) def get_network_statistics(db: Session = Depends(get_db)): """ Get network statistics. Args: db: Database session Returns: Network statistics """ topology_service = TopologyService(db) stats = topology_service.get_network_statistics() # Get most common services from sqlalchemy import func service_counts = db.query( Service.service_name, func.count(Service.id).label('count') ).filter( Service.service_name.isnot(None) ).group_by( Service.service_name ).order_by( func.count(Service.id).desc() ).limit(10).all() # Get last scan time from app.models import Scan last_scan = db.query(Scan).order_by(Scan.started_at.desc()).first() return NetworkStatistics( total_hosts=stats['total_hosts'], online_hosts=stats['online_hosts'], offline_hosts=stats['offline_hosts'], total_services=stats['total_services'], total_scans=db.query(func.count(Scan.id)).scalar() or 0, last_scan=last_scan.started_at if last_scan else None, most_common_services=[ {'service_name': s[0], 'count': s[1]} for s in service_counts ] ) @router.get("/by-service/{service_name}", response_model=List[HostResponse]) def get_hosts_by_service( service_name: str, limit: int = Query(100, ge=1, le=1000), offset: int = Query(0, ge=0), db: Session = Depends(get_db) ): """ Get all hosts that provide a specific service. Args: service_name: Service name to filter by limit: Maximum number of hosts to return offset: Number of hosts to skip db: Database session Returns: List of hosts providing the service """ hosts = db.query(Host).join(Service).filter( Service.service_name == service_name ).distinct().order_by( Host.last_seen.desc() ).limit(limit).offset(offset).all() return hosts @router.get("/{host_id}", response_model=HostDetailResponse) def get_host_detail(host_id: int, db: Session = Depends(get_db)): """ Get detailed information about a specific host. Args: host_id: Host ID db: Database session Returns: Detailed host information """ host = db.query(Host).filter(Host.id == host_id).first() if not host: raise HTTPException(status_code=404, detail=f"Host {host_id} not found") return host @router.get("/{host_id}/services", response_model=List[ServiceResponse]) def get_host_services(host_id: int, db: Session = Depends(get_db)): """ Get all services for a specific host. Args: host_id: Host ID db: Database session Returns: List of services """ host = db.query(Host).filter(Host.id == host_id).first() if not host: raise HTTPException(status_code=404, detail=f"Host {host_id} not found") return host.services @router.delete("/{host_id}") def delete_host(host_id: int, db: Session = Depends(get_db)): """ Delete a host from the database. Args: host_id: Host ID db: Database session Returns: Success message """ host = db.query(Host).filter(Host.id == host_id).first() if not host: raise HTTPException(status_code=404, detail=f"Host {host_id} not found") db.delete(host) db.commit() logger.info(f"Deleted host {host_id} ({host.ip_address})") return {"message": f"Host {host_id} deleted successfully"} @router.get("/ip/{ip_address}", response_model=HostResponse) def get_host_by_ip(ip_address: str, db: Session = Depends(get_db)): """ Get host information by IP address. Args: ip_address: IP address db: Database session Returns: Host information """ host = db.query(Host).filter(Host.ip_address == ip_address).first() if not host: raise HTTPException( status_code=404, detail=f"Host with IP {ip_address} not found" ) return host