Imago/doc/PROMPT_PHASE1_claude-opus.md
Bruno Charest cc99fea20a
Some checks failed
CI / Lint & Format (push) Has been cancelled
CI / Tests (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
Add comprehensive test suite for image processing and related services
- Implement tests for database generator to ensure proper session handling.
- Create tests for EXIF extraction and conversion functions.
- Add tests for image-related endpoints, ensuring proper data retrieval and isolation between clients.
- Develop tests for OCR functionality, including language detection and text extraction.
- Introduce tests for the image processing pipeline, covering success and failure scenarios.
- Validate rate limiting functionality and ensure independent counters for different clients.
- Implement scraper tests to verify HTML content fetching and error handling.
- Add unit tests for various services, including storage and filename generation.
- Establish worker entry point for ARQ to handle background image processing tasks.
2026-02-24 11:22:10 -05:00

13 KiB

Prompt Claude Code — Phase 1 : Fondations sécurité

Modèle : claude-opus-4-6

Usage : claude --model claude-opus-4-6 -p "$(cat PROMPT_PHASE1.md)"

ou coller directement dans une session Claude Code interactive


Tu es un ingénieur backend senior Python spécialisé en FastAPI, SQLAlchemy async et sécurité des APIs REST. Tu travailles sur le projet Imago, un backend centralisé de gestion d'images conçu pour servir plusieurs applications clientes simultanément.

<project_context>

Projet : Imago

Backend FastAPI existant servant de hub centralisé pour :

  • Stocker et gérer des images avec génération automatique de thumbnails
  • Extraire les métadonnées EXIF (appareil photo, GPS, paramètres de prise de vue)
  • Effectuer l'OCR sur les images via Tesseract
  • Analyser les images avec Claude Vision AI (description + classification par tags)

Stack technique en place

  • FastAPI + Uvicorn (serveur ASGI)
  • SQLAlchemy async + Alembic (ORM + migrations)
  • Pydantic v2 + pydantic-settings (validation + config)
  • Pillow + piexif (traitement images + EXIF)
  • pytesseract (OCR)
  • Anthropic Claude via httpx (Vision AI)
  • aiofiles (I/O async)
  • SQLite (développement) / PostgreSQL (production)

Structure du projet

imago/
├── app/
│   ├── main.py              # Application FastAPI
│   ├── config.py            # Settings depuis .env
│   ├── database.py          # Engine SQLAlchemy async + session
│   ├── models/
│   │   └── image.py         # Modèle Image (EXIF, OCR, AI, statut)
│   ├── schemas/
│   │   └── __init__.py      # Schémas Pydantic
│   ├── routers/
│   │   ├── images.py        # Endpoints images (CRUD + pipeline)
│   │   └── ai.py            # Endpoints AI (résumé URL, tâches)
│   └── services/
│       ├── storage.py       # Sauvegarde fichiers + thumbnails
│       ├── exif_service.py  # Extraction EXIF
│       ├── ocr_service.py   # OCR Tesseract
│       ├── ai_vision.py     # Vision AI Claude
│       ├── scraper.py       # Scraping web
│       └── pipeline.py      # Orchestration pipeline AI
├── tests/
│   └── test_services.py
├── requirements.txt
└── .env

Problème actuel

Le backend est entièrement public : aucune authentification, aucune isolation entre clients. N'importe quelle application peut accéder, modifier ou supprimer toutes les données. C'est incompatible avec un hub multi-clients en production. </project_context>

## Mission : implémenter la Phase 1 — Fondations sécurité

Tu dois réaliser les 4 livrables suivants dans l'ordre indiqué. Chaque livrable doit être complet, testé et fonctionnel avant de passer au suivant.

Livrable 1.1 — Authentification API Keys + JWT avec scopes (priorité CRITIQUE)

Objectif : sécuriser tous les endpoints avec deux mécanismes d'authentification complémentaires.

Ce qui doit être créé ou modifié :

  1. app/models/client.py — Nouveau modèle SQLAlchemy APIClient :

    • id : UUID, primary key
    • name : String, nom de l'application cliente (ex: "Shaarli", "App Mobile")
    • api_key_hash : String, hash SHA-256 de la clé API (jamais stocker en clair)
    • scopes : JSON, liste des permissions accordées
    • plan : Enum (free, standard, premium)
    • is_active : Boolean, default True
    • created_at / updated_at : DateTime
  2. app/dependencies/__init__.py + app/dependencies/auth.py — Dépendances FastAPI :

    • verify_api_key(authorization: str = Header(...)) → retourne l'APIClient authentifié
    • require_scope(scope: str) → factory qui vérifie qu'un scope est accordé
    • get_current_client → alias réutilisable dans tous les routers
    • Lever HTTP 401 si clé invalide ou client inactif
    • Lever HTTP 403 si scope manquant
  3. app/routers/auth.py — Endpoints de gestion des clients :

    • POST /auth/clients — Créer un nouveau client (retourne la clé en clair une seule fois)
    • GET /auth/clients — Lister les clients (admin only, scope admin)
    • GET /auth/clients/{id} — Détail d'un client
    • PATCH /auth/clients/{id} — Modifier un client (scopes, plan, is_active)
    • POST /auth/clients/{id}/rotate-key — Régénérer la clé API
    • DELETE /auth/clients/{id} — Désactiver (soft delete)
  4. Scopes à définir :

    • images:read — lire les images et métadonnées
    • images:write — uploader et modifier des images
    • images:delete — supprimer des images
    • ai:use — utiliser les endpoints AI (résumé URL, génération tâches)
    • admin — gestion des clients (réservé à un super-client)
  5. app/config.py — Ajouter :

    • ADMIN_API_KEY : clé du super-client admin (depuis .env)
    • JWT_SECRET_KEY et JWT_ALGORITHM pour les tokens JWT futurs

Contraintes :

  • Les clés API doivent être générées avec secrets.token_urlsafe(32)
  • Le hash doit être SHA-256 via hashlib
  • La clé en clair ne doit jamais être stockée en base ni apparaître dans les logs
  • Utiliser python-jose et passlib (ajouter au requirements.txt)

Livrable 1.2 — Modèle clients + isolation complète des données (priorité CRITIQUE)

Objectif : rendre toutes les données strictement isolées par client.

Ce qui doit être créé ou modifié :

  1. app/models/image.py — Modifier le modèle Image existant :

    • Ajouter client_id = Column(UUID, ForeignKey("api_clients.id"), nullable=False, index=True)
    • Ajouter la relation client = relationship("APIClient", back_populates="images")
  2. app/models/client.py — Ajouter la relation inverse :

    • images = relationship("Image", back_populates="client", cascade="all, delete-orphan")
  3. app/services/storage.py — Modifier le service de stockage :

    • Les fichiers doivent être stockés dans uploads/{client_id}/{filename}
    • Les thumbnails dans thumbnails/{client_id}/{filename}
    • La fonction save_upload doit accepter client_id en paramètre
  4. app/routers/images.py — Modifier TOUS les endpoints :

    • Injecter client: APIClient = Depends(get_current_client) dans chaque endpoint
    • Appliquer require_scope("images:read") sur les GET
    • Appliquer require_scope("images:write") sur les POST
    • Appliquer require_scope("images:delete") sur les DELETE
    • Filtrer systématiquement toutes les requêtes avec WHERE image.client_id = client.id
    • Un client ne doit jamais pouvoir accéder aux images d'un autre client
  5. app/routers/ai.py — Même injection + scope ai:use

  6. alembic/versions/ — Créer une migration Alembic pour :

    • Créer la table api_clients
    • Ajouter la colonne client_id à la table images
    • Créer un client "default" avec toutes les permissions pour la migration des données existantes

Contraintes :

  • Toute requête DB touchant des images DOIT inclure le filtre client_id
  • Écrire une fonction utilitaire get_image_or_404(image_id, client_id, db) qui centralise ce pattern
  • Le filtre doit être appliqué au niveau service, pas seulement dans les routers

Livrable 1.3 — Rate limiting par client et par endpoint (priorité HAUTE)

Objectif : protéger le hub contre les abus et contrôler la consommation par client.

Ce qui doit être créé ou modifié :

  1. Ajouter slowapi au requirements.txt

  2. app/middleware/rate_limit.py — Configuration du rate limiting :

    • Identifier les requêtes par client_id (extrait du token) plutôt que par IP
    • Limites différentes selon le plan :
      • free : 20 uploads/heure, 50 requêtes AI/heure
      • standard : 100 uploads/heure, 200 requêtes AI/heure
      • premium : 500 uploads/heure, 1000 requêtes AI/heure
    • Retourner HTTP 429 avec header Retry-After si limite atteinte
  3. app/routers/images.py — Appliquer les décorateurs rate limit sur :

    • POST /images/upload → limiter par plan
    • POST /images/{id}/reprocess → limiter par plan
  4. app/routers/ai.py — Appliquer sur :

    • POST /ai/summarize → limiter par plan
    • POST /ai/draft-task → limiter par plan
  5. app/config.py — Ajouter les variables de configuration des limites par plan

Contraintes :

  • Les compteurs de rate limit doivent être par client (pas global)
  • Inclure les headers standard : X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset

Livrable 1.4 — Tests d'intégration auth + multi-tenants (priorité HAUTE)

Objectif : valider que la sécurité fonctionne correctement avec une suite de tests exhaustive.

Ce qui doit être créé :

  1. tests/conftest.py — Fixtures pytest :

    • test_db : base SQLite in-memory pour les tests
    • client_a et client_b : deux clients API de test avec des clés distinctes
    • auth_headers_a et auth_headers_b : headers d'authentification correspondants
    • async_client : httpx.AsyncClient configuré pour les tests
  2. tests/test_auth.py — Tests d'authentification :

    • Requête sans clé → HTTP 401
    • Requête avec clé invalide → HTTP 401
    • Requête avec clé valide → HTTP 200
    • Client désactivé → HTTP 401
    • Scope manquant → HTTP 403
    • Rotation de clé → ancienne clé invalide, nouvelle clé valide
    • Création de client → clé retournée une seule fois
  3. tests/test_isolation.py — Tests d'isolation multi-tenants :

    • Client A upload une image → invisible pour Client B
    • Client B ne peut pas lire l'image du Client A (même avec bonne clé)
    • Client B ne peut pas supprimer l'image du Client A → HTTP 404
    • Listing des images de A ne retourne que les images de A
    • Les fichiers sont stockés dans des répertoires séparés
    • Reprocess d'une image appartenant à un autre client → HTTP 404
  4. tests/test_rate_limit.py — Tests de rate limiting :

    • Dépassement de quota → HTTP 429 avec header Retry-After
    • Compteur distinct par client
    • Headers X-RateLimit-* présents sur chaque réponse

Contraintes :

  • Tous les tests doivent être async (pytest-asyncio)
  • Les appels à l'API Anthropic doivent être mockés dans les tests
  • Couverture minimum : 85% sur les modules auth.py, dependencies/auth.py, models/client.py

<execution_rules>

Règles d'exécution

Ordre de réalisation

Implémenter dans l'ordre strict : 1.1 → 1.2 → 1.3 → 1.4. Ne pas passer au livrable suivant sans avoir validé le précédent avec pytest.

À chaque livrable

  1. Lire les fichiers existants concernés avant de les modifier
  2. Écrire le code complet (pas de # TODO ni de # ...)
  3. Mettre à jour requirements.txt si de nouvelles dépendances sont ajoutées
  4. Créer la migration Alembic si le modèle de données change
  5. Lancer pytest tests/ -v et corriger les erreurs avant de continuer

Qualité du code

  • Typage complet sur toutes les fonctions (mypy compatible)
  • Docstrings sur tous les services et dépendances
  • Pas de secrets hardcodés — tout passe par app/config.py + .env
  • Logging structuré avec le niveau approprié (pas de print())
  • Gestion explicite de toutes les exceptions (pas de except Exception: pass)

Sécurité — règles absolues

  • Les clés API ne doivent jamais apparaître dans les logs, réponses d'erreur, ou stack traces
  • Le hash doit être SHA-256 avec sel (utiliser hashlib.pbkdf2_hmac ou passlib)
  • Les messages d'erreur d'authentification doivent être génériques (ne pas indiquer si la clé existe ou si c'est le scope qui manque — sauf pour HTTP 403 vs 401)
  • Valider les entrées avec Pydantic sur tous les endpoints de création/modification

Migrations Alembic

  • Une migration par changement de schéma
  • Inclure les upgrade() et downgrade() complets
  • Tester la migration sur une base vierge avant de la considérer terminée

Résultat attendu

À la fin de la Phase 1, la commande suivante doit réussir sans erreur :

pytest tests/ -v --cov=app --cov-report=term-missing

Et le serveur doit démarrer sans erreur avec :

python run.py

</execution_rules>

<deliverable_summary>

Résumé des livrables attendus

# Fichiers créés ou modifiés Validation
1.1 app/models/client.py, app/dependencies/auth.py, app/routers/auth.py, app/config.py, requirements.txt pytest tests/test_auth.py
1.2 app/models/image.py, app/services/storage.py, app/routers/images.py, app/routers/ai.py, alembic/versions/xxx_add_clients.py pytest tests/test_isolation.py
1.3 app/middleware/rate_limit.py, app/routers/images.py, app/routers/ai.py, app/config.py pytest tests/test_rate_limit.py
1.4 tests/conftest.py, tests/test_auth.py, tests/test_isolation.py, tests/test_rate_limit.py pytest tests/ -v --cov=app

Résultat final de la Phase 1 : hub sécurisé, données strictement isolées par client, déployable en production. </deliverable_summary>