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>
11 KiB
11 KiB
Story 1.7: Admin - Changement de Tier Manuel
Status: done
Story
As an Admin, I want to change a user's tier from Free to Pro (or vice versa), so that I can manually onboard Pro clients.
Acceptance Criteria
- AC1: Mise à jour immédiate — Given I am logged in as admin, when I update a user's tier via PATCH /api/v1/admin/users/{user_id} (or PATCH /admin/users/{user_id} si préfixe actuel), then the user's tier/plan is updated immediately in the database.
- AC2: Passage à Pro — If upgraded to Pro (or equivalent plan), user can access LLM modes immediately (rate limit unlimited; tier_quota considère déjà pro/business/enterprise comme illimité).
- AC3: Passage à Free — If downgraded to Free, rate limiting (5 files/day) applies immediately; prochaine requête de traduction respecte le quota free.
- AC4: Audit — Action is logged with timestamp and admin identifier (admin session or user_id if admin is a user); structlog ou équivalent, sans contenu document.
Tasks / Subtasks
- Task 1: Endpoint PATCH admin users/{user_id} (AC: 1, 2, 3)
- 1.1 Add PATCH /api/v1/admin/users/{user_id} (or /admin/users/{user_id} pour cohérence avec GET /admin/users) with body e.g.
{ "tier": "pro" }or{ "plan": "pro" }selon modèle (User.tier string vs User.plan enum). - 1.2 Protect route with
Depends(require_admin). - 1.3 Resolve user by user_id (UUID); return 404 if user not found.
- 1.4 Validate tier/plan value: allow only "free" | "pro" (et éventuellement starter, business, enterprise si dans PlanType); return 400 for invalid value.
- 1.5 Update User in DB (tier and/or plan); ensure tier_quota and rate limiting use same source (plan or tier).
- 1.1 Add PATCH /api/v1/admin/users/{user_id} (or /admin/users/{user_id} pour cohérence avec GET /admin/users) with body e.g.
- Task 2: Effet immédiat tier (AC: 2, 3)
- 2.1 No cache invalidation required if tier is read from DB on each request (get_current_user / tier_quota check).
- 2.2 If any in-memory or Redis cache of user tier exists, invalidate or update on PATCH.
- Task 3: Logging audit (AC: 4)
- 3.1 Log action: e.g. event "admin_tier_change", user_id target, new_tier, admin_id or "admin_session", timestamp (ISO8601).
- 3.2 Do not log document content; only metadata (NFR11, NFR16).
- Task 4: Tests (AC: 1–4)
- 4.1 Test: admin PATCH with valid tier → 200, user tier updated in DB.
- 4.2 Test: admin PATCH with invalid tier → 400.
- 4.3 Test: admin PATCH with unknown user_id → 404.
- 4.4 Test: non-admin PATCH → 401/403.
- 4.5 Test: after upgrade to pro, user can translate beyond 5/day; after downgrade to free, quota 5 applies.
Dev Notes
- Contexte actuel : GET /admin/users existe dans main.py et utilise
load_users()(auth_service); les users sont exposés avec plan, email, usage. Il n’existe pas encore de PATCH pour modifier le tier/plan. Le modèle User (database/models.py) a à la foistier(string) etplan(PlanType enum: free, starter, pro, business, enterprise). Le middleware tier_quota (story 1.6) utilise le plan pour déterminer si illimité (pro/business/enterprise). - Alignement : Décider si l’API admin expose
tierouplan; garder cohérence avec User.tier et User.plan (les deux en sync lors du PATCH). - Auth admin : require_admin (Bearer token après /admin/login) est déjà en place; réutiliser pour le PATCH.
Project Structure Notes
- Backend à la racine : main.py (routes admin), database/models.py (User), services/auth_service.py (load_users, potentiellement update_user_tier ou équivalent).
- Pas de module admin séparé pour l’instant; les routes admin sont dans main.py. Option : ajouter la logique dans main.py ou créer routes/admin_routes.py et l’inclure.
References
- [Source: _bmad-output/planning-artifacts/epics.md#Story 1.7]
- [Source: _bmad-output/planning-artifacts/architecture.md#API Response Formats]
- [Source: _bmad-output/planning-artifacts/prd.md#FR21, FR22 - Admin change tier]
- [Source: _bmad-output/implementation-artifacts/1-6-middleware-rate-limiting-par-tier.md — tier_quota, plan/tier]
Developer Context (Guardrails)
Technical requirements
- Backend : FastAPI. Endpoint PATCH pour mettre à jour le tier/plan d’un utilisateur cible; authentification admin obligatoire (require_admin).
- DB : Mise à jour de User.tier et User.plan (ou un seul si un seul est la source de vérité); persistance immédiate.
- Rate limiting : Après changement, le prochain appel qui lit le user (get_current_user + tier_quota) doit voir le nouveau tier; pas de cache stale (si cache, invalider).
- Réponses : 200 avec
datacontenant l’utilisateur mis à jour (id, email, tier/plan); 400 body invalide; 404 user non trouvé; 401/403 non-admin.
Architecture compliance
- Conventions API : JSON snake_case; format succès
{ data, meta }; format erreur{ error, message, details? }sans champdata. - Préfixe : /api/v1/ pour cohérence avec le reste (epics indiquent PATCH /api/v1/admin/users/{user_id}); si le projet utilise /admin/users sans /api/v1/, documenter et rester cohérent avec GET /admin/users.
Library / framework
- FastAPI Depends(require_admin) existant.
- SQLAlchemy/session pour update User; utiliser la même couche d’accès que auth_service (database/connection, etc.).
File structure
- Route : main.py (ou routes/admin_routes.py si créé) — PATCH pour users/{user_id}.
- Service : soit dans services/auth_service.py (update_user_plan / update_user_tier), soit dans un nouveau services/admin_service.py.
- Modèle : database/models.py (User.tier, User.plan) — pas de nouvelle table.
Testing requirements
- Tests d’intégration ou unitaires : admin PATCH 200 et vérification en DB; PATCH invalid tier 400; user_id inconnu 404; sans token admin 401/403; test optionnel : après upgrade pro, quota illimité; après downgrade free, 6ème requête 429.
Previous Story Intelligence (1-6 Middleware Rate Limiting par Tier)
- Fichiers modifiés : middleware/tier_quota.py (TierQuotaService, Redis + in-memory), main.py (quota check sur /translate, 429 QUOTA_EXCEEDED, X-Rate-Limit-* headers), models/subscription.py (daily_translation_count), database/models.py (User.tier, plan), tests/test_tier_rate_limit.py.
- Patterns : Le quota est déterminé par le plan utilisateur (pro/business/enterprise = illimité; free = 5/jour). get_current_user est utilisé pour obtenir l’utilisateur; la logique tier est dans tier_quota.check_quota(user_id, plan/tier). Pour la story 1.7, après un PATCH réussi, le prochain appel get_current_user + tier_quota doit voir le nouveau plan/tier — si les users sont chargés depuis la DB à chaque requête, aucun cache à invalider; sinon (cache Redis/session), invalider ou mettre à jour.
- Tests : Fixtures free/pro user, mocks Redis; pour 1.7 réutiliser require_admin et ajouter fixtures admin token.
Git Intelligence Summary
- Codebase actuelle : routes admin sous /admin/* (login, logout, users, stats, cleanup, etc.); pas de PATCH users. User avec tier et plan. Tier quota lit le plan pour unlimited. Implémentation 1.7 = ajouter un seul endpoint PATCH et la logique de mise à jour + log audit.
Latest Tech Information
- FastAPI : Pour PATCH, utiliser un body Pydantic (e.g. AdminUpdateUserTierRequest avec champ tier ou plan); Path() pour user_id. Réponse 200 avec schema utilisateur (snake_case).
- Logging : structlog ou logging standard avec event structuré (admin_tier_change, target_user_id, new_tier, timestamp); pas de contenu document (NFR11).
Project Context Reference
- Structure : main.py contient les routes admin; auth via require_admin (Bearer token). GET /admin/users utilise load_users() depuis auth_service. Base de données : database/models.py (User), database/connection.py. Pour modifier un user, il faudra soit étendre auth_service (update_user_plan), soit un service admin dédié qui utilise la même session/DB.
- Montage : Ajouter une route PATCH à côté de GET /admin/users; garder le même préfixe (/admin/users ou /api/v1/admin/users selon convention projet).
Story Completion Status
- Status : review
- Note : Implementation complete; ready for code review.
Change Log
- 2026-02-20: Story 1.7 created from epics + architecture + previous story 1.6.
- 2026-02-20: Implemented PATCH /admin/users/{user_id}, update_user_plan, audit logging, tests (AC1–4).
- 2026-02-20: Code review (AI): corrections appliquées — format erreur API (404/400 →
{ error, message, details? }), schéma Pydantic Literal pour plan, datetime UTC dans repository, tests adaptés.
Dev Agent Record
Agent Model Used
{{agent_model_name_version}}
Debug Log References
Completion Notes List
- PATCH /admin/users/{user_id} with body
{ "plan": "free"|"starter"|"pro"|"business"|"enterprise" }; protected by require_admin; 200 with data (id, email, name, plan, tier), 400 invalid plan (INVALID_PLAN), 404 user not found (NOT_FOUND), 401 without/invalid admin token. - Réponses erreur conformes à l’architecture : 404 →
{ "error": "NOT_FOUND", "message": "User not found" }; 400 →{ "error": "INVALID_PLAN", "message": "...", "details": { "allowed": [...] } }(pas de champdata). - auth_service.update_user_plan(user_id, plan) keeps User.plan and User.tier in sync (tier "pro" for pro/business/enterprise, "free" otherwise); works with JSON and DB backends.
- Audit log: logger.info("admin_tier_change", extra={ event, target_user_id, new_tier, new_plan, admin_id: "admin_session", timestamp ISO8601 }); no document content (AC4).
- Task 2: tier/plan read from DB (or JSON) on each request via get_current_user; no user-tier cache to invalidate.
- Tests: test_admin_tier_change.py (7 tests) — 4.1–4.5 plus 401 without/invalid token; test 4.2 accepte 400 ou 422 (Literal → 422 pour plan invalide).
- Post-review: AdminUpdateUserTierRequest.plan en Literal; database/repositories.py updated_at en datetime.now(timezone.utc).
File List
- main.py (PATCH /admin/users/{user_id}, AdminUpdateUserTierRequest avec Literal plan, JSONResponse 404/400 format architecture)
- services/auth_service.py (update_user_plan, VALID_PLAN_VALUES)
- database/repositories.py (updated_at timezone-aware UTC)
- tests/test_admin_tier_change.py (new)
Senior Developer Review (AI)
- Date : 2026-02-20
- Résultat : Approuvé après correctifs automatiques.
- Problèmes traités : Format erreur API (HIGH) — 404/400 retournent désormais
{ error, message, details? }. Schéma Pydantic avec Literal pour plan (LOW). Datetime UTC dans UserRepository.update (LOW). Tests adaptés pour 400/422 et nouveau format erreur. - Recommandation : Commiter le dossier
tests/si pas encore versionné (git add tests/).