5349f21752
New FastAPI microservice (port 8020) providing unified blob storage via PUT/GET/DELETE/LIST HTTP API. Local filesystem backend is the default (zero extra deps). S3-compatible and WebDAV backends are built in. Backend is switchable at runtime via POST /migrate, which copies all objects to the new backend, verifies each one, atomically switches, then cleans up the old backend. WebDAV XML parsing uses defusedxml to prevent XXE attacks. Wired into docker-compose (storage_data volume) and registered in the backend service-health poller as 'storage-service'. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
35 lines
1.2 KiB
Python
35 lines
1.2 KiB
Python
from abc import ABC, abstractmethod
|
|
|
|
|
|
class AbstractStorageBackend(ABC):
|
|
"""Common interface every storage backend must implement."""
|
|
|
|
@property
|
|
@abstractmethod
|
|
def driver_name(self) -> str:
|
|
"""Short identifier returned in /health: 'local', 's3', or 'webdav'."""
|
|
|
|
@abstractmethod
|
|
async def put(self, bucket: str, key: str, data: bytes) -> None:
|
|
"""Store *data* under bucket/key. Creates bucket/intermediate dirs as needed."""
|
|
|
|
@abstractmethod
|
|
async def get(self, bucket: str, key: str) -> bytes:
|
|
"""Return the stored bytes. Raises KeyError if the object does not exist."""
|
|
|
|
@abstractmethod
|
|
async def delete(self, bucket: str, key: str) -> None:
|
|
"""Delete the object. No-op if it does not exist."""
|
|
|
|
@abstractmethod
|
|
async def list_keys(self, bucket: str) -> list[str]:
|
|
"""Return all keys stored in *bucket*. Returns [] if bucket is empty/absent."""
|
|
|
|
@abstractmethod
|
|
async def exists(self, bucket: str, key: str) -> bool:
|
|
"""Return True if the object exists."""
|
|
|
|
@abstractmethod
|
|
async def test_connection(self) -> None:
|
|
"""Verify the backend is reachable and writable. Raise on failure."""
|