"""Update management API endpoints. Refactored to use UpdateService for all business logic. Endpoints are now thin adapters that convert HTTP requests/responses. """ from typing import Optional from fastapi import APIRouter, HTTPException from pydantic import BaseModel from ..services.container import container from ..managers.ble_manager import get_ble_manager, NotificationType router = APIRouter() class UpdateCheckResponse(BaseModel): """Response model for update check.""" update_available: bool current_version: str latest_version: Optional[str] = None release_date: Optional[str] = None changelog: Optional[str] = None is_prerelease: bool = False download_size: Optional[int] = None message: Optional[str] = None class UpdateProgressResponse(BaseModel): """Response model for update progress.""" status: str current_version: str available_version: Optional[str] = None download_progress: float = 0.0 error_message: Optional[str] = None last_check: Optional[str] = None class ReleaseNotesRequest(BaseModel): """Request model for release notes.""" version: Optional[str] = None def _service_error_to_http_status(error_code: str) -> int: """Map service error codes to HTTP status codes. Args: error_code: Service error code Returns: HTTP status code """ codes = { "update_check_error": 500, "no_update_available": 400, "download_failed": 500, "update_download_error": 500, "no_update_downloaded": 400, "installation_failed": 500, "update_install_error": 500, "progress_error": 500, "release_notes_error": 500, "check_download_error": 500, "full_update_error": 500, } return codes.get(error_code, 500) @router.get("/check", response_model=UpdateCheckResponse) async def check_for_updates(): """Check for available updates. Uses UpdateService for business logic. """ result = await container.update_service.check_for_updates() if not result.success: raise HTTPException( status_code=_service_error_to_http_status(result.error.code), detail=result.error.message ) # Send BLE notification if update is available if result.data.get("update_available"): try: ble_manager = get_ble_manager() await ble_manager.send_notification( NotificationType.UPDATE_AVAILABLE, f"Update available: v{result.data['latest_version']}", {"version": result.data["latest_version"]} ) except Exception: # BLE notification failure shouldn't affect the response pass return UpdateCheckResponse(**result.data) @router.get("/progress", response_model=UpdateProgressResponse) async def get_update_progress(): """Get current update progress. Uses UpdateService for business logic. """ result = await container.update_service.get_progress() if not result.success: raise HTTPException( status_code=_service_error_to_http_status(result.error.code), detail=result.error.message ) return UpdateProgressResponse(**result.data) @router.post("/download") async def download_update(): """Download the available update. Uses UpdateService for business logic. """ result = await container.update_service.download_update() if not result.success: raise HTTPException( status_code=_service_error_to_http_status(result.error.code), detail=result.error.message ) return {"message": result.data["message"]} @router.post("/install") async def install_update(): """Install the downloaded update. Uses UpdateService for business logic. """ result = await container.update_service.install_update() if not result.success: raise HTTPException( status_code=_service_error_to_http_status(result.error.code), detail=result.error.message ) # Send BLE notification try: ble_manager = get_ble_manager() await ble_manager.send_notification( NotificationType.UPDATE_COMPLETE, "Update installed successfully", {"restart_required": True} ) except Exception: # BLE notification failure shouldn't affect the response pass return { "message": result.data["message"], "restart_required": result.data.get("requires_restart", True) } @router.post("/release-notes", response_model=dict) async def get_release_notes(request: ReleaseNotesRequest): """Get release notes for a specific version. Uses UpdateService for business logic. Args: request: Version to get notes for (latest if not specified) """ result = await container.update_service.get_release_notes(request.version) if not result.success: raise HTTPException( status_code=_service_error_to_http_status(result.error.code), detail=result.error.message ) return { "version": result.data["version"], "notes": result.data["release_notes"] } @router.get("/current-version") async def get_current_version(): """Get current system version. Uses UpdateService for business logic. """ result = await container.update_service.get_progress() if not result.success: raise HTTPException( status_code=_service_error_to_http_status(result.error.code), detail=result.error.message ) return { "version": result.data["current_version"], "last_check": result.data["last_check"] }