""" Infrastructure API router for ClaudeTools. This module defines all REST API endpoints for managing infrastructure assets, 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.infrastructure import ( InfrastructureCreate, InfrastructureResponse, InfrastructureUpdate, ) from api.services import infrastructure_service # Create router with prefix and tags router = APIRouter() @router.get( "", response_model=dict, summary="List all infrastructure items", description="Retrieve a paginated list of all infrastructure items with optional filtering", status_code=status.HTTP_200_OK, ) def list_infrastructure( 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 infrastructure items with pagination. - **skip**: Number of items to skip (default: 0) - **limit**: Maximum number of items to return (default: 100, max: 1000) Returns a list of infrastructure items with pagination metadata. **Example Request:** ``` GET /api/infrastructure?skip=0&limit=50 Authorization: Bearer ``` **Example Response:** ```json { "total": 10, "skip": 0, "limit": 50, "infrastructure": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "hostname": "server-dc-01", "asset_type": "domain_controller", "client_id": "client-uuid", "site_id": "site-uuid", "status": "active", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ] } ``` """ try: items, total = infrastructure_service.get_infrastructure_items(db, skip, limit) return { "total": total, "skip": skip, "limit": limit, "infrastructure": [InfrastructureResponse.model_validate(item) for item in items] } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to retrieve infrastructure items: {str(e)}" ) @router.get( "/{infrastructure_id}", response_model=InfrastructureResponse, summary="Get infrastructure by ID", description="Retrieve a single infrastructure item by its unique identifier", status_code=status.HTTP_200_OK, responses={ 200: { "description": "Infrastructure item found and returned", "model": InfrastructureResponse, }, 404: { "description": "Infrastructure item not found", "content": { "application/json": { "example": {"detail": "Infrastructure with ID 123e4567-e89b-12d3-a456-426614174000 not found"} } }, }, }, ) def get_infrastructure( infrastructure_id: UUID, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Get a specific infrastructure item by ID. - **infrastructure_id**: UUID of the infrastructure item to retrieve Returns the complete infrastructure item details. **Example Request:** ``` GET /api/infrastructure/123e4567-e89b-12d3-a456-426614174000 Authorization: Bearer ``` **Example Response:** ```json { "id": "123e4567-e89b-12d3-a456-426614174000", "hostname": "server-dc-01", "asset_type": "domain_controller", "client_id": "client-uuid", "site_id": "site-uuid", "ip_address": "192.168.1.10", "mac_address": "00:1A:2B:3C:4D:5E", "os": "Windows Server 2022", "os_version": "21H2", "role_description": "Primary domain controller for the network", "status": "active", "has_gui": true, "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ``` """ item = infrastructure_service.get_infrastructure_by_id(db, infrastructure_id) return InfrastructureResponse.model_validate(item) @router.post( "", response_model=InfrastructureResponse, summary="Create new infrastructure item", description="Create a new infrastructure item with the provided details", status_code=status.HTTP_201_CREATED, responses={ 201: { "description": "Infrastructure item created successfully", "model": InfrastructureResponse, }, 422: { "description": "Validation error or invalid foreign key", "content": { "application/json": { "example": { "detail": [ { "loc": ["body", "hostname"], "msg": "field required", "type": "value_error.missing" } ] } } }, }, }, ) def create_infrastructure( infrastructure_data: InfrastructureCreate, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Create a new infrastructure item. Requires a valid JWT token with appropriate permissions. Validates foreign keys (client_id, site_id, parent_host_id) before creation. **Example Request:** ```json POST /api/infrastructure Authorization: Bearer Content-Type: application/json { "hostname": "server-dc-01", "asset_type": "domain_controller", "client_id": "client-uuid", "site_id": "site-uuid", "ip_address": "192.168.1.10", "mac_address": "00:1A:2B:3C:4D:5E", "os": "Windows Server 2022", "os_version": "21H2", "role_description": "Primary domain controller", "status": "active", "powershell_version": "5.1", "shell_type": "powershell", "has_gui": true } ``` **Example Response:** ```json { "id": "123e4567-e89b-12d3-a456-426614174000", "hostname": "server-dc-01", "asset_type": "domain_controller", "status": "active", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ``` """ item = infrastructure_service.create_infrastructure(db, infrastructure_data) return InfrastructureResponse.model_validate(item) @router.put( "/{infrastructure_id}", response_model=InfrastructureResponse, summary="Update infrastructure item", description="Update an existing infrastructure item's details", status_code=status.HTTP_200_OK, responses={ 200: { "description": "Infrastructure item updated successfully", "model": InfrastructureResponse, }, 404: { "description": "Infrastructure item not found", "content": { "application/json": { "example": {"detail": "Infrastructure with ID 123e4567-e89b-12d3-a456-426614174000 not found"} } }, }, 422: { "description": "Validation error or invalid foreign key", "content": { "application/json": { "example": {"detail": "Client with ID client-uuid not found"} } }, }, }, ) def update_infrastructure( infrastructure_id: UUID, infrastructure_data: InfrastructureUpdate, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Update an existing infrastructure item. - **infrastructure_id**: UUID of the infrastructure item to update Only provided fields will be updated. All fields are optional. Validates foreign keys (client_id, site_id, parent_host_id) before updating. **Example Request:** ```json PUT /api/infrastructure/123e4567-e89b-12d3-a456-426614174000 Authorization: Bearer Content-Type: application/json { "status": "decommissioned", "notes": "Server retired and replaced with new hardware" } ``` **Example Response:** ```json { "id": "123e4567-e89b-12d3-a456-426614174000", "hostname": "server-dc-01", "asset_type": "domain_controller", "status": "decommissioned", "notes": "Server retired and replaced with new hardware", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T14:20:00Z" } ``` """ item = infrastructure_service.update_infrastructure(db, infrastructure_id, infrastructure_data) return InfrastructureResponse.model_validate(item) @router.delete( "/{infrastructure_id}", response_model=dict, summary="Delete infrastructure item", description="Delete an infrastructure item by its ID", status_code=status.HTTP_200_OK, responses={ 200: { "description": "Infrastructure item deleted successfully", "content": { "application/json": { "example": { "message": "Infrastructure deleted successfully", "infrastructure_id": "123e4567-e89b-12d3-a456-426614174000" } } }, }, 404: { "description": "Infrastructure item not found", "content": { "application/json": { "example": {"detail": "Infrastructure with ID 123e4567-e89b-12d3-a456-426614174000 not found"} } }, }, }, ) def delete_infrastructure( infrastructure_id: UUID, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Delete an infrastructure item. - **infrastructure_id**: UUID of the infrastructure item to delete This is a permanent operation and cannot be undone. **Example Request:** ``` DELETE /api/infrastructure/123e4567-e89b-12d3-a456-426614174000 Authorization: Bearer ``` **Example Response:** ```json { "message": "Infrastructure deleted successfully", "infrastructure_id": "123e4567-e89b-12d3-a456-426614174000" } ``` """ return infrastructure_service.delete_infrastructure(db, infrastructure_id) @router.get( "/by-site/{site_id}", response_model=dict, summary="Get infrastructure by site", description="Retrieve all infrastructure items for a specific site", status_code=status.HTTP_200_OK, responses={ 200: { "description": "Infrastructure items for site returned", "content": { "application/json": { "example": { "total": 5, "skip": 0, "limit": 100, "infrastructure": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "hostname": "server-dc-01", "asset_type": "domain_controller" } ] } } }, }, }, ) def get_infrastructure_by_site( site_id: str, 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 infrastructure items for a specific site. - **site_id**: UUID of the site - **skip**: Number of items to skip (default: 0) - **limit**: Maximum number of items to return (default: 100, max: 1000) Returns infrastructure items associated with the specified site. **Example Request:** ``` GET /api/infrastructure/by-site/site-uuid-here?skip=0&limit=50 Authorization: Bearer ``` **Example Response:** ```json { "total": 5, "skip": 0, "limit": 50, "infrastructure": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "hostname": "server-dc-01", "asset_type": "domain_controller", "site_id": "site-uuid-here", "status": "active" } ] } ``` """ try: items, total = infrastructure_service.get_infrastructure_by_site(db, site_id, skip, limit) return { "total": total, "skip": skip, "limit": limit, "infrastructure": [InfrastructureResponse.model_validate(item) for item in items] } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to retrieve infrastructure items for site: {str(e)}" ) @router.get( "/by-client/{client_id}", response_model=dict, summary="Get infrastructure by client", description="Retrieve all infrastructure items for a specific client", status_code=status.HTTP_200_OK, responses={ 200: { "description": "Infrastructure items for client returned", "content": { "application/json": { "example": { "total": 15, "skip": 0, "limit": 100, "infrastructure": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "hostname": "server-dc-01", "asset_type": "domain_controller" } ] } } }, }, }, ) def get_infrastructure_by_client( client_id: str, 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 infrastructure items for a specific client. - **client_id**: UUID of the client - **skip**: Number of items to skip (default: 0) - **limit**: Maximum number of items to return (default: 100, max: 1000) Returns infrastructure items associated with the specified client. **Example Request:** ``` GET /api/infrastructure/by-client/client-uuid-here?skip=0&limit=50 Authorization: Bearer ``` **Example Response:** ```json { "total": 15, "skip": 0, "limit": 50, "infrastructure": [ { "id": "123e4567-e89b-12d3-a456-426614174000", "hostname": "server-dc-01", "asset_type": "domain_controller", "client_id": "client-uuid-here", "status": "active" } ] } ``` """ try: items, total = infrastructure_service.get_infrastructure_by_client(db, client_id, skip, limit) return { "total": total, "skip": skip, "limit": limit, "infrastructure": [InfrastructureResponse.model_validate(item) for item in items] } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to retrieve infrastructure items for client: {str(e)}" )