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>
152 lines
12 KiB
Markdown
152 lines
12 KiB
Markdown
# Story 6.3: Redis Configuration
|
||
|
||
Status: done
|
||
|
||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||
|
||
## Story
|
||
|
||
As a **system**,
|
||
I want **Redis configured for rate limiting and caching**,
|
||
so that **the application scales**.
|
||
|
||
## Acceptance Criteria
|
||
|
||
1. **Given** Redis container is running
|
||
**When** the application starts
|
||
**Then** Redis connection is established
|
||
**And** rate limiting middleware uses Redis
|
||
**And** translation progress is cached in Redis
|
||
**And** Redis data is persisted to volume
|
||
|
||
## Tasks / Subtasks
|
||
|
||
- [x] Task 1 (AC: #1) — Connexion Redis au démarrage
|
||
- [x] 1.1 Vérifier que REDIS_URL est lu depuis l'environnement et qu'un client Redis (sync ou async) est initialisé au démarrage de l'app.
|
||
- [x] 1.2 S'assurer que l'échec de connexion Redis est géré proprement (démarrage sans Redis en dev optionnel, ou fail-fast en prod selon convention projet).
|
||
- [x] Task 2 (AC: #1) — Rate limiting via Redis
|
||
- [x] 2.1 Le middleware de rate limiting (par IP et/ou par utilisateur) utilise Redis pour les compteurs (sliding window ou équivalent) afin que la limite soit partagée entre instances.
|
||
- [x] 2.2 Documenter ou adapter le middleware existant (rate_limiting.py et tier_quota) pour qu'au moins le quota par tier (daily) et idéalement les limites par fenêtre utilisent Redis.
|
||
- [x] Task 3 (AC: #1) — Cache de progression traduction
|
||
- [x] 3.1 La progression des traductions (status, progress_percent, current_step) est lue/écrite via Redis pour permettre le polling multi-instance et la persistance courte durée.
|
||
- [x] 3.2 Vérifier que le storage_tracker / métadonnées jobs utilisent bien Redis (déjà le cas) et que l'endpoint GET /api/v1/translations/{id} s'appuie sur ce stockage.
|
||
- [x] Task 4 (AC: #1) — Persistance Redis
|
||
- [x] 4.1 En environnement Docker (dev et prod), Redis est lancé avec persistance (appendonly yes) et volume dédié (déjà en place dans docker-compose.dev.yml et docker-compose.yml).
|
||
- [x] 4.2 Documenter dans .env.example ou README que REDIS_URL est requis pour le rate limiting et le cache de progression en production.
|
||
|
||
## Dev Notes
|
||
|
||
- **Contexte** : Les stories 6-1 et 6-2 ont livré Docker Compose dev et prod avec un service Redis (redis:7-alpine, volume, healthcheck). Le backend utilise déjà REDIS_URL à plusieurs endroits : tier_quota (quota quotidien par user), storage_tracker (métadonnées fichiers/jobs), auth blocklist (JWT révoqués), admin sessions, health check. Le middleware `rate_limiting.py` (limites par IP) est actuellement **en mémoire** (SlidingWindowCounter, TokenBucket) ; les AC exigent que le rate limiting middleware utilise Redis pour la scalabilité.
|
||
- **Architecture** : [Source: _bmad-output/planning-artifacts/architecture.md] — Rate Limiting Redis + sliding window ; Translation → Redis (rate limiting, progress). Health check format : `redis: connected` dans GET /health.
|
||
- **NFR10** : REDIS_URL via variables d'environnement uniquement.
|
||
- **Fichiers à toucher** : configuration Redis centralisée (core/config ou équivalent), `middleware/rate_limiting.py` (option Redis backend), `middleware/tier_quota.py` (déjà Redis), `services/storage_tracker.py` (déjà Redis), `main.py` (health check Redis), `.env.example` / README.
|
||
|
||
### Project Structure Notes
|
||
|
||
- Racine : `office_translator/` avec `main.py`, `middleware/`, `services/`, `routes/`. Redis utilisé depuis plusieurs modules ; viser un client partagé (singleton) pour éviter trop de connexions.
|
||
- Docker : `docker-compose.yml` et `docker-compose.dev.yml` définissent déjà le service `redis` avec volume et `redis-server --appendonly yes`.
|
||
|
||
### References
|
||
|
||
- [Source: _bmad-output/planning-artifacts/architecture.md] — Infrastructure & Deployment, Rate Limiting, Health Check Format.
|
||
- [Source: _bmad-output/planning-artifacts/epics.md] — Epic 6, Story 6.3, AC BDD.
|
||
- [Source: _bmad-output/implementation-artifacts/6-2-docker-compose-production.md] — Redis service prod, REDIS_URL, healthcheck.
|
||
|
||
### Technical Requirements (Architecture Compliance)
|
||
|
||
- **Connexion** : Un seul client Redis async partagé (ou sync selon usage) recommandé pour tier_quota, storage_tracker, et si possible rate_limiting ; initialisation au démarrage ou lazy avec gestion d'erreur claire.
|
||
- **Rate limiting** : Utiliser Redis pour stocker les fenêtres (sliding window) par clé (ex. `rate_limit:ip:{ip}:minute`). Si le middleware actuel est en mémoire, ajouter un backend Redis optionnel et l'activer quand REDIS_URL est défini (comportement existant dans tier_quota et storage_tracker).
|
||
- **Progress** : Les métadonnées de job (id, status, progress_percent, current_step) doivent être lisibles via Redis pour GET /api/v1/translations/{id} (déjà le cas si storage_tracker est utilisé pour ces infos).
|
||
- **Persistance** : Redis avec `appendonly yes` et volume monté (déjà en place dans Compose) ; pas de changement nécessaire si déjà configuré.
|
||
- **Health check** : GET /health doit inclure un champ `redis` avec status "healthy" / "unhealthy" / "not_configured" (déjà présent dans main.py).
|
||
|
||
### Architecture Compliance
|
||
|
||
- Alignement avec architecture.md : Redis pour rate limiting distribué et cache (progress). Pas de nouveau service ; configuration et usage cohérents.
|
||
- Respect de la structure projet : middleware/, services/, core/ si config centralisée.
|
||
|
||
### Library / Framework Requirements
|
||
|
||
- **redis** (Python) : utiliser `redis` (sync) et `redis.asyncio` (async) selon le module. Version compatible Python 3.11+ (redis >= 4.5 pour asyncio). Déjà présent dans le projet (tier_quota, storage_tracker, auth_service, admin_routes, main.py).
|
||
- Pas de nouvelle dépendance si redis est déjà dans requirements.txt ou pyproject.toml ; sinon l’ajouter.
|
||
|
||
### File Structure Requirements
|
||
|
||
- Ne pas dupliquer la logique de connexion Redis : centraliser dans un module (ex. `core/redis.py` ou réutiliser le pattern `_get_async_redis()` depuis un seul module importé par les autres).
|
||
- `.env.example` : REDIS_URL documenté (ex. `redis://redis:6379/0` pour Docker, `redis://localhost:6379/0` pour dev local).
|
||
|
||
### Testing Requirements
|
||
|
||
- Vérifier que lorsque REDIS_URL est défini, l’application se connecte à Redis et que GET /health renvoie `redis: healthy`.
|
||
- Vérifier que le rate limiting (par user/tier ou par IP si migré) utilise Redis : deux requêtes depuis deux processus/instances différentes partagent la même limite.
|
||
- Vérifier que la progression d’une traduction (GET /api/v1/translations/{id}) est bien lue depuis Redis après un enregistrement par le worker.
|
||
- Vérifier qu’en Docker, le volume Redis persiste les données après redémarrage du conteneur redis.
|
||
|
||
### Previous Story Intelligence (6-2 Docker Compose Production)
|
||
|
||
- **Fichiers créés/modifiés** : `docker-compose.yml`, `.env.example`, `scripts/verify-prod-compose.sh`. Redis service déjà défini avec healthcheck, volume `redis_data`, command `redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru`.
|
||
- **Patterns établis** : backend dépend de redis avec `condition: service_healthy` ; REDIS_URL=redis://redis:6379/0 dans environment backend.
|
||
- **Différence clé pour 6-3** : pas de changement Compose nécessaire pour Redis ; focus sur l’application (connexion, middleware, cache progress, documentation).
|
||
|
||
### Git Intelligence Summary
|
||
|
||
- Codebase existante utilise déjà Redis dans plusieurs services (tier_quota, storage_tracker, auth blocklist, admin sessions, health). La story 6-3 formalise et complète la configuration : s’assurer que le rate limiting (middleware IP) utilise Redis si pas déjà le cas, et que toute la doc/config est cohérente.
|
||
|
||
### Latest Tech Information
|
||
|
||
- redis-py 5.x : API stable, redis.asyncio recommandé pour FastAPI. Compatible Redis 6/7. Pas de breaking change majeur pour from_url, ping(), setex, get, etc.
|
||
- Redis 7 : appendonly yes et volume suffisent pour la persistance ; allkeys-lru avec maxmemory pour éviter saturation.
|
||
|
||
### Project Context Reference
|
||
|
||
- Aucun fichier `project-context.md` trouvé. Contexte métier et stack : architecture.md et epics.md.
|
||
|
||
## Dev Agent Record
|
||
|
||
### Agent Model Used
|
||
|
||
(dev-story workflow)
|
||
|
||
### Debug Log References
|
||
|
||
-
|
||
|
||
### Completion Notes List
|
||
|
||
- **Task 1**: Added `core/redis.py` with get_redis_url(), get_async_redis(), get_sync_redis(), ping_sync(). Refactored storage_tracker, tier_quota, main.py health/ready, admin_routes, auth_service to use shared clients. Connection failure handled gracefully (None when REDIS_URL missing or connection fails).
|
||
- **Task 2**: Rate limiting middleware uses Redis when REDIS_URL is set: sliding-window counters (minute/hour/day, trans_minute/trans_hour) stored in Redis (key pattern rate_limit:ip:{client_id}:{window}). Fallback to in-memory when Redis unavailable. tier_quota already used Redis.
|
||
- **Task 3**: Translation job status cached in Redis: core.redis set_job_status_async/get_job_status_async (key translation:job:{job_id}). Job created/updated in translate_routes triggers Redis write; background task syncs job to Redis every 0.5s until completed/failed. GET /api/v1/translations/{id} reads from Redis first, then falls back to _translation_jobs.
|
||
- **Task 4**: Docker Compose dev/prod already use Redis with appendonly yes and volume. .env.example updated with Redis section stating REDIS_URL required for rate limiting and translation progress cache in production.
|
||
- **Code Review (AI)** : GET /download/{id} résout le job via Redis (get_job_status_async) puis _translation_jobs pour cohérence multi-instance. Rate limiting Redis : application de max_total_size_per_hour_mb (clé rate_limit:size_hour:{client_id}:{hour_ts}). core/redis : socket_connect_timeout=5 pour le client sync, logs sur set_job_status_async/get_job_status_async. Test ping_sync succès ajouté. File List complétée avec fichiers modifiés non listés initialement.
|
||
|
||
### File List
|
||
|
||
- core/__init__.py (créé)
|
||
- core/redis.py (créé — timeout sync, logs job status)
|
||
- services/storage_tracker.py (modifié — utilise core.redis.get_async_redis)
|
||
- middleware/tier_quota.py (modifié — utilise core.redis.get_async_redis)
|
||
- main.py (modifié — health/ready utilisent core.redis.ping_sync)
|
||
- routes/admin_routes.py (modifié — get_redis_client utilise core.redis.get_sync_redis)
|
||
- services/auth_service.py (modifié — _get_blocklist_redis utilise core.redis.get_sync_redis)
|
||
- middleware/rate_limiting.py (modifié — backend Redis sliding window + size_hour; quand REDIS_URL défini)
|
||
- routes/translate_routes.py (modifié — set_job_status_async/get_job_status_async, GET status from Redis, GET download résout job via Redis)
|
||
- .env.example (modifié — section Redis)
|
||
- tests/test_core_redis.py (créé — tests unitaires core.redis + ping_sync success)
|
||
- _bmad-output/implementation-artifacts/6-3-redis-configuration.md (modifié — statut, tâches, Dev Agent Record)
|
||
- _bmad-output/implementation-artifacts/sprint-status.yaml (modifié — 6-3 → done)
|
||
- alembic/env.py (modifié — non listé initialement, ajouté en revue)
|
||
- docker-compose.yml (modifié — non listé initialement, ajouté en revue)
|
||
- docker-compose.dev.yml (modifié — non listé initialement, ajouté en revue)
|
||
- .env.production (modifié — non listé initialement, ajouté en revue)
|
||
- scripts/verify-dev-compose.sh (créé — non listé initialement, ajouté en revue)
|
||
|
||
### Senior Developer Review (AI)
|
||
|
||
- **Date** : 2026-03-10. **Issues trouvés** : 2 critiques, 3 haute, 3 moyenne, 2 basse. **Écarts Git vs File List** : 5.
|
||
- **Correctifs appliqués** : GET /download/{id} résout le job via Redis puis mémoire ; rate limiting Redis applique max_total_size_per_hour_mb ; core/redis timeout + logs ; test ping_sync success ; File List complétée.
|
||
- **Statut** : done (tous les correctifs HIGH/MEDIUM appliqués).
|
||
|
||
### Change Log
|
||
|
||
- 2026-03-10 — Code review (AI) : correctifs appliqués, story passée en done, sprint-status synced.
|