Add admin panel, WebSocket support, and API versioning
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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user