Add admin panel, WebSocket support, and API versioning
CI / Lint & Format (push) Failing after 28s
CI / Tests (push) Has been skipped
CI / Security Scan (push) Failing after 8s
CI / Docker Build (push) Has been skipped

Introduce an admin portal (React + Nginx), WebSocket routing, and
API versioning middleware with `/api/v1/` prefix deprecation.
Add master API key authentication, new Prometheus metrics for AI
token consumption and active WebSockets, and extend S3 config
with a public endpoint URL. Update test paths and fixtures to
align with the new routing structure.
This commit is contained in:
2026-06-22 11:25:22 -04:00
parent d68deb9c74
commit 2f2a5e9b4a
158 changed files with 64811 additions and 503 deletions
+59 -377
View File
@@ -1,403 +1,85 @@
# Imago
# Imago Hub
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.
Imago est un hub Backend & Traitement IA multi-tenant open-source dédié à la gestion d'images. Conçu pour propulser des applications frontend riches (comme Shaarli), il offre un stockage média hautement concurrent, des traitements asynchrones planifiés, une observabilité complète du pipeline et des intégrations poussées avec l'Intelligence Artificielle.
---
## ✨ Fonctionnalités Principales
## Fonctionnalités
- 📸 **API d'Upload & Stockage :** Stockage d'images isolé par client (multi-tenant), génération automatique de miniatures, support S3 (MinIO) ou stockage local.
- 🔍 **Extraction de Métadonnées :** Découverte en temps réel des données EXIF, géolocalisation GPS, résolution et caractéristiques des fichiers.
- 📝 **Pipelines OCR :** Extraction de texte hors-ligne intégrée aux images via le moteur Tesseract.
- 🤖 **Vision IA Intégrée :** Résumé automatique de contenu, description de scènes, et classification par tags (Propulsé par les API Gemini & OpenRouter).
- ⚡ **Flux de Données en Temps Réel :** Streaming d'événements WebSocket encapsulant les opérations lourdes de la file d'attente (ARQ / Redis).
- 🔐 **Sécurité & Multi-tenancy :** Authentification par clé API, gestion de quotas (Rate Limiting) basés sur des plans, isolation totale des données entre les clients.
- 💻 **Portail d'Administration :** Interface React complète pour gérer les clients API, surveiller la file d'attente et consulter les métriques.
| 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 |
## 📚 Documentation Complète
---
Pour une compréhension approfondie du projet, veuillez consulter les documents suivants dans le répertoire `doc/` :
## Stack technique
- 📖 **[Guide d'Utilisateur (USER_GUIDE.md)](doc/USER_GUIDE.md) :** Explications détaillées sur l'utilisation des fonctionnalités, le portail admin et les concepts clés.
- 🏗️ **[Architecture (ARCHITECTURE.md)](doc/ARCHITECTURE.md) :** Plongée technique dans la stack (FastAPI, React, Redis, ARQ, MinIO) et le fonctionnement du pipeline asynchrone.
- 📡 **[Guide de l'API (API_GUIDE.md)](doc/API_GUIDE.md) :** Référence complète des endpoints REST, de l'authentification et des exemples de requêtes.
- 🔌 **[Guide SDK Python](docs/sdk.md) & [WebSockets](docs/websocket.md) :** (Dans le dossier `docs/`) Pour l'intégration programmatique bas-niveau.
```
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
```
*Note: La spécification OpenAPI pour l'écosystème `/api/v1/` peut être consultée dynamiquement sur `http://localhost:8000/docs` (une fois l'application lancée).*
---
## 🚀 Installation via Docker (Recommandé)
## Installation
Le projet fournit une configuration `docker-compose.yml` complète qui orchestre tous les services nécessaires :
1. **admin** : Portail d'administration (React + Nginx) sur le port `3000`
2. **backend** : API Imago (FastAPI) sur le port `8000`
3. **db** : Base de données PostgreSQL
4. **redis** : Serveur Redis (Broker & Cache)
5. **worker** : Worker ARQ pour les tâches IA & OCR en arrière-plan
6. **minio** : Serveur de stockage objet compatible S3 sur les ports `9000` (API) et `9001` (Console)
### Prérequis
- Python 3.12+ (3.14 supporté)
- Tesseract OCR installé sur le système
- Redis (optionnel, pour ARQ worker)
### Étapes de déploiement
```bash
# 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.txt` appliquent automatiquement des versions compatibles Python 3.14 (notamment pour Pillow et Pydantic).
```bash
# 1. Cloner et installer
git clone <repo>
# 1. Cloner le dépôt
git clone <repo_url>
cd imago
# Production
# 2. Configuration de l'environnement
cp .env.example .env
# (Éditez le fichier .env si nécessaire, notamment pour ajouter vos clés API IA : GEMINI_API_KEY ou OPENROUTER_API_KEY)
# 3. Lancer toute la stack
docker-compose up -d --build
# 4. Appliquer les migrations de base de données (si non faites automatiquement)
docker-compose exec backend alembic upgrade head
```
L'API sera disponible sur [http://localhost:8000](http://localhost:8000) et le panel admin sur [http://localhost:3000](http://localhost:3000).
---
## 💻 Installation Locale (Développement)
Si vous souhaitez exécuter le projet sans Docker (nécessite Python 3.10+, PostgreSQL ou SQLite, Redis et Tesseract OCR) :
```bash
# 1. Setup Venv
pip install -r requirements.txt
# Développement (inclut linting, tests, pre-commit)
pip install -r requirements-dev.txt
pre-commit install
# 2. Configuration
# 2. Base de données & Migrations
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)
# 3. Démarrage
python run.py # API Endpoint -> http://localhost:8000
python worker.py # Required ARQ Redis Jobs
```
### Avec Docker
## 🧪 Tests
Nous exécutons une suite de tests automatisée résiliente (80+ tests) simulant nativement des buckets S3 et l'infrastructure ARQ sans dépendance réseau externe :
```bash
docker-compose up -d # API + Redis + Worker + PostgreSQL
```
### Commandes utiles (Makefile)
```bash
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 `/health` et `/metrics`) nécessitent une clé API valide passée dans le header `X-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.
```bash
curl -X POST http://localhost:8000/images/upload \
-H "X-API-Key: rEYQtw3LxJJlcmBq-cgQcdeY74JcpJ45COuFWokmxPg" \
-F "[email protected]"
```
Réponse :
```json
{
"id": 1,
"uuid": "a1b2c3d4-...",
"original_name": "photo.jpg",
"status": "pending",
"message": "Image uploadée — traitement AI en cours"
}
```
### Polling du statut
```bash
curl http://localhost:8000/images/1/status -H "X-API-Key: rEYQtw3LxJJlcmBq-cgQcdeY74JcpJ45COuFWokmxPg"
```
```json
{
"id": 1,
"status": "done",
"started_at": "2024-01-15T10:30:00",
"done_at": "2024-01-15T10:30:08"
}
```
### Détail complet
```bash
curl http://localhost:8000/images/1 -H "X-API-Key: rEYQtw3LxJJlcmBq-cgQcdeY74JcpJ45COuFWokmxPg"
```
```json
{
"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
```bash
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
```bash
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
```bash
# Tests rapides
make test
# Tests avec couverture
make test-cov
# Directement
python -m pytest tests/ -v --cov=app
# Ou directement :
pytest tests/ -v
```
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 :
1. **Lint** — ruff check + format + mypy
2. **Tests** — pytest + couverture
3. **Sécurité** — bandit via ruff (règles S)
4. **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
```
Veuillez consulter le `CHANGELOG.md` pour suivre les développements majeurs.