305 lines
9.9 KiB
Python
305 lines
9.9 KiB
Python
"""
|
|
Service de gestion de l'historique des commandes ad-hoc.
|
|
"""
|
|
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
from typing import List, Optional
|
|
|
|
from sqlalchemy import select, update, delete
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models.database import async_session_maker
|
|
from app.schemas.ansible import AdHocHistoryEntry, AdHocHistoryCategory
|
|
from app.core.constants import DEFAULT_ADHOC_CATEGORIES
|
|
|
|
|
|
class AdHocHistoryService:
|
|
"""Service pour gérer l'historique des commandes ad-hoc en base de données."""
|
|
|
|
def __init__(self):
|
|
self._default_categories_initialized = False
|
|
|
|
async def _ensure_default_categories(self, session: AsyncSession):
|
|
"""S'assure que les catégories par défaut existent."""
|
|
if self._default_categories_initialized:
|
|
return
|
|
|
|
from app.crud.log import LogRepository
|
|
repo = LogRepository(session)
|
|
|
|
# Vérifier si des catégories existent déjà
|
|
existing = await self.get_categories()
|
|
if not existing:
|
|
# Créer les catégories par défaut
|
|
for cat in DEFAULT_ADHOC_CATEGORIES:
|
|
await self._create_category_internal(
|
|
session,
|
|
name=cat["name"],
|
|
description=cat.get("description"),
|
|
color=cat.get("color", "#7c3aed"),
|
|
icon=cat.get("icon", "fa-folder")
|
|
)
|
|
await session.commit()
|
|
|
|
self._default_categories_initialized = True
|
|
|
|
async def _create_category_internal(
|
|
self,
|
|
session: AsyncSession,
|
|
name: str,
|
|
description: str = None,
|
|
color: str = "#7c3aed",
|
|
icon: str = "fa-folder"
|
|
):
|
|
"""Crée une catégorie en base (interne)."""
|
|
from app.models.log import Log
|
|
# Utiliser le modèle Log avec un type spécial pour stocker les catégories
|
|
log = Log(
|
|
level="ADHOC_CATEGORY",
|
|
message=name,
|
|
source=description or "",
|
|
host_id=None,
|
|
task_id=f"{color}|{icon}", # Stocker color et icon dans task_id
|
|
)
|
|
session.add(log)
|
|
|
|
async def add_command(
|
|
self,
|
|
command: str,
|
|
target: str,
|
|
module: str = "shell",
|
|
become: bool = False,
|
|
category: str = "default",
|
|
description: str = None
|
|
) -> AdHocHistoryEntry:
|
|
"""Ajoute une commande à l'historique."""
|
|
async with async_session_maker() as session:
|
|
await self._ensure_default_categories(session)
|
|
|
|
from app.models.log import Log
|
|
cmd_id = f"adhoc_{uuid.uuid4().hex[:12]}"
|
|
|
|
log = Log(
|
|
level="ADHOC_COMMAND",
|
|
message=command,
|
|
source=category,
|
|
host_id=target,
|
|
task_id=cmd_id,
|
|
)
|
|
session.add(log)
|
|
await session.commit()
|
|
|
|
return AdHocHistoryEntry(
|
|
id=cmd_id,
|
|
command=command,
|
|
target=target,
|
|
module=module,
|
|
become=become,
|
|
category=category,
|
|
description=description,
|
|
created_at=datetime.now(timezone.utc),
|
|
last_used=datetime.now(timezone.utc),
|
|
use_count=1
|
|
)
|
|
|
|
async def get_commands(
|
|
self,
|
|
category: str = None,
|
|
search: str = None,
|
|
limit: int = 50
|
|
) -> List[AdHocHistoryEntry]:
|
|
"""Récupère les commandes de l'historique."""
|
|
async with async_session_maker() as session:
|
|
from app.models.log import Log
|
|
|
|
stmt = select(Log).where(Log.level == "ADHOC_COMMAND")
|
|
|
|
if category and category != "all":
|
|
stmt = stmt.where(Log.source == category)
|
|
|
|
stmt = stmt.order_by(Log.created_at.desc()).limit(limit)
|
|
|
|
result = await session.execute(stmt)
|
|
logs = result.scalars().all()
|
|
|
|
commands = []
|
|
for log in logs:
|
|
if search and search.lower() not in log.message.lower():
|
|
continue
|
|
|
|
commands.append(AdHocHistoryEntry(
|
|
id=log.task_id or str(log.id),
|
|
command=log.message,
|
|
target=log.host_id or "all",
|
|
module="shell",
|
|
become=False,
|
|
category=log.source or "default",
|
|
created_at=log.created_at,
|
|
last_used=log.created_at,
|
|
use_count=1
|
|
))
|
|
|
|
return commands
|
|
|
|
async def update_command_category(
|
|
self,
|
|
command_id: str,
|
|
category: str,
|
|
description: str = None
|
|
) -> bool:
|
|
"""Met à jour la catégorie d'une commande."""
|
|
async with async_session_maker() as session:
|
|
from app.models.log import Log
|
|
|
|
stmt = (
|
|
update(Log)
|
|
.where(Log.task_id == command_id)
|
|
.where(Log.level == "ADHOC_COMMAND")
|
|
.values(source=category)
|
|
)
|
|
result = await session.execute(stmt)
|
|
await session.commit()
|
|
return result.rowcount > 0
|
|
|
|
async def delete_command(self, command_id: str) -> bool:
|
|
"""Supprime une commande de l'historique."""
|
|
async with async_session_maker() as session:
|
|
from app.models.log import Log
|
|
|
|
stmt = (
|
|
delete(Log)
|
|
.where(Log.task_id == command_id)
|
|
.where(Log.level == "ADHOC_COMMAND")
|
|
)
|
|
result = await session.execute(stmt)
|
|
await session.commit()
|
|
return result.rowcount > 0
|
|
|
|
async def get_categories(self) -> List[AdHocHistoryCategory]:
|
|
"""Récupère la liste des catégories."""
|
|
async with async_session_maker() as session:
|
|
from app.models.log import Log
|
|
|
|
stmt = select(Log).where(Log.level == "ADHOC_CATEGORY")
|
|
result = await session.execute(stmt)
|
|
logs = result.scalars().all()
|
|
|
|
if not logs:
|
|
# Retourner les catégories par défaut
|
|
return [
|
|
AdHocHistoryCategory(
|
|
name=cat["name"],
|
|
description=cat.get("description"),
|
|
color=cat.get("color", "#7c3aed"),
|
|
icon=cat.get("icon", "fa-folder")
|
|
)
|
|
for cat in DEFAULT_ADHOC_CATEGORIES
|
|
]
|
|
|
|
categories = []
|
|
for log in logs:
|
|
# Extraire color et icon depuis task_id
|
|
color, icon = "#7c3aed", "fa-folder"
|
|
if log.task_id and "|" in log.task_id:
|
|
parts = log.task_id.split("|", 1)
|
|
color = parts[0]
|
|
icon = parts[1] if len(parts) > 1 else "fa-folder"
|
|
|
|
categories.append(AdHocHistoryCategory(
|
|
name=log.message,
|
|
description=log.source or None,
|
|
color=color,
|
|
icon=icon
|
|
))
|
|
|
|
return categories
|
|
|
|
async def add_category(
|
|
self,
|
|
name: str,
|
|
description: str = None,
|
|
color: str = "#7c3aed",
|
|
icon: str = "fa-folder"
|
|
) -> AdHocHistoryCategory:
|
|
"""Ajoute une nouvelle catégorie."""
|
|
async with async_session_maker() as session:
|
|
await self._create_category_internal(session, name, description, color, icon)
|
|
await session.commit()
|
|
|
|
return AdHocHistoryCategory(
|
|
name=name,
|
|
description=description,
|
|
color=color,
|
|
icon=icon
|
|
)
|
|
|
|
async def update_category(
|
|
self,
|
|
old_name: str,
|
|
new_name: str,
|
|
description: str = None,
|
|
color: str = "#7c3aed",
|
|
icon: str = "fa-folder"
|
|
) -> bool:
|
|
"""Met à jour une catégorie existante."""
|
|
async with async_session_maker() as session:
|
|
from app.models.log import Log
|
|
|
|
# Mettre à jour la catégorie
|
|
stmt = (
|
|
update(Log)
|
|
.where(Log.message == old_name)
|
|
.where(Log.level == "ADHOC_CATEGORY")
|
|
.values(
|
|
message=new_name,
|
|
source=description or "",
|
|
task_id=f"{color}|{icon}"
|
|
)
|
|
)
|
|
result = await session.execute(stmt)
|
|
|
|
# Mettre à jour les commandes associées si le nom a changé
|
|
if old_name != new_name:
|
|
stmt2 = (
|
|
update(Log)
|
|
.where(Log.source == old_name)
|
|
.where(Log.level == "ADHOC_COMMAND")
|
|
.values(source=new_name)
|
|
)
|
|
await session.execute(stmt2)
|
|
|
|
await session.commit()
|
|
return result.rowcount > 0
|
|
|
|
async def delete_category(self, name: str) -> bool:
|
|
"""Supprime une catégorie et déplace ses commandes vers 'default'."""
|
|
if name == "default":
|
|
return False
|
|
|
|
async with async_session_maker() as session:
|
|
from app.models.log import Log
|
|
|
|
# Déplacer les commandes vers default
|
|
stmt1 = (
|
|
update(Log)
|
|
.where(Log.source == name)
|
|
.where(Log.level == "ADHOC_COMMAND")
|
|
.values(source="default")
|
|
)
|
|
await session.execute(stmt1)
|
|
|
|
# Supprimer la catégorie
|
|
stmt2 = (
|
|
delete(Log)
|
|
.where(Log.message == name)
|
|
.where(Log.level == "ADHOC_CATEGORY")
|
|
)
|
|
result = await session.execute(stmt2)
|
|
await session.commit()
|
|
return result.rowcount > 0
|
|
|
|
|
|
# Instance singleton du service
|
|
adhoc_history_service = AdHocHistoryService()
|