from datetime import datetime from typing import Optional, List, Dict, Any, Generic, TypeVar from pydantic import BaseModel from uuid import UUID T = TypeVar("T") # Pagination schemas class PaginationMeta(BaseModel): page: int limit: int total: int total_pages: int class PaginatedResponse(BaseModel, Generic[T]): items: List[T] pagination: PaginationMeta # Project schemas class ProjectCreate(BaseModel): name: str description: Optional[str] = None is_public: bool = True class ProjectResponse(BaseModel): id: UUID name: str description: Optional[str] is_public: bool created_at: datetime updated_at: datetime created_by: str class Config: from_attributes = True # Package schemas class PackageCreate(BaseModel): name: str description: Optional[str] = None class PackageResponse(BaseModel): id: UUID project_id: UUID name: str description: Optional[str] created_at: datetime updated_at: datetime class Config: from_attributes = True # Artifact schemas class ArtifactResponse(BaseModel): id: str size: int content_type: Optional[str] original_name: Optional[str] created_at: datetime created_by: str ref_count: int format_metadata: Optional[Dict[str, Any]] = None class Config: from_attributes = True # Tag schemas class TagCreate(BaseModel): name: str artifact_id: str class TagResponse(BaseModel): id: UUID package_id: UUID name: str artifact_id: str created_at: datetime created_by: str class Config: from_attributes = True # Upload response class UploadResponse(BaseModel): artifact_id: str size: int project: str package: str tag: Optional[str] format_metadata: Optional[Dict[str, Any]] = None deduplicated: bool = False # Resumable upload schemas class ResumableUploadInitRequest(BaseModel): """Request to initiate a resumable upload""" expected_hash: str # SHA256 hash of the file (client must compute) filename: str content_type: Optional[str] = None size: int tag: Optional[str] = None class ResumableUploadInitResponse(BaseModel): """Response from initiating a resumable upload""" upload_id: Optional[str] # None if file already exists already_exists: bool artifact_id: Optional[str] = None # Set if already_exists is True chunk_size: int # Recommended chunk size for parts class ResumableUploadPartResponse(BaseModel): """Response from uploading a part""" part_number: int etag: str class ResumableUploadCompleteRequest(BaseModel): """Request to complete a resumable upload""" tag: Optional[str] = None class ResumableUploadCompleteResponse(BaseModel): """Response from completing a resumable upload""" artifact_id: str size: int project: str package: str tag: Optional[str] class ResumableUploadStatusResponse(BaseModel): """Status of a resumable upload""" upload_id: str uploaded_parts: List[int] total_uploaded_bytes: int # Consumer schemas class ConsumerResponse(BaseModel): id: UUID package_id: UUID project_url: str last_access: datetime created_at: datetime class Config: from_attributes = True # Health check class HealthResponse(BaseModel): status: str version: str = "1.0.0"