""" StorageBackend ABC for DocuVault. Mirrors backend/ai/base.py — declares the abstract interface that all storage backends (MinIO, OneDrive, Google Drive, Nextcloud, WebDAV) must implement. Seven abstract methods define the contract: put_object — store bytes, return object key get_object — fetch bytes by key delete_object — remove object by key presigned_get_url — generate a time-limited download URL health_check — verify backend connectivity generate_presigned_put_url — generate a presigned PUT URL for direct browser upload stat_object — return authoritative file size from object storage """ from abc import ABC, abstractmethod class StorageBackend(ABC): """Abstract base class for DocuVault object storage backends.""" @abstractmethod async def put_object( self, user_id: str, document_id: str, file_bytes: bytes, extension: str, content_type: str, ) -> str: """Store bytes and return the generated object key. The key MUST follow the STORE-02 schema: {user_id}/{document_id}/{uuid4()}{ext}. The human-readable filename MUST NOT appear in the returned key. """ ... @abstractmethod async def get_object(self, object_key: str) -> bytes: """Fetch object bytes by key. Raises on missing key.""" ... @abstractmethod async def delete_object(self, object_key: str) -> None: """Delete an object by key. No-op if the key does not exist.""" ... @abstractmethod async def presigned_get_url(self, object_key: str, expires_minutes: int = 60) -> str: """Return a time-limited pre-signed download URL for the object.""" ... @abstractmethod async def health_check(self) -> bool: """Return True if the backend is reachable and operational.""" ... @abstractmethod async def generate_presigned_put_url( self, object_key: str, expires_minutes: int = 15 ) -> str: """Return a presigned PUT URL for direct browser-to-storage upload. RESEARCH.md Finding 3 — public client requirement: the returned URL must use a browser-resolvable hostname (not the internal Docker hostname). The presigned URL is tied to the exact object_key and expires after expires_minutes (default 15 — D-05). """ ... @abstractmethod async def stat_object(self, object_key: str) -> int: """Return the authoritative file size in bytes for the given object. RESEARCH.md Finding 5 — returns the size (.size attribute) from the object storage stat call. This is the only trusted source of file size; never use client-supplied size values (D-07, T-03-05). Raises S3Error(code='NoSuchKey') if the object does not exist. """ ...