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
+1 -1
View File
@@ -84,7 +84,7 @@ path_separator = os
# database URL. This is consumed by the user-maintained env.py script only.
# other means of configuring database URLs may be customized within the env.py
# file.
sqlalchemy.url = %(DATABASE_MIGRATE_URL)s
sqlalchemy.url = postgresql+psycopg://placeholder:placeholder@localhost/docuvault
[post_write_hooks]
+1
View File
@@ -173,6 +173,7 @@ async def register(
used_bytes=0,
)
session.add(new_user)
await session.flush() # persist User before Quota FK
session.add(quota)
await session.commit()
await session.refresh(new_user)
+1 -1
View File
@@ -1,7 +1,7 @@
import asyncio
from contextlib import asynccontextmanager
import aioredis
from redis import asyncio as aioredis
from fastapi import FastAPI, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
+3 -3
View File
@@ -2,6 +2,7 @@ fastapi>=0.111
uvicorn[standard]>=0.29
python-multipart
pydantic-settings>=2.2
pydantic[email]>=2.0
anthropic>=0.26
openai>=1.30
PyMuPDF>=1.24
@@ -16,11 +17,10 @@ sqlalchemy[asyncio]>=2.0.49
psycopg[binary]>=3.3.4
alembic>=1.18.4
minio>=7.2.20
celery[redis]>=5.6.3
redis>=7.4.0
celery[redis]>=5.5.0
redis>=4.6.0
aiosqlite>=0.20.0
PyJWT>=2.8.0
pwdlib[argon2]>=0.2.1
pyotp>=2.9.0
aioredis>=2.0.0
slowapi>=0.1.9
+1
View File
@@ -423,6 +423,7 @@ async def bootstrap_admin(session: AsyncSession) -> None:
used_bytes=0,
)
session.add(admin_user)
await session.flush() # persist User first so Quota FK is satisfied
session.add(quota)
await session.commit()
logger.info("Admin account bootstrapped for %s", settings.admin_email)
+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),