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
+16 -6
View File
@@ -25,6 +25,9 @@ from app.schemas import (
from app.services import storage
from app.middleware import limiter, get_upload_rate_limit
from app.workers.image_worker import QUEUE_STANDARD, QUEUE_PREMIUM
from app.metrics import (
hub_images_uploaded, hub_images_deleted, hub_storage_used_bytes, hub_arq_jobs_enqueued
)
logger = logging.getLogger(__name__)
@@ -102,13 +105,14 @@ async def upload_image(
await db.commit()
await db.refresh(image)
# Enqueue dans ARQ (persistant, avec retry)
# Enqueue dans ARQ
arq_pool = request.app.state.arq_pool
await arq_pool.enqueue_job(
"process_image_task",
image.id,
str(client.id)
)
await arq_pool.enqueue_job("process_image_task", image.id, str(client.id))
# Metrics
hub_images_uploaded.labels(client_id=client.id).inc()
hub_storage_used_bytes.labels(client_id=client.id).set(client.storage_used_bytes)
hub_arq_jobs_enqueued.labels(queue="default").inc()
return UploadResponse(
id=image.id,
@@ -190,6 +194,8 @@ async def list_images(
height=img.height,
uploaded_at=img.uploaded_at,
processing_status=img.processing_status,
client_id=img.client_id,
client_name=client.name,
ai_tags=img.ai_tags,
ai_description=img.ai_description,
thumbnail_path=img.thumbnail_path,
@@ -444,6 +450,10 @@ async def delete_image(
await db.delete(image)
await db.commit()
# Metrics
hub_images_deleted.labels(client_id=client.id).inc()
hub_storage_used_bytes.labels(client_id=client.id).set(client.storage_used_bytes)
return DeleteResponse(deleted_id=image_id)