Files
office_translator/_bmad-output/implementation-artifacts/1-7-admin-changement-de-tier-manuel.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

11 KiB
Raw Blame History

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

  1. 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.
  2. 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é).
  3. AC3: Passage à Free — If downgraded to Free, rate limiting (5 files/day) applies immediately; prochaine requête de traduction respecte le quota free.
  4. 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).
  • 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: 14)
    • 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 nexiste pas encore de PATCH pour modifier le tier/plan. Le modèle User (database/models.py) a à la fois tier (string) et plan (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 lAPI admin expose tier ou plan; 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 linstant; les routes admin sont dans main.py. Option : ajouter la logique dans main.py ou créer routes/admin_routes.py et linclure.

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 dun 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 data contenant lutilisateur 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 champ data.
  • 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 daccè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 dinté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 lutilisateur; 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 (AC14).
  • 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 à larchitecture : 404 → { "error": "NOT_FOUND", "message": "User not found" }; 400 → { "error": "INVALID_PLAN", "message": "...", "details": { "allowed": [...] } } (pas de champ data).
  • 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.14.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/).