from fastapi import APIRouter, Depends, Query from pydantic import BaseModel from sqlalchemy import func, select from sqlalchemy.orm import Session from app import schemas as s from app.api.v1.prefixes import _prefix_outs from app.db import get_db from app.models import Address, AddressStatus, AuditLog, Prefix, PrefixStatus, Vrf from app.security import current_user from app.services import capacity, utilization router = APIRouter(dependencies=[Depends(current_user)]) class Overview(BaseModel): prefixes: int vrfs: int assigned: int capacity: int utilization: int reserved: int top_prefixes: list[s.PrefixOut] recent_changes: list[s.AuditOut] @router.get("/overview", response_model=Overview, tags=["overview"]) def overview(db: Session = Depends(get_db)): prefixes = db.scalars(select(Prefix).where(Prefix.status == PrefixStatus.active)).all() outs = _prefix_outs(db, list(prefixes)) parents = {p.parent_id for p in outs if p.parent_id} leaves = [p for p in outs if p.id not in parents] # ёмкость считаем по листьям, чтобы не удваивать leaves4 = [p for p in leaves if p.family == 4] # IPv6-пространство несопоставимо по размеру — в метрики использования не входит cap = sum(p.capacity for p in leaves4) v4 = func.family(Address.address) == 4 assigned = db.scalar(select(func.count()).select_from(Address).where(Address.status == AddressStatus.assigned, v4)) or 0 reserved = db.scalar(select(func.count()).select_from(Address).where(Address.status == AddressStatus.reserved, v4)) or 0 top = sorted((p for p in leaves4 if p.capacity > 1), key=lambda p: p.utilization, reverse=True)[:4] recent = db.scalars(select(AuditLog).order_by(AuditLog.id.desc()).limit(4)).all() return Overview( prefixes=db.scalar(select(func.count()).select_from(Prefix)) or 0, vrfs=db.scalar(select(func.count()).select_from(Vrf)) or 0, assigned=assigned, capacity=cap, utilization=utilization(assigned, cap), reserved=reserved, top_prefixes=top, recent_changes=[s.AuditOut.from_row(r) for r in recent], )