from fastapi import APIRouter, Depends, HTTPException, status from typing import List from app.core.security import get_current_user, User from app.services.docker_service import DockerService, ContainerInfo from app.services.update_service import UpdateService, ImageInfo, ImageUpdate router = APIRouter() docker_service = DockerService() update_service = UpdateService() @router.get("/status") async def get_docker_status(current_user: User = Depends(get_current_user)): """Vérifie le statut de la connexion Docker""" return { "connected": docker_service.is_connected(), "message": "Docker est accessible" if docker_service.is_connected() else "Docker n'est pas accessible" } @router.get("/containers", response_model=List[ContainerInfo]) async def list_containers( all: bool = True, current_user: User = Depends(get_current_user) ): """Liste tous les conteneurs Docker""" if not docker_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) return docker_service.get_containers(all=all) @router.post("/containers/{container_id}/start") async def start_container( container_id: str, current_user: User = Depends(get_current_user) ): """Démarre un conteneur""" if not docker_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) success = docker_service.start_container(container_id) if not success: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Impossible de démarrer le conteneur" ) return {"status": "success", "message": f"Conteneur {container_id} démarré"} @router.post("/containers/{container_id}/stop") async def stop_container( container_id: str, current_user: User = Depends(get_current_user) ): """Arrête un conteneur""" if not docker_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) success = docker_service.stop_container(container_id) if not success: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Impossible d'arrêter le conteneur" ) return {"status": "success", "message": f"Conteneur {container_id} arrêté"} @router.post("/containers/{container_id}/restart") async def restart_container( container_id: str, current_user: User = Depends(get_current_user) ): """Redémarre un conteneur""" if not docker_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) success = docker_service.restart_container(container_id) if not success: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Impossible de redémarrer le conteneur" ) return {"status": "success", "message": f"Conteneur {container_id} redémarré"} @router.delete("/containers/{container_id}") async def delete_container( container_id: str, force: bool = False, current_user: User = Depends(get_current_user) ): """Supprime un conteneur""" if not docker_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) success = docker_service.remove_container(container_id, force=force) if not success: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Impossible de supprimer le conteneur" ) return {"status": "success", "message": f"Conteneur {container_id} supprimé"} # ===== ENDPOINTS DE MISE À JOUR ===== @router.get("/images", response_model=List[ImageInfo]) async def list_images(current_user: User = Depends(get_current_user)): """Liste toutes les images Docker locales""" if not update_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) return update_service.get_all_images_info() @router.get("/images/check-update/{image_name}") async def check_image_update( image_name: str, current_user: User = Depends(get_current_user) ): """Vérifie si une image a une mise à jour disponible""" if not update_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) update_info = update_service.check_image_updates(image_name) if not update_info: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Impossible de vérifier les mises à jour pour {image_name}" ) return update_info @router.get("/images/check-all-updates") async def check_all_updates(current_user: User = Depends(get_current_user)): """Vérifie les mises à jour de toutes les images en cours d'utilisation""" if not update_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) try: from app.services.docker_service import DockerService ds = DockerService() containers = ds.get_containers(all=False) # Conteneurs actifs uniquement updates = [] for container in containers: image_name = container.image update_info = update_service.check_image_updates(image_name) if update_info: updates.append({ "container": container.name, "update": update_info }) return { "total_containers": len(containers), "containers_with_updates": len(updates), "updates": updates } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Erreur lors de la vérification des mises à jour: {str(e)}" ) @router.post("/images/pull") async def pull_image( image: str, tag: str = "latest", current_user: User = Depends(get_current_user) ): """Télécharge une image Docker""" if not update_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) success = update_service.pull_image(image, tag) if not success: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Impossible de télécharger l'image {image}:{tag}" ) return { "status": "success", "message": f"Image {image}:{tag} téléchargée avec succès" } @router.post("/containers/{container_id}/update-image") async def update_container_image( container_id: str, new_image: str, new_tag: str = "latest", current_user: User = Depends(get_current_user) ): """Met à jour l'image d'un conteneur avec la dernière version Processus inspiré de TrueNAS Scale: 1. Arrête le conteneur 2. Télécharge la nouvelle image 3. Redémarre le conteneur """ if not update_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) result = update_service.update_container_image(container_id, new_image, new_tag) if not result.get("success"): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=result.get("message", "Erreur lors de la mise à jour") ) return result @router.post("/images/prune") async def prune_images( dangling_only: bool = True, current_user: User = Depends(get_current_user) ): """Nettoie les images inutilisées (similaire à TrueNAS Scale)""" if not update_service.is_connected(): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Docker n'est pas accessible" ) result = update_service.prune_unused_images(dangling_only=dangling_only) if not result.get("success"): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=result.get("message", "Erreur lors du nettoyage") ) return result