import logging from typing import Any from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import get_current_user from app.core.config import settings from app.db.database import AsyncSessionLocal, get_db from app.db.models import Node, PendingDevice, ScanRun from app.schemas.nodes import NodeCreate from app.schemas.scan import PendingDeviceResponse, ScanRunResponse from app.services.scanner import run_scan class ScanConfig(BaseModel): ranges: list[str] interval_seconds: int logger = logging.getLogger(__name__) router = APIRouter() async def _background_scan(run_id: str, ranges: list[str]) -> None: async with AsyncSessionLocal() as db: await run_scan(ranges, db, run_id) @router.post("/trigger", response_model=ScanRunResponse) async def trigger_scan( background_tasks: BackgroundTasks, db: AsyncSession = Depends(get_db), _: str = Depends(get_current_user), ) -> ScanRun: ranges = settings.scanner_ranges run = ScanRun(status="running", ranges=ranges) db.add(run) await db.commit() await db.refresh(run) background_tasks.add_task(_background_scan, run.id, ranges) return run @router.get("/pending", response_model=list[PendingDeviceResponse]) async def list_pending(db: AsyncSession = Depends(get_db), _: str = Depends(get_current_user)) -> list[PendingDevice]: result = await db.execute(select(PendingDevice).where(PendingDevice.status == "pending")) return list(result.scalars().all()) @router.get("/hidden", response_model=list[PendingDeviceResponse]) async def list_hidden(db: AsyncSession = Depends(get_db), _: str = Depends(get_current_user)) -> list[PendingDevice]: result = await db.execute(select(PendingDevice).where(PendingDevice.status == "hidden")) return list(result.scalars().all()) @router.post("/pending/{device_id}/approve", response_model=dict) async def approve_device( device_id: str, node_data: NodeCreate, db: AsyncSession = Depends(get_db), _: str = Depends(get_current_user), ) -> dict[str, Any]: device = await db.get(PendingDevice, device_id) if device: device.status = "approved" node = Node(**node_data.model_dump()) db.add(node) await db.commit() return {"approved": True, "node_id": node.id} return {"approved": False} @router.post("/pending/{device_id}/hide") async def hide_device( device_id: str, db: AsyncSession = Depends(get_db), _: str = Depends(get_current_user) ) -> dict[str, bool]: device = await db.get(PendingDevice, device_id) if device: device.status = "hidden" await db.commit() return {"hidden": True} @router.post("/pending/{device_id}/ignore") async def ignore_device( device_id: str, db: AsyncSession = Depends(get_db), _: str = Depends(get_current_user) ) -> dict[str, bool]: device = await db.get(PendingDevice, device_id) if device: await db.delete(device) await db.commit() return {"ignored": True} @router.get("/runs", response_model=list[ScanRunResponse]) async def list_runs(db: AsyncSession = Depends(get_db), _: str = Depends(get_current_user)) -> list[ScanRun]: result = await db.execute(select(ScanRun).order_by(ScanRun.started_at.desc()).limit(20)) return list(result.scalars().all()) @router.get("/config", response_model=ScanConfig) async def get_scan_config(_: str = Depends(get_current_user)) -> ScanConfig: return ScanConfig( ranges=settings.scanner_ranges, interval_seconds=settings.status_checker_interval, ) @router.post("/config", response_model=ScanConfig) async def update_scan_config(payload: ScanConfig, _: str = Depends(get_current_user)) -> ScanConfig: try: settings.scanner_ranges = payload.ranges settings.status_checker_interval = payload.interval_seconds settings.save_overrides() return payload except Exception as exc: raise HTTPException(status_code=500, detail=str(exc)) from exc