Files
stupid-simple-network-inven…/backend/routers/vlans.py
T
olivier 88cf6458d0 Initial commit — Stupid Simple Network Inventory
Application web d'inventaire réseau manuel avec FastAPI, Vue 3 et Docker.
Inclut l'authentification JWT, la découverte ICMP, et la topologie en cards CSS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 09:19:19 +02:00

103 lines
3.0 KiB
Python

import ipaddress
import re
from typing import Optional, List
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, field_validator
from sqlalchemy import nullsfirst
from sqlalchemy.orm import Session
from database import get_db
import models
router = APIRouter()
_COLOR_RE = re.compile(r"^#[0-9a-fA-F]{6}$")
class VlanCreate(BaseModel):
vlan_id: Optional[int] = None
name: str
cidr: Optional[str] = ""
color: str = "#4A90D9"
@field_validator("vlan_id")
@classmethod
def _vlan_id(cls, v: Optional[int]) -> Optional[int]:
if v is not None and not (1 <= v <= 4094):
raise ValueError("vlan_id must be between 1 and 4094")
return v
@field_validator("name")
@classmethod
def _name(cls, v: str) -> str:
v = v.strip()
if not v:
raise ValueError("name cannot be empty")
if len(v) > 100:
raise ValueError("name too long (max 100 characters)")
return v
@field_validator("cidr")
@classmethod
def _cidr(cls, v: Optional[str]) -> Optional[str]:
if v:
try:
ipaddress.ip_network(v, strict=False)
except ValueError:
raise ValueError(f"Invalid CIDR notation: {v!r}")
return v
@field_validator("color")
@classmethod
def _color(cls, v: str) -> str:
if not _COLOR_RE.match(v):
raise ValueError("color must be a 6-digit hex color (e.g. #4A90D9)")
return v
class VlanOut(VlanCreate):
id: int
model_config = {"from_attributes": True}
@router.get("/", response_model=List[VlanOut])
def list_vlans(db: Session = Depends(get_db)):
return db.query(models.Vlan).order_by(nullsfirst(models.Vlan.vlan_id)).all()
@router.post("/", response_model=VlanOut)
def create_vlan(vlan: VlanCreate, db: Session = Depends(get_db)):
if vlan.vlan_id is not None:
existing = db.query(models.Vlan).filter(models.Vlan.vlan_id == vlan.vlan_id).first()
if existing:
raise HTTPException(status_code=400, detail=f"VLAN {vlan.vlan_id} existe déjà")
db_vlan = models.Vlan(**vlan.model_dump())
db.add(db_vlan)
db.commit()
db.refresh(db_vlan)
return db_vlan
@router.put("/{vlan_pk}", response_model=VlanOut)
def update_vlan(vlan_pk: int, vlan: VlanCreate, db: Session = Depends(get_db)):
db_vlan = db.query(models.Vlan).filter(models.Vlan.id == vlan_pk).first()
if not db_vlan:
raise HTTPException(status_code=404, detail="VLAN introuvable")
for k, v in vlan.model_dump().items():
setattr(db_vlan, k, v)
db.commit()
db.refresh(db_vlan)
return db_vlan
@router.delete("/{vlan_pk}")
def delete_vlan(vlan_pk: int, db: Session = Depends(get_db)):
db_vlan = db.query(models.Vlan).filter(models.Vlan.id == vlan_pk).first()
if not db_vlan:
raise HTTPException(status_code=404, detail="VLAN introuvable")
db.delete(db_vlan)
db.commit()
return {"ok": True}