feat(ansible): Ajoute les tâches cron pour recherche et sauvegarde

📝 style(templates): Crée un template de mail pour notifications de sauvegarde

🐛 fix(.gitignore): Exclut les fichiers de sauvegarde .zip

🎨 style(dashboard): Crée une page pour la gestion des sauvegardes

 feat(command): Implémente la commande de sauvegarde avec notification et rétention

🎨 style(dashboard): Améliore l'interface de recherche avec des effets visuels

 feat(dashboard): Ajoute une page pour la gestion des sauvegardes

 test(controller): Ajoute la logique de téléchargement et suppression des sauvegardes
This commit is contained in:
Serreau Jovann
2026-01-16 11:04:13 +01:00
parent dde4ec4217
commit 540bdc1d07
13 changed files with 724 additions and 57 deletions

View File

@@ -0,0 +1,121 @@
{% extends 'dashboard/base.twig' %}
{% block title %}Sauvegardes{% endblock %}
{% block title_header %}Gestion des Sauvegardes{% endblock %}
{% block actions %}
<div class="px-5 py-2 bg-blue-600/10 border border-blue-500/30 text-blue-400 text-[10px] font-bold uppercase tracking-widest rounded-full shadow-lg shadow-blue-500/10">
Rétention : 7 Jours
</div>
{% endblock %}
{% block body %}
<div class="relative min-h-[60vh]">
{# Orbes de lumière en arrière-plan pour l'effet de profondeur #}
<div class="absolute -top-20 -left-10 w-64 h-64 bg-blue-600/10 rounded-full blur-[100px] -z-10 pointer-events-none"></div>
<div class="absolute bottom-10 right-0 w-80 h-80 bg-indigo-600/5 rounded-full blur-[120px] -z-10 pointer-events-none"></div>
<div class="space-y-8 relative z-10">
<h2 class="text-2xl font-black text-white uppercase tracking-tighter">
Historique des <span class="text-blue-600">Sauvegardes</span>
</h2>
{# Conteneur principal Glassmorphism Sombre #}
<div class="backdrop-blur-xl bg-[#1e293b]/40 border border-white/5 rounded-[2.5rem] shadow-2xl overflow-hidden">
<table class="w-full text-left border-collapse">
<thead>
<tr class="bg-slate-900/50 border-b border-white/5">
<th class="p-6 text-[10px] font-bold uppercase tracking-[0.2em] text-slate-500">Identité de la Sauvegarde</th>
<th class="p-6 text-[10px] font-bold uppercase tracking-[0.2em] text-slate-500 text-center">État</th>
<th class="p-6 text-[10px] font-bold uppercase tracking-[0.2em] text-slate-500 text-center">Taille</th>
<th class="p-6 text-[10px] font-bold uppercase tracking-[0.2em] text-slate-500 text-right">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-white/5">
{% for backup in backups %}
<tr class="hover:bg-white/[0.02] transition-all duration-300 group">
{# Identité #}
<td class="p-6">
<div class="flex items-center space-x-4">
<div class="w-12 h-12 rounded-2xl bg-blue-600/10 flex items-center justify-center text-blue-500 border border-blue-500/20 group-hover:scale-110 transition-transform duration-500">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4"></path>
</svg>
</div>
<div>
<p class="text-sm font-bold text-white tracking-tight">Base de données SQL</p>
<p class="text-[11px] text-slate-500 font-medium">Réalisé le {{ backup.createdAt|date('d/m/Y') }} à {{ backup.createdAt|date('H:i') }}</p>
</div>
</div>
</td>
{# Statut en Français #}
<td class="p-6 text-center">
<div class="flex justify-center">
{% if backup.status == 'SUCCESS' %}
<div class="inline-flex items-center px-4 py-1.5 bg-emerald-500/10 border border-emerald-500/20 rounded-full backdrop-blur-md">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500 mr-2 animate-pulse"></span>
<span class="text-[9px] font-black text-emerald-500 uppercase tracking-widest leading-none">Saine</span>
</div>
{% else %}
<div class="inline-flex items-center px-4 py-1.5 bg-red-500/10 border border-red-500/20 rounded-full backdrop-blur-md">
<span class="w-1.5 h-1.5 rounded-full bg-red-500 mr-2"></span>
<span class="text-[9px] font-black text-red-500 uppercase tracking-widest leading-none">Échec</span>
</div>
{% endif %}
</div>
</td>
{# Taille #}
<td class="p-6 text-center">
<span class="text-xs font-bold text-slate-300 font-mono bg-black/20 px-3 py-1 rounded-lg border border-white/5">
{{ backup.sizeMb|default('0.00') }} <span class="text-[9px] opacity-40 uppercase tracking-tighter">MO</span>
</span>
</td>
{# Actions #}
<td class="p-6 text-right">
<div class="flex items-center justify-end space-x-2">
{% if backup.status == 'SUCCESS' %}
<a href="{{ path('app_crm_backup_download', {id: backup.id}) }}"
download="sauvegarde.zip"
data-turbo="false"
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all shadow-lg shadow-blue-600/20 group/btn">
<svg class="w-4 h-4 mr-2 group-hover/btn:-translate-y-0.5 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
<span class="text-[10px] font-black uppercase tracking-widest">Télécharger</span>
</a>
{% endif %}
{% if is_granted('ROLE_ROOT') %}
<a href="{{ path('app_crm_backup_delete', {id: backup.id}) }}"
onclick="return confirm('Supprimer définitivement cette sauvegarde ?')"
class="inline-flex items-center justify-center w-10 h-10 bg-white/5 border border-white/10 rounded-xl text-slate-400 hover:bg-red-500 hover:text-white hover:border-red-500 transition-all">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
</a>
{% endif %}
</div>
</td>
</tr>
{% else %}
<tr>
<td colspan="4" class="p-20 text-center">
<div class="flex flex-col items-center opacity-30 text-white">
<svg class="w-16 h-16 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"></path>
</svg>
<p class="text-[10px] font-black uppercase tracking-[0.3em]">Historique vide</p>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}