""" Tools Controller Provides utility tool endpoints including: - DNS lookups - Environment data (weather, forecast, sun times, air quality) """ from typing import Dict, Optional from fastapi import APIRouter, Depends, HTTPException, status from src.controllers.base import BaseController from src.logging_config import get_logger from src.dns.schemas import DNSLookupRequest, DNSLookupResponse from src.dns.service import DNSService from src.dns.exceptions import DNSQueryError from src.domains.tools.environment.schemas import EnvironmentResponse from src.domains.tools.environment.service import get_environment_service from src.auth.oidc import get_optional_user logger = get_logger(__name__) class ToolsController(BaseController): """ Controller for utility tools Provides endpoints for: - DNS lookups """ def __init__(self): super().__init__(prefix="/tools", tags=["Tools"]) self.dns_service = DNSService() self.environment_service = get_environment_service() def create_router(self) -> APIRouter: """Create and configure the router""" router = APIRouter(prefix=self.prefix, tags=self.tags) @router.post( "/dns/lookup", response_model=DNSLookupResponse, status_code=status.HTTP_200_OK, summary="Perform DNS lookup", description=""" Perform DNS lookups for various record types. Uses dnspython for reliable DNS queries with support for multiple record types and custom nameservers. Perfect for troubleshooting DNS issues and checking domain configurations. **Supported Record Types:** - A: IPv4 address records - AAAA: IPv6 address records - MX: Mail exchange records - TXT: Text records (SPF, DKIM, etc.) - CNAME: Canonical name records - NS: Nameserver records - SOA: Start of authority records - PTR: Pointer records (reverse DNS) - CAA: Certification authority authorization - SRV: Service records **Features:** - Custom nameserver support (e.g., 8.8.8.8, 1.1.1.1) - Query time measurement - Detailed error messages **Rate Limiting:** None (internal network use only) """ ) async def dns_lookup(request: DNSLookupRequest) -> DNSLookupResponse: """ Perform DNS lookup for a domain Args: request: DNS lookup request with domain, record type, and optional nameserver Returns: DNS lookup results with records and metadata Raises: HTTPException: 400 for invalid queries, 500 for processing errors """ try: logger.info(f"Received DNS lookup request for: {request.domain} ({request.record_type})") result = await self.dns_service.lookup(request) return result except DNSQueryError as e: logger.warning(f"DNS query error: {str(e)}") raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"DNS query failed: {str(e)}" ) except Exception as e: logger.error(f"Unexpected error during DNS lookup: {str(e)}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected error occurred during DNS lookup" ) @router.get( "/environment", response_model=EnvironmentResponse, status_code=status.HTTP_200_OK, summary="Get environment data", description=""" Fetch current environment data including weather, forecast, sun times, and optionally air quality. Data is retrieved from the user's volatile Qdrant collection which is populated by background data collectors. **Data Sources:** - Weather: Current temperature, conditions, humidity, wind - Forecast: Multi-day weather outlook - Sun Times: Sunrise, sunset, daylight duration - Air Quality: AQI and pollutant levels (when available) **Authentication:** - Uses authenticated user's `preferred_username` if available - Falls back to 'default' for unauthenticated requests """ ) async def get_environment( user: Optional[Dict] = Depends(get_optional_user), ) -> EnvironmentResponse: """ Get current environment data. Args: user: Optional authenticated user info Returns: Environment data with weather, forecast, sun times, and air quality """ try: # Determine user identifier user_id = "default" if user: logger.debug(f"User claims: {user}") user_id = user.get("preferred_username") or user.get("sub", "default") logger.info(f"Fetching environment data for user: {user_id} (preferred_username={user.get('preferred_username')}, sub={user.get('sub')})") else: logger.info(f"Fetching environment data for user: {user_id} (no auth)") result = await self.environment_service.get_current(user_id) return result except Exception as e: logger.error(f"Error fetching environment data: {str(e)}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to fetch environment data" ) return router # Create controller instance tools_controller = ToolsController()