""" Network API router for ClaudeTools. This module defines all REST API endpoints for managing networks, including CRUD operations with proper authentication, validation, and error handling. """ from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.orm import Session from api.database import get_db from api.middleware.auth import get_current_user from api.schemas.network import ( NetworkCreate, NetworkResponse, NetworkUpdate, ) from api.services import network_service # Create router with prefix and tags router = APIRouter() @router.get( "", response_model=dict, summary="List all networks", description="Retrieve a paginated list of all networks with optional filtering", status_code=status.HTTP_200_OK, ) def list_networks( skip: int = Query( default=0, ge=0, description="Number of records to skip for pagination" ), limit: int = Query( default=100, ge=1, le=1000, description="Maximum number of records to return (max 1000)" ), db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ List all networks with pagination. - **skip**: Number of networks to skip (default: 0) - **limit**: Maximum number of networks to return (default: 100, max: 1000) Returns a list of networks with pagination metadata. **Example Request:** ``` GET /api/networks?skip=0&limit=50 Authorization: Bearer ``` **Example Response:** ```json { "total": 5, "skip": 0, "limit": 50, "networks": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "client_id": "abc12345-6789-0def-1234-56789abcdef0", "site_id": "def12345-6789-0def-1234-56789abcdef0", "network_name": "Main LAN", "network_type": "lan", "cidr": "192.168.1.0/24", "gateway_ip": "192.168.1.1", "vlan_id": null, "notes": "Primary office network", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ] } ``` """ try: networks, total = network_service.get_networks(db, skip, limit) return { "total": total, "skip": skip, "limit": limit, "networks": [NetworkResponse.model_validate(network) for network in networks] } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to retrieve networks: {str(e)}" ) @router.get( "/by-site/{site_id}", response_model=dict, summary="Get networks by site", description="Retrieve all networks for a specific site with pagination", status_code=status.HTTP_200_OK, responses={ 200: { "description": "Networks found and returned", "content": { "application/json": { "example": { "total": 3, "skip": 0, "limit": 100, "networks": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "client_id": "abc12345-6789-0def-1234-56789abcdef0", "site_id": "def12345-6789-0def-1234-56789abcdef0", "network_name": "Main LAN", "network_type": "lan", "cidr": "192.168.1.0/24", "gateway_ip": "192.168.1.1", "vlan_id": None, "notes": "Primary office network", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ] } } } }, 404: { "description": "Site not found", "content": { "application/json": { "example": {"detail": "Site with ID def12345-6789-0def-1234-56789abcdef0 not found"} } }, }, }, ) def get_networks_by_site( site_id: UUID, skip: int = Query( default=0, ge=0, description="Number of records to skip for pagination" ), limit: int = Query( default=100, ge=1, le=1000, description="Maximum number of records to return (max 1000)" ), db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Get all networks for a specific site. - **site_id**: UUID of the site - **skip**: Number of networks to skip (default: 0) - **limit**: Maximum number of networks to return (default: 100, max: 1000) Returns a list of networks for the specified site with pagination metadata. **Example Request:** ``` GET /api/networks/by-site/def12345-6789-0def-1234-56789abcdef0?skip=0&limit=50 Authorization: Bearer ``` """ networks, total = network_service.get_networks_by_site(db, site_id, skip, limit) return { "total": total, "skip": skip, "limit": limit, "networks": [NetworkResponse.model_validate(network) for network in networks] } @router.get( "/{network_id}", response_model=NetworkResponse, summary="Get network by ID", description="Retrieve a single network by its unique identifier", status_code=status.HTTP_200_OK, responses={ 200: { "description": "Network found and returned", "model": NetworkResponse, }, 404: { "description": "Network not found", "content": { "application/json": { "example": {"detail": "Network with ID 123e4567-e89b-12d3-a456-426614174000 not found"} } }, }, }, ) def get_network( network_id: UUID, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Get a specific network by ID. - **network_id**: UUID of the network to retrieve Returns the complete network details. **Example Request:** ``` GET /api/networks/123e4567-e89b-12d3-a456-426614174000 Authorization: Bearer ``` **Example Response:** ```json { "id": "123e4567-e89b-12d3-a456-426614174000", "client_id": "abc12345-6789-0def-1234-56789abcdef0", "site_id": "def12345-6789-0def-1234-56789abcdef0", "network_name": "Main LAN", "network_type": "lan", "cidr": "192.168.1.0/24", "gateway_ip": "192.168.1.1", "vlan_id": null, "notes": "Primary office network", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ``` """ network = network_service.get_network_by_id(db, network_id) return NetworkResponse.model_validate(network) @router.post( "", response_model=NetworkResponse, summary="Create new network", description="Create a new network with the provided details", status_code=status.HTTP_201_CREATED, responses={ 201: { "description": "Network created successfully", "model": NetworkResponse, }, 404: { "description": "Site not found", "content": { "application/json": { "example": {"detail": "Site with ID def12345-6789-0def-1234-56789abcdef0 not found"} } }, }, 422: { "description": "Validation error", "content": { "application/json": { "example": { "detail": [ { "loc": ["body", "network_name"], "msg": "field required", "type": "value_error.missing" } ] } } }, }, }, ) def create_network( network_data: NetworkCreate, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Create a new network. Requires a valid JWT token with appropriate permissions. The site_id must reference an existing site if provided. **Example Request:** ```json POST /api/networks Authorization: Bearer Content-Type: application/json { "client_id": "abc12345-6789-0def-1234-56789abcdef0", "site_id": "def12345-6789-0def-1234-56789abcdef0", "network_name": "Main LAN", "network_type": "lan", "cidr": "192.168.1.0/24", "gateway_ip": "192.168.1.1", "vlan_id": null, "notes": "Primary office network" } ``` **Example Response:** ```json { "id": "123e4567-e89b-12d3-a456-426614174000", "client_id": "abc12345-6789-0def-1234-56789abcdef0", "site_id": "def12345-6789-0def-1234-56789abcdef0", "network_name": "Main LAN", "network_type": "lan", "cidr": "192.168.1.0/24", "gateway_ip": "192.168.1.1", "vlan_id": null, "notes": "Primary office network", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ``` """ network = network_service.create_network(db, network_data) return NetworkResponse.model_validate(network) @router.put( "/{network_id}", response_model=NetworkResponse, summary="Update network", description="Update an existing network's details", status_code=status.HTTP_200_OK, responses={ 200: { "description": "Network updated successfully", "model": NetworkResponse, }, 404: { "description": "Network or site not found", "content": { "application/json": { "example": {"detail": "Network with ID 123e4567-e89b-12d3-a456-426614174000 not found"} } }, }, }, ) def update_network( network_id: UUID, network_data: NetworkUpdate, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Update an existing network. - **network_id**: UUID of the network to update Only provided fields will be updated. All fields are optional. If updating site_id, the new site must exist. **Example Request:** ```json PUT /api/networks/123e4567-e89b-12d3-a456-426614174000 Authorization: Bearer Content-Type: application/json { "gateway_ip": "192.168.1.254", "notes": "Gateway IP updated for redundancy" } ``` **Example Response:** ```json { "id": "123e4567-e89b-12d3-a456-426614174000", "client_id": "abc12345-6789-0def-1234-56789abcdef0", "site_id": "def12345-6789-0def-1234-56789abcdef0", "network_name": "Main LAN", "network_type": "lan", "cidr": "192.168.1.0/24", "gateway_ip": "192.168.1.254", "vlan_id": null, "notes": "Gateway IP updated for redundancy", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T14:20:00Z" } ``` """ network = network_service.update_network(db, network_id, network_data) return NetworkResponse.model_validate(network) @router.delete( "/{network_id}", response_model=dict, summary="Delete network", description="Delete a network by its ID", status_code=status.HTTP_200_OK, responses={ 200: { "description": "Network deleted successfully", "content": { "application/json": { "example": { "message": "Network deleted successfully", "network_id": "123e4567-e89b-12d3-a456-426614174000" } } }, }, 404: { "description": "Network not found", "content": { "application/json": { "example": {"detail": "Network with ID 123e4567-e89b-12d3-a456-426614174000 not found"} } }, }, }, ) def delete_network( network_id: UUID, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Delete a network. - **network_id**: UUID of the network to delete This is a permanent operation and cannot be undone. **Example Request:** ``` DELETE /api/networks/123e4567-e89b-12d3-a456-426614174000 Authorization: Bearer ``` **Example Response:** ```json { "message": "Network deleted successfully", "network_id": "123e4567-e89b-12d3-a456-426614174000" } ``` """ return network_service.delete_network(db, network_id)