Files
office_translator/_bmad-output/implementation-artifacts/5-6-admin-manual-file-cleanup.md
Sepehr Ramezani 26bd096a06 feat: production deployment - full update with providers, admin, glossaries, pricing, tests
Major changes across backend, frontend, infrastructure:
- Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud)
- Admin panel: user management, pricing, settings
- Glossary system with CSV import/export
- Subscription and tier quota management
- Security hardening (rate limiting, API key auth, path traversal fixes)
- Docker compose for dev, prod, and IONOS deployment
- Alembic migrations for new tables
- Frontend: dashboard, pricing page, landing page, i18n (en/fr)
- Test suite and verification scripts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-25 15:01:47 +02:00

13 KiB

Story 5.6: admin-manual-file-cleanup

Status: done

Story

En tant qu'Admin, Je veux déclencher le nettoyage manuel des fichiers temporaires orphelins, Afin de libérer de l'espace disque sur le serveur.

Acceptance Criteria

  1. Page dédiée: Le bouton de cleanup se trouve sur /admin/system (pas sur le dashboard principal)
  2. Bouton Cleanup: Un bouton "Cleanup orphaned files" est visible et cliquable
  3. Exécution immédiate: Au clic, le job de cleanup s'exécute immédiatement via POST /api/v1/admin/cleanup/trigger
  4. Résultat affiché: Après exécution, afficher "X fichiers supprimés" (ex: "5 fichiers supprimés")
  5. Bouton désactivé: Le bouton est désactivé pendant l'exécution du cleanup (état loading)
  6. Gestion d'erreurs: En cas d'erreur, afficher un message utilisateur clair en français
  7. Feedback visuel: Loader/spinner pendant l'exécution
  8. Refresh automatique: Les stats du dashboard sont rafraîchies après un cleanup réussi

Tasks / Subtasks

  • Task 1: Créer le hook useSystemPage (AC: #6, #8)

    • 1.1 Créer frontend/src/app/admin/system/useSystemPage.ts
    • 1.2 Combiner useAdminDashboard et useCleanup en un seul hook
    • 1.3 Exposer: data, isLoading, isPurging, triggerCleanup, purgeResult, error
    • 1.4 Gérer le refresh automatique après cleanup
  • Task 2: Créer le composant CleanupSection (AC: #2, #3, #4, #5, #7)

    • 2.1 Créer frontend/src/app/admin/system/CleanupSection.tsx
    • 2.2 Card avec icône Trash2, titre "Fichiers Temporaires"
    • 2.3 Afficher le nombre de fichiers trackés (tracked_files_count)
    • 2.4 Bouton "Nettoyer les fichiers orphelins" avec état loading
    • 2.5 Afficher le résultat après cleanup: "X fichiers supprimés"
    • 2.6 Désactiver le bouton pendant l'exécution (isPurging)
    • 2.7 Désactiver si tracked_files_count === 0
  • Task 3: Créer le composant DiskSpaceCard (AC: #1)

    • 3.1 Créer frontend/src/app/admin/system/DiskSpaceCard.tsx
    • 3.2 Afficher l'espace disque utilisé (used_percent)
    • 3.3 Afficher l'espace total et libre (total_gb, free_gb)
    • 3.4 Progress bar pour visualiser l'utilisation
    • 3.5 Réutiliser le pattern de SystemHealthCards.tsx
  • Task 4: Modifier la page system/page.tsx (AC: #1, #6)

    • 4.1 Importer useSystemPage hook
    • 4.2 Remplacer le placeholder par le contenu réel
    • 4.3 Header avec titre "Système" et description
    • 4.4 Layout en grille: DiskSpaceCard + CleanupSection
    • 4.5 Section ProviderStatus (réutiliser depuis admin/page.tsx)
    • 4.6 Gestion d'erreur globale avec message en français
  • Task 5: Tests et validation (AC: Tous)

    • 5.1 npm run build → 0 erreurs TypeScript
    • 5.2 Tester avec backend actif → cleanup fonctionne
    • 5.3 Tester états: loading, success, error
    • 5.4 Vérifier que le bouton est désactivé pendant l'exécution
    • 5.5 Vérifier le refresh des stats après cleanup

Dev Notes

🏗️ Stack Technique

Technologie Version
Next.js 16.0.6 (App Router)
React 19.2.0
TanStack Query v5
Tailwind CSS configuré
shadcn/ui Card, Button, Progress
Lucide React Trash2, HardDrive, Loader2, AlertCircle

📁 Structure Cible (Colocation Pattern)

frontend/src/app/admin/system/
├── page.tsx                # ⭐ Page principale (modifier)
├── useSystemPage.ts        # ⭐ Nouveau: Hook combiné
├── CleanupSection.tsx      # ⭐ Nouveau: Section cleanup
├── DiskSpaceCard.tsx       # ⭐ Nouveau: Card espace disque
└── types.ts                # Optionnel: Types spécifiques

⚠️ Règle absolue (architecture.md):

🚨 FICHIERS SPÉCIAUX: page.tsx, layout.tsx → TOUJOURS minuscules
🚨 COLOCATION: Components/hooks/types dans le dossier de leur page

🔗 API Endpoints

Endpoint Méthode Description
/api/v1/admin/dashboard GET Stats système (disk, cleanup, providers)
/api/v1/admin/cleanup/trigger POST Déclencher le cleanup manuel

📊 Response Format: POST /api/v1/admin/cleanup/trigger

{
  "status": "success",
  "files_cleaned": 5,
  "message": "Cleaned up 5 expired files"
}

⚠️ Note: Le backend ne retourne pas actuellement les MB libérés. L'UI affichera uniquement "X fichiers supprimés". Une amélioration backend future pourrait ajouter bytes_freed à la réponse.

📊 Response Format: GET /api/v1/admin/dashboard (section cleanup)

{
  "cleanup": {
    "files_cleaned": 42,
    "tracked_files_count": 5
  },
  "system": {
    "disk": {
      "used_percent": 45.2,
      "total_gb": 500,
      "free_gb": 274
    }
  }
}

🎨 Patterns UI à Réutiliser

CleanupSection.tsx - Section de cleanup:

<Card className="py-0">
  <CardContent className="flex items-center gap-3 px-4 py-3">
    <div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-red-500/10">
      <Trash2 className="size-4 text-red-500" />
    </div>
    <div className="flex flex-1 flex-col gap-0.5">
      <span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
        Fichiers Temporaires
      </span>
      <span className="text-sm font-semibold tabular-nums text-foreground">
        {trackedFilesCount} fichiers orphelins
      </span>
      {purgeResult && (
        <span className="text-[10px] text-green-500">
          {purgeResult.files_cleaned} fichiers supprimés
        </span>
      )}
    </div>
    <Button
      variant="outline"
      size="sm"
      className="h-7 shrink-0 gap-1.5 border-red-200/30 text-red-500 hover:bg-red-500/10"
      onClick={onCleanup}
      disabled={isPurging || trackedFilesCount === 0}
    >
      {isPurging ? (
        <Loader2 className="size-3 animate-spin" />
      ) : (
        <Trash2 className="size-3" />
      )}
      {isPurging ? "Nettoyage..." : trackedFilesCount === 0 ? "Propre" : "Nettoyer"}
    </Button>
  </CardContent>
</Card>

useSystemPage.ts - Hook combiné:

"use client";

import { useAdminDashboard } from "../useAdminDashboard";
import { useCleanup } from "../useCleanup";

export function useSystemPage() {
  const { data, isLoading, error, refetch } = useAdminDashboard();
  const { isPurging, purgeResult, triggerCleanup, error: cleanupError } = useCleanup();

  const handleCleanup = async () => {
    await triggerCleanup();
    refetch(); // Refresh stats after cleanup
  };

  return {
    data,
    isLoading,
    error: error || cleanupError,
    isPurging,
    purgeResult,
    handleCleanup,
  };
}

🔄 Flux de données

User click "Nettoyer"
    ↓
handleCleanup()
    ↓
triggerCleanup() → POST /api/v1/admin/cleanup/trigger
    ↓ (isPurging = true, button disabled)
Response: { files_cleaned: 5 }
    ↓
refetch() → GET /api/v1/admin/dashboard
    ↓ (isPurging = false, button enabled)
UI: "5 fichiers supprimés"

⚠️ Points d'Attention Critiques

  1. Authentification: Tous les appels API doivent inclure le header Authorization: Bearer {adminToken}. Le token est stocké dans useTranslationStore().settings.adminToken.

  2. Réutiliser les hooks existants:

    • useCleanup existe déjà dans admin/useCleanup.ts
    • useAdminDashboard existe déjà dans admin/useAdminDashboard.ts
    • Ne PAS recréer ces hooks, les réutiliser
  3. Ne PAS dupliquer SystemHealthCards: Le composant SystemHealthCards.tsx est utilisé sur le dashboard principal. Pour /admin/system, créer des composants dédiés plus détaillés.

  4. Gestion des erreurs: Mapper les erreurs API vers des messages utilisateur en français:

    • AUTH_REQUIRED → "Veuillez vous connecter"
    • UNAUTHORIZED → "Session expirée"
    • HTTP_ERROR_403 → "Accès refusé"
  5. État du bouton: Le bouton doit être désactivé dans deux cas:

    • isPurging === true (cleanup en cours)
    • trackedFilesCount === 0 (pas de fichiers à nettoyer)
  6. ProviderStatus: Réutiliser le composant ProviderStatus.tsx existant pour afficher le statut des providers.

📋 Checklist de Validation Avant Dev

  • useCleanup.ts et useAdminDashboard.ts existent et fonctionnent
  • Composants shadcn/ui disponibles (Card, Button, Progress)
  • Backend endpoint /api/v1/admin/cleanup/trigger opérationnel
  • Pattern d'authentification admin identifié (même que Story 5.3)

🚀 Integration Steps

  1. Créer useSystemPage.ts - Hook combinant dashboard et cleanup
  2. Créer CleanupSection.tsx - Composant de cleanup dédié
  3. Créer DiskSpaceCard.tsx - Card espace disque détaillée
  4. Modifier system/page.tsx - Intégrer tous les composants
  5. Tester avec backend actif

📚 Previous Story Intelligence (Story 5.3, 5.4, 5.5)

Learnings from previous Admin stories:

  • Utiliser TanStack Query useQuery pour le data fetching
  • Le token admin est dans useTranslationStore().settings.adminToken
  • Utiliser le pattern de colocation (components/hooks/types dans le dossier admin/system/)
  • Les composants shadcn/ui sont disponibles: Card, Button, Progress, Badge, Tooltip
  • Mapper les erreurs API vers des messages utilisateur en français
  • Structure des fichiers: page.tsx (minuscule!), composants PascalCase dans le même dossier

Files created in Story 5.5 for reference:

  • frontend/src/app/admin/useTranslationStats.ts - pattern useQuery à suivre
  • frontend/src/app/admin/StatsOverview.tsx - pattern cards stats à suivre
  • frontend/src/app/admin/types.ts - interfaces TypeScript

Existing files to reuse:

  • frontend/src/app/admin/useCleanup.ts - hook mutation cleanup
  • frontend/src/app/admin/useAdminDashboard.ts - hook data fetching
  • frontend/src/app/admin/ProviderStatus.tsx - composant statut providers
  • frontend/src/app/admin/types.ts - interfaces CleanupResponse, AdminDashboardData

🔍 Git Intelligence (Derniers commits)

3d37ce4 feat: Update Docker and Kubernetes for database infrastructure
550f351 feat: Add PostgreSQL database infrastructure
c4d6cae Production-ready improvements: security hardening, Redis sessions

Insights:

  • Infrastructure base de données récemment mise à jour
  • Redis configuré pour les sessions
  • Focus sur la sécurité et la robustesse

References

  • [Source: _bmad-output/planning-artifacts/epics.md#Story-5.6] — Story requirements
  • [Source: _bmad-output/planning-artifacts/architecture.md#Frontend-Architecture] — Colocation pattern
  • [Source: _bmad-output/planning-artifacts/architecture.md#API-Response-Formats] — Response format {data, meta}
  • [Source: routes/admin_routes.py#L396-L411] — Backend cleanup endpoint
  • [Source: frontend/src/app/admin/useCleanup.ts] — Hook cleanup existant
  • [Source: frontend/src/app/admin/SystemHealthCards.tsx] — Pattern UI cards existant
  • [Source: frontend/src/app/admin/page.tsx] — Dashboard principal (référence)

Dev Agent Record

Agent Model Used

GLM-5 (zai-coding-plan/glm-5)

Debug Log References

Aucun problème rencontré lors de l'implémentation.

Completion Notes List

  • Task 1: Hook useSystemPage créé - combine useAdminDashboard et useCleanup avec refresh automatique
  • Task 2: Composant CleanupSection créé avec bouton, états loading, affichage résultat
  • Task 3: Composant DiskSpaceCard créé avec progress bar et infos disque
  • Task 4: Page /admin/system mise à jour avec layout grille et gestion d'erreurs
  • Task 5: Build TypeScript réussi - 0 erreurs

Implementation Summary:

  • Page /admin/system maintenant fonctionnelle avec:
    • Affichage espace disque (used%, total, free)
    • Section cleanup avec bouton "Nettoyer"
    • États loading/success/error gérés
    • Refresh automatique après cleanup
    • Réutilisation du composant ProviderStatus existant

File List

Nouveaux fichiers:

  • frontend/src/app/admin/system/useSystemPage.ts
  • frontend/src/app/admin/system/CleanupSection.tsx
  • frontend/src/app/admin/system/DiskSpaceCard.tsx
  • frontend/src/app/admin/system/page.tsx

Senior Developer Review (AI)

Date: 2026-02-24
Reviewer: GLM-5 (Code Review Workflow)

Issues Found: 2 HIGH, 3 MEDIUM, 2 LOW

# Severity Issue File Status
1 HIGH Double-fetch inutile - refetch() redondant car useCleanup invalide déjà le cache useSystemPage.ts:10-12 Fixed
2 HIGH Race condition - refetch() s'exécute même si triggerCleanup() échoue useSystemPage.ts:10-13 Fixed
3 MEDIUM File List imprécis - page.tsx est un nouveau fichier, pas modifié Story file Fixed
4 MEDIUM Export inutilisé - refetch exporté mais jamais utilisé useSystemPage.ts:22 Fixed
5 MEDIUM Console warnings - baseline-browser-mapping outdated package.json Skipped (minor)
6 LOW Hardcoded strings sans i18n page.tsx Skipped (future)
7 LOW Type import inutilisé AdminDashboardData CleanupSection.tsx:6 Fixed

Fixes Applied

  1. useSystemPage.ts: Simplifié handleCleanup() - suppression du refetch() redondant et de l'export inutilisé
  2. CleanupSection.tsx: Supprimé l'import AdminDashboardData inutilisé
  3. Story File List: Corrigé - page.tsx marqué comme nouveau fichier

Verdict

APPROVED - Toutes les issues HIGH et MEDIUM critiques ont été corrigées. Code review terminé.