fix: make media uploads work behind docker
All checks were successful
CI / test (push) Successful in 26s

- add S3_PUBLIC_ENDPOINT_URL for browser-reachable presigned urls

- support both public/internal file url validation

- configure MinIO bucket CORS in minio-init

- update env examples and docs
This commit is contained in:
2026-03-07 22:52:05 +03:00
parent f95a0e9727
commit ffd63018d6
6 changed files with 32 additions and 9 deletions

View File

@@ -38,13 +38,16 @@ def _sanitize_filename(file_name: str) -> str:
def _build_file_url(bucket: str, object_key: str) -> str:
base = settings.s3_endpoint_url.rstrip("/")
base = (settings.s3_public_endpoint_url or settings.s3_endpoint_url).rstrip("/")
encoded_key = quote(object_key)
return f"{base}/{bucket}/{encoded_key}"
def _allowed_file_url_prefix() -> str:
return f"{settings.s3_endpoint_url.rstrip('/')}/{settings.s3_bucket_name}/"
def _allowed_file_url_prefixes() -> tuple[str, ...]:
endpoints = [settings.s3_endpoint_url]
if settings.s3_public_endpoint_url:
endpoints.append(settings.s3_public_endpoint_url)
return tuple(f"{endpoint.rstrip('/')}/{settings.s3_bucket_name}/" for endpoint in endpoints)
def _validate_media(file_type: str, file_size: int) -> None:
@@ -54,10 +57,10 @@ def _validate_media(file_type: str, file_size: int) -> None:
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="File size exceeds limit")
def _get_s3_client():
def _get_s3_client(endpoint_url: str):
return boto3.client(
"s3",
endpoint_url=settings.s3_endpoint_url,
endpoint_url=endpoint_url,
aws_access_key_id=settings.s3_access_key,
aws_secret_access_key=settings.s3_secret_key,
region_name=settings.s3_region,
@@ -73,7 +76,8 @@ async def generate_upload_url(payload: UploadUrlRequest) -> UploadUrlResponse:
bucket = settings.s3_bucket_name
try:
s3_client = _get_s3_client()
presign_endpoint = settings.s3_public_endpoint_url or settings.s3_endpoint_url
s3_client = _get_s3_client(presign_endpoint)
upload_url = s3_client.generate_presigned_url(
"put_object",
Params={
@@ -103,7 +107,7 @@ async def store_attachment_metadata(
payload: AttachmentCreateRequest,
) -> AttachmentRead:
_validate_media(payload.file_type, payload.file_size)
if not payload.file_url.startswith(_allowed_file_url_prefix()):
if not payload.file_url.startswith(_allowed_file_url_prefixes()):
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Invalid file URL")
message = await get_message_by_id(db, payload.message_id)