chore: commit pending phase-3 work and add TEST_ACCOUNTS.md

Includes planning artifacts (03-CONTEXT, 03-DISCUSSION-LOG, 03-02-SUMMARY),
integration test script, MinIO/auth/docker fixes, and local dev account reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-05-24 11:30:56 +02:00
parent 254e756cb8
commit a5994d9ff4
14 changed files with 902 additions and 19 deletions
+11 -10
View File
@@ -45,11 +45,11 @@ class MinIOBackend(StorageBackend):
endpoint=endpoint,
access_key=access_key,
secret_key=secret_key,
secure=secure, # False for Docker internal HTTP traffic between containers
secure=secure,
)
# Second client for presigned URL generation — uses browser-accessible hostname.
# Falls back to internal client endpoint if not configured.
# RESEARCH.md Finding 3 — dual-client pattern to avoid Docker hostname pitfall (T-03-10).
# MINIO_SERVER_URL on MinIO rewrites the presigned URL host at the server side,
# so both clients can point at the internal endpoint — the signature is valid
# for localhost:9000 because MinIO itself generated it that way (T-03-10).
self._public_client = Minio(
endpoint=(public_endpoint or endpoint),
access_key=access_key,
@@ -121,15 +121,16 @@ class MinIOBackend(StorageBackend):
async def generate_presigned_put_url(
self, object_key: str, expires_minutes: int = 15
) -> str:
"""Return a presigned PUT URL using the public-endpoint client.
"""Return a presigned PUT URL with a browser-resolvable hostname.
Uses self._public_client so the returned URL contains a browser-resolvable
hostname (not the Docker-internal 'minio:9000' address).
RESEARCH.md Finding 2: presigned_put_object(bucket, key, expires=timedelta).
RESEARCH.md Finding 3: dual-client pattern for Docker hostname pitfall (T-03-10).
Generates the URL via the internal client (minio:9000 — reachable from
the backend container), then rewrites the host to the public endpoint
(localhost:9000 — reachable from the browser). T-03-10 / Finding 3.
"""
# MINIO_SERVER_URL on the MinIO container causes it to embed the public
# hostname (localhost:9000) in signed URLs, so the internal client suffices.
return await asyncio.to_thread(
self._public_client.presigned_put_object,
self._client.presigned_put_object,
self._bucket,
object_key,
timedelta(minutes=expires_minutes),