""" Service management API endpoints for WROLPi Controller. """ from typing import Optional, Union from fastapi import APIRouter, HTTPException, Query from controller.api.schemas import ( ServiceActionResponse, ServiceLogsResponse, ServiceStatusResponse, ServicesListErrorResponse, ) from controller.lib.config import is_docker_mode from controller.lib.docker_services import ( can_manage_containers, get_all_containers_status, get_container_logs, get_container_status, restart_container, start_container, stop_container, ) from controller.lib.systemd import ( disable_service, enable_service, get_all_services_status, get_service_logs, get_service_status, restart_service, start_service, stop_service, ) router = APIRouter(prefix="/api/services", tags=["services"]) @router.get( "", response_model=Union[list[ServiceStatusResponse], ServicesListErrorResponse], ) async def list_services() -> Union[list[ServiceStatusResponse], ServicesListErrorResponse]: """Get status of all managed services.""" if is_docker_mode(): if can_manage_containers(): statuses = get_all_containers_status() statuses = sorted(statuses, key=lambda s: s["name"]) return [ServiceStatusResponse(**s) for s in statuses if "error" not in s] else: return ServicesListErrorResponse( error="Docker management not available", reason="Docker socket not mounted or docker library not installed", ) else: statuses = get_all_services_status() statuses = sorted(statuses, key=lambda s: s["name"]) return [ServiceStatusResponse(**s) for s in statuses] @router.get("/{name}", response_model=ServiceStatusResponse) async def get_service(name: str) -> ServiceStatusResponse: """Get status of a specific service.""" if is_docker_mode(): status = get_container_status(name) else: status = get_service_status(name) if "error" in status and status.get("status") is None: raise HTTPException(status_code=404, detail=status["error"]) return ServiceStatusResponse(**status) @router.post("/{name}/start", response_model=ServiceActionResponse) async def service_start(name: str) -> ServiceActionResponse: """Start a service.""" if is_docker_mode(): result = start_container(name) else: result = start_service(name) if not result.get("success"): raise HTTPException(status_code=500, detail=result.get("error", "Failed to start")) return ServiceActionResponse(**result) @router.post("/{name}/stop", response_model=ServiceActionResponse) async def service_stop(name: str) -> ServiceActionResponse: """Stop a service.""" if is_docker_mode(): result = stop_container(name) else: result = stop_service(name) if not result.get("success"): raise HTTPException(status_code=500, detail=result.get("error", "Failed to stop")) return ServiceActionResponse(**result) @router.post("/{name}/restart", response_model=ServiceActionResponse) async def service_restart(name: str) -> ServiceActionResponse: """Restart a service.""" if is_docker_mode(): result = restart_container(name) else: result = restart_service(name) if not result.get("success"): raise HTTPException(status_code=500, detail=result.get("error", "Failed to restart")) return ServiceActionResponse(**result) @router.post("/{name}/enable", response_model=ServiceActionResponse) async def service_enable(name: str) -> ServiceActionResponse: """Enable a service to start at boot (systemd only).""" if is_docker_mode(): raise HTTPException( status_code=501, detail="Enable/disable not available in Docker mode" ) result = enable_service(name) if not result.get("success"): raise HTTPException(status_code=500, detail=result.get("error", "Failed")) return ServiceActionResponse(**result) @router.post("/{name}/disable", response_model=ServiceActionResponse) async def service_disable(name: str) -> ServiceActionResponse: """Disable a service from starting at boot (systemd only).""" if is_docker_mode(): raise HTTPException( status_code=501, detail="Enable/disable not available in Docker mode" ) result = disable_service(name) if not result.get("success"): raise HTTPException(status_code=500, detail=result.get("error", "Failed")) return ServiceActionResponse(**result) @router.get("/{name}/logs", response_model=ServiceLogsResponse) async def service_logs( name: str, lines: int = Query(default=100, ge=1, le=10000), since: Optional[str] = Query(default=None), ) -> ServiceLogsResponse: """Get service logs.""" if is_docker_mode(): result = get_container_logs(name, lines) else: result = get_service_logs(name, lines, since) if "error" in result: raise HTTPException(status_code=500, detail=result["error"]) return ServiceLogsResponse(**result)