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>
352 lines
13 KiB
Markdown
352 lines
13 KiB
Markdown
# 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
|
|
|
|
- [x] **Task 1: Créer le hook useSystemPage** (AC: #6, #8)
|
|
- [x] 1.1 Créer `frontend/src/app/admin/system/useSystemPage.ts`
|
|
- [x] 1.2 Combiner `useAdminDashboard` et `useCleanup` en un seul hook
|
|
- [x] 1.3 Exposer: data, isLoading, isPurging, triggerCleanup, purgeResult, error
|
|
- [x] 1.4 Gérer le refresh automatique après cleanup
|
|
|
|
- [x] **Task 2: Créer le composant CleanupSection** (AC: #2, #3, #4, #5, #7)
|
|
- [x] 2.1 Créer `frontend/src/app/admin/system/CleanupSection.tsx`
|
|
- [x] 2.2 Card avec icône Trash2, titre "Fichiers Temporaires"
|
|
- [x] 2.3 Afficher le nombre de fichiers trackés (tracked_files_count)
|
|
- [x] 2.4 Bouton "Nettoyer les fichiers orphelins" avec état loading
|
|
- [x] 2.5 Afficher le résultat après cleanup: "X fichiers supprimés"
|
|
- [x] 2.6 Désactiver le bouton pendant l'exécution (isPurging)
|
|
- [x] 2.7 Désactiver si tracked_files_count === 0
|
|
|
|
- [x] **Task 3: Créer le composant DiskSpaceCard** (AC: #1)
|
|
- [x] 3.1 Créer `frontend/src/app/admin/system/DiskSpaceCard.tsx`
|
|
- [x] 3.2 Afficher l'espace disque utilisé (used_percent)
|
|
- [x] 3.3 Afficher l'espace total et libre (total_gb, free_gb)
|
|
- [x] 3.4 Progress bar pour visualiser l'utilisation
|
|
- [x] 3.5 Réutiliser le pattern de SystemHealthCards.tsx
|
|
|
|
- [x] **Task 4: Modifier la page system/page.tsx** (AC: #1, #6)
|
|
- [x] 4.1 Importer useSystemPage hook
|
|
- [x] 4.2 Remplacer le placeholder par le contenu réel
|
|
- [x] 4.3 Header avec titre "Système" et description
|
|
- [x] 4.4 Layout en grille: DiskSpaceCard + CleanupSection
|
|
- [x] 4.5 Section ProviderStatus (réutiliser depuis admin/page.tsx)
|
|
- [x] 4.6 Gestion d'erreur globale avec message en français
|
|
|
|
- [x] **Task 5: Tests et validation** (AC: Tous)
|
|
- [x] 5.1 `npm run build` → 0 erreurs TypeScript
|
|
- [x] 5.2 Tester avec backend actif → cleanup fonctionne
|
|
- [x] 5.3 Tester états: loading, success, error
|
|
- [x] 5.4 Vérifier que le bouton est désactivé pendant l'exécution
|
|
- [x] 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
|
|
|
|
```json
|
|
{
|
|
"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)
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
```tsx
|
|
<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é:
|
|
```tsx
|
|
"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
|
|
|
|
- [x] `useCleanup.ts` et `useAdminDashboard.ts` existent et fonctionnent
|
|
- [x] Composants shadcn/ui disponibles (Card, Button, Progress)
|
|
- [x] Backend endpoint `/api/v1/admin/cleanup/trigger` opérationnel
|
|
- [x] 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é.
|