Files
office_translator/_bmad-output/implementation-artifacts/3-5-api-versioning-api-v1.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

383 lines
16 KiB
Markdown

# Story 3.5: API Versioning /api/v1/
Status: done
## Story
En tant que **Développeur**,
Je veux **que tous les endpoints API soient préfixés avec /api/v1/**,
de sorte que **les futures versions de l'API puissent coexister sans casser les clients existants**.
## Acceptance Criteria
1. **Préfixe Obligatoire**: Tous les endpoints API sont montés sous `/api/v1/` prefix. (FR33)
2. **Endpoints Non-Versionnés**: Les endpoints sans préfixe de version retournent 404 (sauf health check et documentation).
3. **Documentation OpenAPI**: La version est documentée dans la spécification OpenAPI (title, version, description).
4. **Rétrocompatibilité**: Les clients existants utilisant `/api/v1/*` ne sont pas affectés.
5. **Health Check Préservé**: `/health` et `/ready` restent accessibles sans préfixe pour les probes Kubernetes.
6. **Documentation Accessible**: `/docs` (Swagger UI) et `/redoc` restent accessibles sans préfixe.
## Tasks / Subtasks
- [x] **Task 1: Auditer les Endpoints Non-Versionnés** (AC: #1, #2)
- [x] 1.1 Lister tous les endpoints dans `main.py` qui ne sont pas sous `/api/v1/`
- [x] 1.2 Identifier les endpoints à migrer vs à préserver (health, docs)
- [x] 1.3 Documenter la liste des endpoints à déplacer
- [x] **Task 2: Créer un Routeur Racine API v1** (AC: #1)
- [x] 2.1 Créer `routes/api_v1_router.py` avec un routeur principal pour `/api/v1`
- [x] 2.2 Inclure tous les sous-routeurs (translate, auth, api-keys, admin)
- [x] 2.3 Configurer les tags OpenAPI pour la documentation
- [x] **Task 3: Migrer les Endpoints de main.py** (AC: #1, #2)
- [x] 3.1 Déplacer `/languages` vers `/api/v1/languages`
- [x] 3.2 Déplacer `/translate` vers `/api/v1/translate` (déjà fait via translate_routes.py)
- [x] 3.3 Déplacer `/translate-batch` vers `/api/v1/translate/batch`
- [x] 3.4 Déplacer `/download/{filename}` vers `/api/v1/download/{filename}`
- [x] 3.5 Déplacer `/extract-texts` vers `/api/v1/extract-texts`
- [x] 3.6 Déplacer `/reconstruct-document` vers `/api/v1/reconstruct-document`
- [x] 3.7 Déplacer `/ollama/models` vers `/api/v1/ollama/models`
- [x] 3.8 Déplacer `/ollama/configure` vers `/api/v1/ollama/configure`
- [x] 3.9 Déplacer `/rate-limit/status` vers `/api/v1/rate-limit/status`
- [x] 3.10 Déplacer `/admin/*` vers `/api/v1/admin/*`
- [x] 3.11 Déplacer `/metrics` vers `/api/v1/metrics`
- [x] **Task 4: Configurer les Exceptions de Versioning** (AC: #5, #6)
- [x] 4.1 Garder `/health` accessible sans préfixe (Kubernetes probe)
- [x] 4.2 Garder `/ready` accessible sans préfixe (Kubernetes probe)
- [x] 4.3 Garder `/docs` accessible sans préfixe (Swagger UI)
- [x] 4.4 Garder `/redoc` accessible sans préfixe (ReDoc)
- [x] 4.5 Garder `/openapi.json` accessible sans préfixe
- [x] 4.6 Optionnel: Garder `/` comme endpoint info avec redirect vers `/docs`
- [x] **Task 5: Mettre à jour la Documentation OpenAPI** (AC: #3)
- [x] 5.1 Vérifier que `config.API_VERSION` est utilisé dans FastAPI app
- [x] 5.2 Ajouter la description de versioning dans la description de l'API
- [x] 5.3 Documenter les endpoints versionnés dans les tags OpenAPI
- [x] **Task 6: Nettoyer main.py** (AC: #1, #2)
- [x] 6.1 Supprimer les endpoints déplacés de main.py
- [x] 6.2 Garder uniquement les endpoints exception (health, docs, root)
- [x] 6.3 Inclure le routeur API v1 principal
- [x] **Task 7: Ajouter les Tests** (AC: Tous)
- [x] 7.1 Test endpoint versionné accessible (`/api/v1/health` ou `/api/v1/languages`)
- [x] 7.2 Test endpoint non-versionné retourne 404 (sauf exceptions)
- [x] 7.3 Test health check accessible sans préfixe
- [x] 7.4 Test documentation accessible sans préfixe
- [x] 7.5 Test tous les endpoints migrés fonctionnent
## Dev Notes
### État Actuel du Versioning
**Endpoints DÉJÀ versionnés** (via routeurs):
- `POST /api/v1/translate` - translate_routes.py ✅
- `GET /api/v1/translations/{id}` - translate_routes.py ✅
- `GET /api/v1/download/{job_id}` - translate_routes.py ✅
- `POST /api/v1/auth/register` - auth_routes.py ✅
- `POST /api/v1/auth/login` - auth_routes.py ✅
- `POST /api/v1/api-keys` - api_key_routes.py ✅
- `GET /api/v1/api-keys` - api_key_routes.py ✅
- `DELETE /api/v1/api-keys/{key_id}` - api_key_routes.py ✅
- `DELETE /api/v1/admin/api-keys/{key_id}` - admin_routes.py ✅
**Endpoints NON versionnés** (dans main.py à migrer):
| Endpoint | Migration |
|----------|-----------|
| `/` | Garder ou redirect vers /docs |
| `/health` | **GARDER** (K8s probe) |
| `/ready` | **GARDER** (K8s probe) |
| `/languages` | → `/api/v1/languages` |
| `/translate` | → Supprimer (doublon avec /api/v1/translate) |
| `/translate-batch` | → `/api/v1/translate/batch` |
| `/download/{filename}` | → Supprimer (doublon avec /api/v1/download/{job_id}) |
| `/extract-texts` | → `/api/v1/extract-texts` |
| `/reconstruct-document` | → `/api/v1/reconstruct-document` |
| `/ollama/models` | → `/api/v1/ollama/models` |
| `/ollama/configure` | → `/api/v1/ollama/configure` |
| `/metrics` | → `/api/v1/metrics` |
| `/rate-limit/status` | → `/api/v1/rate-limit/status` |
| `/admin/login` | → `/api/v1/admin/login` |
| `/admin/logout` | → `/api/v1/admin/logout` |
| `/admin/verify` | → `/api/v1/admin/verify` |
| `/admin/dashboard` | → `/api/v1/admin/dashboard` |
| `/admin/users` | → `/api/v1/admin/users` |
| `/admin/users/{user_id}` | → `/api/v1/admin/users/{user_id}` |
| `/admin/stats` | → `/api/v1/admin/stats` |
| `/admin/cleanup/trigger` | → `/api/v1/admin/cleanup/trigger` |
| `/admin/files/tracked` | → `/api/v1/admin/files/tracked` |
| `/admin/config/provider` | → `/api/v1/admin/config/provider` |
| `/cleanup/{filename}` | → `/api/v1/cleanup/{filename}` |
### Architecture Proposée
```
main.py
├── /health (exception - K8s probe)
├── /ready (exception - K8s probe)
├── /docs (exception - Swagger UI)
├── /redoc (exception - ReDoc)
├── /openapi.json (exception - OpenAPI spec)
├── / (optionnel: redirect vers /docs)
└── include_router(api_v1_router) # Tout le reste sous /api/v1/
routes/
├── api_v1_router.py # NOUVEAU - Routeur principal /api/v1
│ ├── include_router(translate_v1_router)
│ ├── include_router(auth_v1_router)
│ ├── include_router(api_key_router)
│ ├── include_router(admin_v1_router) # NOUVEAU
│ └── include_router(legacy_router) # NOUVEAU
├── admin_routes.py # NOUVEAU - Endpoints admin versionnés
├── legacy_routes.py # NOUVEAU - Endpoints legacy versionnés
├── translate_routes.py # Existant
├── auth_routes.py # Existant
└── api_key_routes.py # Existant
```
### Patterns à Suivre
**Pattern de Routeur Principal**:
```python
# routes/api_v1_router.py
from fastapi import APIRouter
router = APIRouter(tags=["API v1"])
# Inclure les sous-routeurs
from routes.translate_routes import router_v1 as translate_router
from routes.auth_routes import router_v1 as auth_router
from routes.api_key_routes import router as api_key_router
router.include_router(translate_router, tags=["Translation"])
router.include_router(auth_router, tags=["Authentication"])
router.include_router(api_key_router, tags=["API Keys"])
```
**Pattern d'Endpoint Admin Versionné**:
```python
# routes/admin_routes.py
from fastapi import APIRouter
router = APIRouter(prefix="/admin", tags=["Admin"])
@router.post("/login")
async def admin_login(...):
...
@router.get("/dashboard")
async def get_admin_dashboard(...):
...
```
**Configuration FastAPI**:
```python
# main.py
from routes.api_v1_router import router as api_v1_router
app = FastAPI(
title="Office Translator API",
version="1.0.0",
description="API de traduction de documents Office. Tous les endpoints sont préfixés avec /api/v1/.",
)
# Endpoints exception (sans préfixe)
@app.get("/health")
async def health_check():
...
# Inclure le routeur principal
app.include_router(api_v1_router)
```
### Structure de Fichiers
```
routes/
├── __init__.py
├── api_v1_router.py # CRÉÉ - Routeur principal
├── admin_routes.py # CRÉÉ - Endpoints admin versionnés
├── legacy_routes.py # CRÉÉ - Endpoints migrés de main.py
├── translate_routes.py # Existant - Pas de changement
├── auth_routes.py # Existant - Pas de changement
└── api_key_routes.py # Existant - Pas de changement
main.py # MODIFIÉ - Nettoyer et inclure api_v1_router
config.py # MODIFIÉ - Description mise à jour
tests/
└── test_story_3_5_api_versioning.py # CRÉÉ
```
### Project Structure Notes
- Le projet suit une structure plate (pas de dossier `backend/app/`)
- Les routeurs sont dans `routes/`
- Les modèles sont dans `database/models.py`
- Les services sont dans `services/`
- Les tests sont dans `tests/`
### Références
- [Source: _bmad-output/planning-artifacts/epics.md#Story 3.5]
- [Source: _bmad-output/planning-artifacts/architecture.md#API & Communication Patterns]
- [Source: main.py - Endpoints actuels]
- [Source: routes/translate_routes.py - Pattern de routeur versionné]
## Intelligence des Stories Précédentes (Epic 3)
### Story 3.1 (API Key Generation) - Enseignements
1. **Routeur api_key_routes.py** utilise `prefix="/api/v1/api-keys"`
2. **Pattern de réponse** `{data: {...}, meta: {...}}` établi
3. **Tests complets** avec fixtures dans `tests/conftest.py`
### Story 3.2 (API Key Revocation User) - Enseignements
1. **Soft delete** avec `is_active=False`
2. **Fonction `get_user_by_api_key`** dans `services/auth_service.py`
3. **Format d'erreur structuré** `{error, message, details?}`
### Story 3.3 (Admin API Key Revocation) - Enseignements
1. **Routes admin dans main.py** - migré vers `routes/admin_routes.py`
2. **Dépendance `require_admin`** pour l'authentification admin
3. **Audit logging** avec `logger.info()`
### Story 3.4 (API Auth X-API-Key) - Enseignements
1. **Coexistence JWT + API Key** dans `get_authenticated_user`
2. **Middleware d'auth** dans `routes/translate_routes.py`
3. **Priorité API key sur JWT** si les deux présents
## Intelligence Git (Commits Récents)
Derniers commits pertinents:
- `3d37ce4`: PostgreSQL database infrastructure
- `c4d6cae`: Redis sessions, security hardening
- `dfd45d9`: Admin login endpoint
**Patterns identifiés**:
- Routeurs avec `APIRouter(prefix="/api/v1")`
- Tests avec `pytest` dans `tests/`
- Fixtures dans `tests/conftest.py`
## Contexte Métier
### Epic 3: API & Automation (Pro)
Cette story est la **cinquième de l'Epic 3** qui permet aux utilisateurs Pro d'automatiser les traductions:
1. ~~API Keys - Génération~~ (Story 3.1 ✅)
2. ~~API Keys - Révocation User~~ (Story 3.2 ✅)
3. ~~API Keys - Révocation Admin~~ (Story 3.3 ✅)
4. ~~Authentification X-API-Key~~ (Story 3.4 ✅)
5. **API Versioning** (cette story - COMPLETE)
6. Documentation OpenAPI (Story 3.6 - ready-for-dev)
7. Webhooks (Stories 3.7-3.8 - backlog)
8. Glossaires (Stories 3.9-3.10 - backlog)
9. Custom Prompts (Stories 3.11-3.12 - backlog)
### Valeur Business
Le versioning d'API est critique pour:
- **Évolutivité**: Pouvoir faire évoluer l'API sans casser les clients existants
- **Documentation**: Clarté sur la version utilisée
- **Migration**: Permettre une transition douce lors de changements majeurs
- **Professionalisme**: Standard de l'industrie pour les APIs REST
### Dépendances
- **Stories 3.1-3.4** (prérequis): Endpoints déjà versionnés ✅
- **Story 3.6** (impact): Documentation OpenAPI à mettre à jour
## Guardrails Développeur
### ❌ À NE PAS FAIRE
1. **NE PAS** supprimer les endpoints health/ready (K8s probes)
2. **NE PAS** supprimer l'accès à /docs et /redoc
3. **NE PAS** casser les clients existants utilisant `/api/v1/*`
4. **NE PAS** dupliquer le code - utiliser `include_router`
5. **NE PAS** oublier de tester tous les endpoints migrés
6. **NE PAS** modifier les routeurs existants (translate_routes.py, auth_routes.py, api_key_routes.py)
### ✅ À FAIRE
1. **TOUJOURS** préfixer les nouveaux endpoints avec `/api/v1/`
2. **TOUJOURS** garder health/ready accessibles sans préfixe
3. **TOUJOURS** tester que les endpoints non-versionnés retournent 404
4. **TOUJOURS** documenter la version dans OpenAPI
5. **TOUJOURS** utiliser `include_router` pour composer les routeurs
6. **CRÉER** un routeur principal `api_v1_router.py`
7. **CRÉER** un routeur admin `admin_routes.py`
## Dev Agent Record
### Agent Model Used
Claude Sonnet 4
### Debug Log References
- App import successful after refactoring
- All 31 versioning tests pass
- Updated existing tests to use versioned endpoints
- Patch paths updated for translator mocks
### Completion Notes List
- ✅ Analyse exhaustive du contexte terminée - guide complet créé pour le développeur
- ✅ Endpoints existants analysés (versionnés et non-versionnés)
- ✅ Architecture proposée avec routeur principal et sous-routeurs
- ✅ Liste complète des endpoints à migrer documentée
- ✅ Exceptions documentées (health, ready, docs, redoc)
- ✅ Routes api_v1_router.py créé - agrège tous les sous-routeurs
- ✅ Routes admin_routes.py créé - tous les endpoints admin sous /api/v1/admin/
- ✅ Routes legacy_routes.py créé - endpoints migrés (languages, ollama, metrics, etc.)
- ✅ main.py nettoyé - ne contient plus que les exceptions (health, ready, root)
- ✅ Config.py mis à jour avec description mentionnant /api/v1
- ✅ Tests complets créés dans tests/test_story_3_5_api_versioning.py
- ✅ Tests existants mis à jour pour utiliser les endpoints versionnés
- ✅ Ancien auth_router marqué DEPRECATED - non importé, conservé pour référence
### Code Review Fixes Applied (2026-02-22)
- 🔧 Sécurité: Suppression du mot de passe admin par défaut "changeme123"
- 🔧 Stripe webhook migré vers /api/v1/auth/webhook/stripe
- 🔧 Valeurs hardcodées remplacées par config (metrics, rate-limit/status)
- 🔧 Tests ajoutés pour endpoints manquants (extract-texts, reconstruct, batch, download, cleanup)
- 🔧 Ancien router /api/auth marqué DEPRECATED avec documentation
### File List
- `routes/api_v1_router.py` - CRÉÉ - Routeur principal /api/v1
- `routes/admin_routes.py` - CRÉÉ - Endpoints admin versionnés
- `routes/legacy_routes.py` - CRÉÉ - Endpoints migrés de main.py
- `main.py` - MODIFIÉ - Nettoyé et inclut api_v1_router uniquement
- `config.py` - MODIFIÉ - Description mise à jour avec mention /api/v1
- `tests/test_story_3_5_api_versioning.py` - CRÉÉ - 31 tests complets
- `tests/test_admin_tier_change.py` - MODIFIÉ - URLs et patches mis à jour
- `tests/test_story_3_3_admin_api_key_revocation.py` - MODIFIÉ - URLs et patches mis à jour
- `tests/test_auth_register.py` - MODIFIÉ - Test legacy path skipped
## Change Log
- 2026-02-22: Story créée avec contexte complet (endpoints à migrer, architecture proposée, code suggéré)
- 2026-02-22: Implémentation complète - tous les endpoints migrés sous /api/v1/, tests passants
## Checklist de Validation
Avant de marquer cette story comme terminée, vérifier:
- [x] Tous les endpoints API sont sous `/api/v1/`
- [x] Les endpoints non-versionnés retournent 404 (sauf exceptions)
- [x] `/health` et `/ready` sont accessibles sans préfixe
- [x] `/docs` et `/redoc` sont accessibles sans préfixe
- [x] La version est documentée dans OpenAPI
- [x] Les clients existants utilisant `/api/v1/*` ne sont pas affectés
- [x] Tous les tests passent
- [x] Le routeur principal `api_v1_router.py` est créé
- [x] Le routeur admin `admin_routes.py` est créé
- [x] main.py est nettoyé et ne contient que les endpoints exception