146 lines
5.0 KiB
Python
146 lines
5.0 KiB
Python
"""
|
|
Routes API pour la gestion des playbooks.
|
|
"""
|
|
|
|
import re
|
|
from datetime import datetime, timezone
|
|
|
|
import yaml
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
from app.core.dependencies import verify_api_key
|
|
from app.schemas.ansible import PlaybookContentRequest
|
|
from app.schemas.common import LogEntry
|
|
from app.services import ansible_service, db
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/{filename}/content")
|
|
async def get_playbook_content(
|
|
filename: str,
|
|
api_key_valid: bool = Depends(verify_api_key)
|
|
):
|
|
"""Récupère le contenu d'un playbook."""
|
|
playbook_path = ansible_service.playbooks_dir / filename
|
|
|
|
if not filename.endswith(('.yml', '.yaml')):
|
|
raise HTTPException(status_code=400, detail="Extension de fichier invalide. Utilisez .yml ou .yaml")
|
|
|
|
if not playbook_path.exists():
|
|
raise HTTPException(status_code=404, detail=f"Playbook non trouvé: {filename}")
|
|
|
|
# Vérifier que le fichier est bien dans le répertoire playbooks (sécurité)
|
|
try:
|
|
playbook_path.resolve().relative_to(ansible_service.playbooks_dir.resolve())
|
|
except ValueError:
|
|
raise HTTPException(status_code=403, detail="Accès non autorisé")
|
|
|
|
try:
|
|
content = playbook_path.read_text(encoding='utf-8')
|
|
stat = playbook_path.stat()
|
|
return {
|
|
"filename": filename,
|
|
"content": content,
|
|
"size": stat.st_size,
|
|
"modified": datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).isoformat()
|
|
}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Erreur lecture fichier: {str(e)}")
|
|
|
|
|
|
@router.put("/{filename}/content")
|
|
async def save_playbook_content(
|
|
filename: str,
|
|
request: PlaybookContentRequest,
|
|
api_key_valid: bool = Depends(verify_api_key)
|
|
):
|
|
"""Sauvegarde le contenu d'un playbook."""
|
|
if not filename.endswith(('.yml', '.yaml')):
|
|
raise HTTPException(status_code=400, detail="Extension de fichier invalide. Utilisez .yml ou .yaml")
|
|
|
|
# Valider le nom de fichier (sécurité)
|
|
if not re.match(r'^[a-zA-Z0-9_-]+\.(yml|yaml)$', filename):
|
|
raise HTTPException(status_code=400, detail="Nom de fichier invalide")
|
|
|
|
playbook_path = ansible_service.playbooks_dir / filename
|
|
|
|
# S'assurer que le répertoire existe
|
|
ansible_service.playbooks_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Valider le contenu YAML
|
|
try:
|
|
parsed = yaml.safe_load(request.content)
|
|
if parsed is None:
|
|
raise HTTPException(status_code=400, detail="Contenu YAML vide ou invalide")
|
|
except yaml.YAMLError as e:
|
|
raise HTTPException(status_code=400, detail=f"Erreur de syntaxe YAML: {str(e)}")
|
|
|
|
is_new = not playbook_path.exists()
|
|
|
|
try:
|
|
playbook_path.write_text(request.content, encoding='utf-8')
|
|
stat = playbook_path.stat()
|
|
|
|
# Log l'action
|
|
action = "créé" if is_new else "modifié"
|
|
log_entry = LogEntry(
|
|
id=db.get_next_id("logs"),
|
|
timestamp=datetime.now(timezone.utc),
|
|
level="INFO",
|
|
message=f"Playbook {filename} {action}",
|
|
source="playbook_editor"
|
|
)
|
|
db.logs.insert(0, log_entry)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": f"Playbook {filename} {'créé' if is_new else 'sauvegardé'} avec succès",
|
|
"filename": filename,
|
|
"size": stat.st_size,
|
|
"modified": datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).isoformat(),
|
|
"is_new": is_new
|
|
}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Erreur sauvegarde fichier: {str(e)}")
|
|
|
|
|
|
@router.delete("/{filename}")
|
|
async def delete_playbook(
|
|
filename: str,
|
|
api_key_valid: bool = Depends(verify_api_key)
|
|
):
|
|
"""Supprime un playbook."""
|
|
if not filename.endswith(('.yml', '.yaml')):
|
|
raise HTTPException(status_code=400, detail="Extension de fichier invalide")
|
|
|
|
playbook_path = ansible_service.playbooks_dir / filename
|
|
|
|
if not playbook_path.exists():
|
|
raise HTTPException(status_code=404, detail=f"Playbook non trouvé: {filename}")
|
|
|
|
# Vérifier que le fichier est bien dans le répertoire playbooks (sécurité)
|
|
try:
|
|
playbook_path.resolve().relative_to(ansible_service.playbooks_dir.resolve())
|
|
except ValueError:
|
|
raise HTTPException(status_code=403, detail="Accès non autorisé")
|
|
|
|
try:
|
|
playbook_path.unlink()
|
|
|
|
log_entry = LogEntry(
|
|
id=db.get_next_id("logs"),
|
|
timestamp=datetime.now(timezone.utc),
|
|
level="WARN",
|
|
message=f"Playbook {filename} supprimé",
|
|
source="playbook_editor"
|
|
)
|
|
db.logs.insert(0, log_entry)
|
|
|
|
return {
|
|
"success": True,
|
|
"message": f"Playbook {filename} supprimé avec succès"
|
|
}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Erreur suppression fichier: {str(e)}")
|