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:
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
Data models for the Imago Client SDK.
|
||||
"""
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
@dataclass
|
||||
class ExifCamera:
|
||||
make: Optional[str] = None
|
||||
model: Optional[str] = None
|
||||
lens: Optional[str] = None
|
||||
iso: Optional[int] = None
|
||||
aperture: Optional[str] = None
|
||||
shutter_speed: Optional[str] = None
|
||||
focal_length: Optional[str] = None
|
||||
flash: Optional[int] = None
|
||||
orientation: Optional[int] = None
|
||||
software: Optional[str] = None
|
||||
taken_at: Optional[datetime] = None
|
||||
|
||||
@dataclass
|
||||
class ExifGPS:
|
||||
latitude: Optional[float] = None
|
||||
longitude: Optional[float] = None
|
||||
altitude: Optional[float] = None
|
||||
has_gps: bool = False
|
||||
maps_url: Optional[str] = None
|
||||
|
||||
@dataclass
|
||||
class ExifData:
|
||||
camera: Optional[ExifCamera] = None
|
||||
gps: Optional[ExifGPS] = None
|
||||
raw: Optional[Dict[str, Any]] = None
|
||||
|
||||
@dataclass
|
||||
class OcrData:
|
||||
has_text: bool = False
|
||||
text: Optional[str] = None
|
||||
language: Optional[str] = None
|
||||
confidence: Optional[float] = None
|
||||
|
||||
@dataclass
|
||||
class AiData:
|
||||
description: Optional[str] = None
|
||||
tags: List[str] = field(default_factory=list)
|
||||
confidence: Optional[float] = None
|
||||
model_used: Optional[str] = None
|
||||
processed_at: Optional[datetime] = None
|
||||
prompt_tokens: Optional[int] = None
|
||||
output_tokens: Optional[int] = None
|
||||
|
||||
@dataclass
|
||||
class ProcessingInfo:
|
||||
status: str
|
||||
error: Optional[str] = None
|
||||
started_at: Optional[datetime] = None
|
||||
done_at: Optional[datetime] = None
|
||||
|
||||
@dataclass
|
||||
class HubImage:
|
||||
id: int
|
||||
uuid: str
|
||||
original_name: str
|
||||
mime_type: Optional[str] = None
|
||||
file_size: Optional[int] = None
|
||||
width: Optional[int] = None
|
||||
height: Optional[int] = None
|
||||
uploaded_at: Optional[datetime] = None
|
||||
processing_status: str = "pending"
|
||||
client_id: Optional[str] = None
|
||||
client_name: Optional[str] = None
|
||||
thumbnail_path: Optional[str] = None
|
||||
|
||||
# Extended data
|
||||
exif: Optional[ExifData] = None
|
||||
ocr: Optional[OcrData] = None
|
||||
ai: Optional[AiData] = None
|
||||
processing: Optional[ProcessingInfo] = None
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "HubImage":
|
||||
# Simplified factory method from a JSON API response
|
||||
return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__ and k not in ("exif", "ocr", "ai", "processing")})
|
||||
Reference in New Issue
Block a user