4.4 KiB
4.4 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
[2.0.0] - 2026-06-22
Security
- API Key hashing now uses SHA-256 + pepper (SECRET_KEY) with timing-safe comparison via
secrets.compare_digest() - CORS validation rejects
["*"]whenallow_credentials=True(Pydantic model_validator) - Scope validation:
ClientCreate.scopesrejects invalid values viafield_validator - Master key comparison uses constant-time compare
Added
- Dead Letter Queue (DLQ): Failed ARQ jobs pushed to Redis DLQ after
PIPELINE_MAX_RETRIESattempts. Admin endpoints to list/retry/clear dead jobs (GET/POST/DELETE /admin/api/queue/dead) - Circuit Breaker AI: All AI calls wrapped with
asyncio.wait_for()+ exponential backoff retry (configurable viaAI_REQUEST_TIMEOUT,AI_MAX_RETRIES) - Request ID Middleware:
X-Request-IDheader injected in all responses + bound to structlog context - Rate Limiting per Plan: Dynamic rate limits based on client plan (free/standard/premium) via ContextVar
- Redis Rate Limit Storage: Optional persistence via
RATE_LIMIT_STORAGE_URL - Worker script:
worker.pyfor standalone ARQ worker launch - Docker Compose stack: PostgreSQL 16 + Redis 7 + MinIO + API + Worker
.env.example: All configuration variables documented- Grafana Dashboard:
docs/grafana-dashboard.jsonwith 10 panels (images, tokens, pipeline, storage, WebSocket, errors) - Database indexes: Composite indexes
(client_id, uploaded_at)and(client_id, processing_status)on images table
Changed
APP_VERSIONaligned to2.0.0in config (was1.0.0)delete_files()now fully async — usesawait backend.delete()instead of fire-and-forgetensure_future()- Upload flow: files deleted on DB commit failure (no orphaned S3 files)
- Gemini client: cached with TTL — recreated if
GEMINI_API_KEYchanges parse_redis_url()extracted toapp/workers/redis_client.py(DRY — was duplicated in main.py and image_worker.py)- Rate limit key function encodes plan for per-plan bucket isolation
_FallbackArqPoolmoved toapp/workers/arq_fallback.py(avoids circular imports)- ARQ worker: configurable
PIPELINE_TIMEOUTandPIPELINE_MAX_RETRIES,on_shutdownhandler - Pipeline:
push_dead_job()on final retry failure marks image asERRORwith DLQ metadata - Admin panel: VITE_API_BASE_URL changed from internal Docker hostname to
http://localhost:8000
Fixed
- ARQ fallback now returns explicit warning in
UploadResponse.messageinstead of silently ignoring - Removed obsolete comment in
ai_vision.pyaboutStorageBackend.read - Removed signal handler instability in test fixtures
- Fixed Docker Compose admin connectivity (browser couldn't resolve internal
backendhostname)
[1.0.0] - Previous
Added (Phase 3: DX)
- WebSockets / Real-Time:
- Subscriptions on per-image workflows (
/ws/pipeline/{image_id}). - Redis 60-second Event Buffering for reconnections.
- Admin Monitor WebSocket feed (
/ws/admin/monitor).
- Subscriptions on per-image workflows (
- REST API Versioning:
- Global
/api/v1/prefix for structural stability. - Added strict API Sunset Date tracking middleware.
- Global
- Python SDK (
imago-client):- Publishable PyPI-ready package located inside
sdk/. - Automatic
httpxHTTP reconnects, WebSockets streaming natively wrapped, and robust generic Error classes (ImagoError,AuthError, etc.).
- Publishable PyPI-ready package located inside
- Admin Backend APIs:
- Complete JSON statistics (
/admin/api/stats), global client configurations (/admin/api/clients/*) and Queue observability natively piped to the existing React Interface.
- Complete JSON statistics (
- Shaarli Integrations:
- Skeleton integration middleware (
integration/shaarli) capable of bridging Shaarli's events right into Imago using the official SDK.
- Skeleton integration middleware (
- Extensive Documentation:
- Modular guides provided inside
docs/detailing WebSockets, Integrations, and SDK consumption.
- Modular guides provided inside
Changed
- All API routing endpoints have been moved behind the
/api/v1namespace. Existing API Clients must update base URLs from/to/api/v1/.
Fixed
- Fixed unawaited coroutines in async mocks causing
TypeErroron Pytest environments. - Fixed an incorrect payload key mapping in
APIClientresponses causing 500s (AttributeError). - Mocked S3 and ARQ correctly globally to ensure local automated tests pass completely isolated from network logic.