13 KiB
Imago
Backend FastAPI pour la gestion d'images et fonctionnalités AI, conçu comme complément à l'interface Web-thème Shaarli-Pro et toute autre applications qui voudrait l'utiliser.
Fonctionnalités
| Feature | Description |
|---|---|
| 📸 Upload d'images | Stockage + thumbnails, isolés par client (multi-tenant) |
| 🛡️ Sécurité | Authentification par API Key + scopes granulaires + Rate Limiting |
| 🔍 Extraction EXIF | Appareil photo, GPS, ISO, ouverture, date de prise de vue |
| 📝 OCR | Extraction du texte visible dans l'image (Tesseract + fallback AI) |
| 🤖 Vision AI | Description naturelle + classification par tags (Gemini / OpenRouter) |
| 🔗 Résumé URL | Scraping d'une page web + résumé AI |
| ✅ Rédaction de tâches | Génération structurée d'une tâche à partir d'une description |
| 📋 File de tâches ARQ | Pipeline persistant avec retry automatique (Redis) |
| 📊 Métriques Prometheus | Endpoint /metrics + custom counters/histograms |
| 💾 Storage abstrait | Local (HMAC signed URLs) ou S3/MinIO (presigned URLs) |
| 📈 Quota tracking | Suivi du stockage par client avec enforcement |
| 🔧 Logging structuré | JSON (production) / Console colorée (dev) via structlog |
Stack technique
FastAPI + Uvicorn — Framework web async
SQLAlchemy (async) — ORM + SQLite/Postgres
ARQ + Redis — File de tâches persistante
Pillow + piexif — Traitement d'images + EXIF
pytesseract — OCR (Tesseract)
Google GenAI SDK — Vision AI (Gemini)
aioboto3 — Stockage S3/MinIO
structlog — Logging structuré JSON
Prometheus — Métriques et monitoring
slowapi — Rate limiting par client
ruff — Linting + formatting
Installation
Prérequis
- Python 3.12+ (3.14 supporté)
- Tesseract OCR installé sur le système
- Redis (optionnel, pour ARQ worker)
# Ubuntu/Debian
sudo apt install tesseract-ocr tesseract-ocr-fra tesseract-ocr-eng
# macOS
brew install tesseract tesseract-lang
# Windows
scoop install main/tesseract
Setup
Note
Les fichiers
requirements.txtappliquent automatiquement des versions compatibles Python 3.14 (notamment pour Pillow et Pydantic).
# 1. Cloner et installer
git clone <repo>
cd imago
# Production
pip install -r requirements.txt
# Développement (inclut linting, tests, pre-commit)
pip install -r requirements-dev.txt
pre-commit install
# 2. Configuration
cp .env.example .env
# Éditer .env — voir la section Configuration ci-dessous
# 3. Migrations
alembic upgrade head
# La migration crée un client "default" et affiche sa clé API une seule fois.
# 4. Démarrage
python run.py # API sur http://localhost:8000
python worker.py # Worker ARQ (requiert Redis)
Avec Docker
docker-compose up -d # API + Redis + Worker + PostgreSQL
Commandes utiles (Makefile)
make help # Affiche toutes les commandes
make install-dev # Dépendances dev + pre-commit
make lint # Ruff + Mypy
make format # Auto-format du code
make test # Tests rapides
make test-cov # Tests + couverture HTML
make serve # Serveur dev (reload)
make worker # Worker ARQ
make docker-up # Docker compose up
make clean # Nettoyage __pycache__, .pytest_cache, etc.
Pipeline de traitement AI
Chaque image uploadée déclenche un pipeline via ARQ (Redis) :
Upload → Sauvegarde fichier + thumbnail → BDD (PENDING)
│
▼ (ARQ Worker — persistant, avec retry)
┌────────────────────────────────────────┐
│ Étape 1 — Extraction EXIF │
│ Étape 2 — OCR (Tesseract + AI) │
│ Étape 3 — Vision AI (Gemini) │
└────────────────────────────────────────┘
│
▼
BDD (DONE) + événements Redis pub/sub
Important
Isolation multi-tenant : fichiers et données sont compartimentés par
client_id. Un client ne peut ni voir, ni modifier, ni supprimer les images d'un autre.
Chaque étape est indépendante : un échec partiel n'arrête pas le pipeline.
Priority queues : les clients premium ont une file dédiée.
Retry automatique avec backoff exponentiel. Dead-letter après max retries.
Endpoints API
Images (Auth requise)
| Méthode | URL | Description | Scope |
|---|---|---|---|
POST |
/images/upload |
Uploader une image | images:write |
GET |
/images |
Lister (paginé, filtrable, quota info) | images:read |
GET |
/images/{id} |
Détail complet | images:read |
GET |
/images/{id}/status |
Statut du pipeline | images:read |
GET |
/images/{id}/exif |
Métadonnées EXIF | images:read |
GET |
/images/{id}/ocr |
Texte OCR extrait | images:read |
GET |
/images/{id}/ai |
Description + tags AI | images:read |
GET |
/images/{id}/download-url |
URL signée de téléchargement | images:read |
GET |
/images/{id}/thumbnail-url |
URL signée du thumbnail | images:read |
GET |
/images/tags/all |
Tous les tags du client | images:read |
POST |
/images/{id}/reprocess |
Relancer le pipeline | images:write |
DELETE |
/images/{id} |
Supprimer image | images:delete |
Intelligence Artificielle (Auth requise)
| Méthode | URL | Description | Scope |
|---|---|---|---|
POST |
/ai/summarize |
Résumé AI d'une URL | ai:use |
POST |
/ai/draft-task |
Rédaction de tâche | ai:use |
Administration & Clients (Admin only)
| Méthode | URL | Description | Scope |
|---|---|---|---|
POST |
/auth/clients |
Créer un client API | admin |
GET |
/auth/clients |
Lister les clients | admin |
GET |
/auth/clients/{id} |
Détail d'un client | admin |
PATCH |
/auth/clients/{id} |
Modifier client (plan, scopes) | admin |
POST |
/auth/clients/{id}/rotate-key |
Régénérer clé API | admin |
DELETE |
/auth/clients/{id} |
Soft delete (désactivation) | admin |
Fichiers signés
| Méthode | URL | Description | Auth |
|---|---|---|---|
GET |
/files/signed/{token} |
Télécharger via URL signée | Token HMAC |
Observabilité & Santé
| Méthode | URL | Description | Auth |
|---|---|---|---|
GET |
/ |
Info application | Non |
GET |
/health |
Santé du service | Non |
GET |
/health/detailed |
Santé détaillée (DB, Redis, ARQ, OCR) | Non |
GET |
/metrics |
Métriques Prometheus | Non |
GET |
/docs |
Documentation Swagger | Non |
Exemples d'utilisation
Tip
Tous les appels (sauf
/healthet/metrics) nécessitent une clé API valide passée dans le headerX-API-Key.
Upload d'une image
api_key=emH92l92LD4L7cLhl2imidMZANsIUb9x_AlGWiYpVSA client_id=925463e0-27a4-4993-aa3a-f1cb31c19d32 warning=Notez cette clé ! Elle ne sera plus affichée.
curl -X POST http://localhost:8000/images/upload \
-H "X-API-Key: rEYQtw3LxJJlcmBq-cgQcdeY74JcpJ45COuFWokmxPg" \
-F "file=@pushup.gif"
Réponse :
{
"id": 1,
"uuid": "a1b2c3d4-...",
"original_name": "photo.jpg",
"status": "pending",
"message": "Image uploadée — traitement AI en cours"
}
Polling du statut
curl http://localhost:8000/images/1/status -H "X-API-Key: rEYQtw3LxJJlcmBq-cgQcdeY74JcpJ45COuFWokmxPg"
{
"id": 1,
"status": "done",
"started_at": "2024-01-15T10:30:00",
"done_at": "2024-01-15T10:30:08"
}
Détail complet
curl http://localhost:8000/images/1 -H "X-API-Key: rEYQtw3LxJJlcmBq-cgQcdeY74JcpJ45COuFWokmxPg"
{
"id": 1,
"original_name": "photo.jpg",
"processing_status": "done",
"exif": {
"camera": {
"make": "Canon",
"model": "EOS R5",
"iso": 400,
"aperture": "f/2.8",
"shutter_speed": "1/250",
"focal_length": "50mm",
"taken_at": "2024-06-15T14:30:00"
},
"gps": {
"latitude": 48.8566,
"longitude": 2.3522,
"has_gps": true,
"maps_url": "https://maps.google.com/?q=48.8566,2.3522"
}
},
"ocr": {
"has_text": true,
"text": "Café de Flore",
"language": "fr",
"confidence": 0.94
},
"ai": {
"description": "Une terrasse de café parisien animée en fin d'après-midi. Les tables en osier sont disposées sur le trottoir boulevard Saint-Germain, avec une clientèle détendue profitant du soleil. L'enseigne emblématique du Café de Flore est visible en arrière-plan.",
"tags": ["café", "paris", "terrasse", "france", "bistrot", "extérieur", "urbain"],
"confidence": 0.97,
"model_used": "gemini-1.5-pro"
}
}
Résumé d'URL
curl -X POST http://localhost:8000/ai/summarize \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/article", "language": "français"}'
Rédaction de tâche
curl -X POST http://localhost:8000/ai/draft-task \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"description": "Mettre à jour la documentation technique du projet backend",
"context": "Projet Imago, backend FastAPI"
}'
Configuration
| Variable | Défaut | Description |
|---|---|---|
ADMIN_API_KEY |
— | Clé maîtresse pour gérer les clients API |
JWT_SECRET_KEY |
— | Secret pour la signature des tokens |
AI_PROVIDER |
gemini |
gemini ou openrouter |
GEMINI_API_KEY |
— | Clé API Gemini |
DATABASE_URL |
PostgreSQL (Docker) / SQLite (Local) | URL de connexion (Postgres recommandé) |
REDIS_URL |
redis://localhost:6379/0 |
URL Redis pour ARQ |
STORAGE_BACKEND |
local |
local ou s3 |
S3_BUCKET |
— | Bucket S3/MinIO |
S3_ENDPOINT_URL |
— | Endpoint S3 custom (MinIO/R2) |
S3_ACCESS_KEY |
— | Clé d'accès S3 |
S3_SECRET_KEY |
— | Secret S3 |
SIGNED_URL_SECRET |
— | Secret HMAC pour URLs signées locales |
MAX_UPLOAD_SIZE_MB |
50 |
Taille max par upload |
OCR_ENABLED |
true |
Activer/désactiver l'OCR |
WORKER_MAX_JOBS |
10 |
Concurrence du worker ARQ |
WORKER_MAX_TRIES |
3 |
Retries max avant dead-letter |
DEBUG |
false |
Mode debug (console logging) |
Tests
# Tests rapides
make test
# Tests avec couverture
make test-cov
# Directement
python -m pytest tests/ -v --cov=app
76 tests couvrant : auth, isolation multi-tenant, rate limiting, pipeline, EXIF, OCR, AI, stockage.
CI/CD
Le pipeline GitHub Actions (.github/workflows/ci.yml) exécute :
- Lint — ruff check + format + mypy
- Tests — pytest + couverture
- Sécurité — bandit via ruff (règles S)
- Docker — build + smoke test (branche main uniquement)
Structure du projet
imago/
├── app/
│ ├── main.py # FastAPI + lifespan + Prometheus
│ ├── config.py # Settings Pydantic
│ ├── database.py # Engine SQLAlchemy async
│ ├── logging_config.py # structlog JSON/Console
│ ├── metrics.py # Prometheus custom metrics
│ ├── models/
│ │ ├── image.py # Modèle Image
│ │ └── client.py # APIClient + quotas
│ ├── schemas/
│ │ ├── __init__.py # Schémas images + quota
│ │ └── auth.py # Schémas Auth/Clients
│ ├── routers/
│ │ ├── images.py # CRUD images + signed URLs
│ │ ├── ai.py # Endpoints AI
│ │ ├── auth.py # Gestion clients
│ │ └── files.py # Serving signed local files
│ ├── dependencies/
│ │ └── auth.py # verify_api_key, require_scope
│ ├── middleware/
│ │ ├── rate_limit.py # slowapi configuration
│ │ └── logging_middleware.py # HTTP request logger
│ ├── services/
│ │ ├── storage.py # Sauvegarde isolée par client
│ │ ├── storage_backend.py # ABC + Local/S3 backends
│ │ ├── pipeline.py # Pipeline EXIF → OCR → AI
│ │ ├── ai_vision.py # Gemini/OpenRouter
│ │ ├── exif_service.py # Extraction EXIF
│ │ ├── ocr_service.py # Tesseract OCR
│ │ └── scraper.py # Scraping web
│ └── workers/
│ ├── redis_client.py # Redis pool partagé
│ └── image_worker.py # ARQ worker + retries
├── tests/ # 76 tests
├── .github/workflows/ci.yml # CI/CD pipeline
├── pyproject.toml # ruff, mypy, coverage config
├── Makefile # Commandes utiles
├── docker-compose.yml # API + Redis + Worker + PostgreSQL
├── Dockerfile
├── requirements.txt # Production deps
├── requirements-dev.txt # Dev deps (lint, test)
├── .pre-commit-config.yaml # Pre-commit hooks
├── worker.py # ARQ worker entrypoint
└── .env.example