refactor: audit and reduce HetznerStorageClient wrapper duplication

Summary

app/utils/hetzner_storage.py is a 626-line wrapper over the MinIO Python SDK. A significant portion of this code duplicates functionality already provided by the SDK — presigned URL generation, bucket creation, object existence checks, and metadata attachment. This creates a maintenance surface that must track SDK changes.

Problem

  • Presigned URL generation (GET and PUT) is re-implemented when minio.Client.presigned_get_object / presigned_put_object already exist
  • Bucket existence and auto-creation logic reimplements minio.Client.bucket_exists + make_bucket
  • A custom ProgressLoggingThread class is used for upload progress — the MinIO SDK's progress parameter accepts a threading.Thread subclass natively
  • Object metadata attachment mirrors the SDK's metadata parameter in put_object
  • Global singleton pattern with manual initialization is unnecessary — dependency injection via FastAPI's Depends is idiomatic

Long-term consideration: boto3 (Apache 2.0) is the industry-standard S3 SDK. Hetzner Object Storage is S3-compatible, so boto3 with endpoint_url works directly. boto3 has broader tooling, more active maintenance, and a larger community than the MinIO SDK for client-side use.

Proposed Actions

  1. Immediate: Audit each method in HetznerStorageClient against the MinIO SDK docs and remove any method that directly wraps an existing SDK call without adding logic
  2. Short-term: Replace the singleton pattern with a FastAPI dependency (Depends(get_storage_client))
  3. Medium-term: Evaluate migrating from minio SDK to boto3 for portability and ecosystem breadth

Impact

Dimension Current After (Step 1+2)
LOC 626 lines Est. 200–300 lines
SDK drift risk High (manual reimplementation) Low (delegate to SDK)
Testability Requires mocking entire class Mock at SDK boundary
Portability MinIO SDK only boto3 path opens S3-compatible options
FastAPI integration Global singleton Idiomatic Depends injection

Files Affected

  • app/utils/hetzner_storage.py — audit and reduce
  • Any service/task files importing HetznerStorageClient — update instantiation if DI pattern is adopted
  • pyproject.toml — optionally add boto3 if migration is chosen