fix: Support local file storage uploads in addition to S3
Build and Push Outline MCP Docker Image / build (push) Successful in 8s

The upload endpoint now detects whether Outline uses local file storage
(multipart form POST) or S3 (pre-signed PUT URL) and handles both.
This commit is contained in:
2026-05-25 02:44:13 +00:00
parent d637394c0e
commit c68bb66de1
+41 -18
View File
@@ -633,31 +633,54 @@ async def upload_endpoint(request: Request) -> JSONResponse:
status_code=502,
)
attachment_data = create_response.get("data", {})
upload_url = attachment_data.get("uploadUrl")
response_data = create_response.get("data", {})
upload_url = response_data.get("uploadUrl")
form_fields = response_data.get("form", {})
attachment_data = response_data.get("attachment", {})
if not upload_url:
return JSONResponse(
{"ok": False, "error": "Outline API did not return an uploadUrl"},
status_code=502,
)
# 3. Stream the request body directly to S3 upload_url
try:
# Pre-signed S3 PUT URLs usually enforce exact headers
headers = {
"Content-Type": content_type,
"Content-Length": str(size),
}
# Resolve relative uploadUrl against the Outline base URL
if upload_url.startswith("/"):
upload_url = f"{OUTLINE_API_URL}{upload_url}"
logger.info(f"Streaming file content to storage (size={size} bytes)")
async with httpx.AsyncClient() as client:
put_response = await client.put(
upload_url,
content=request.stream(),
headers=headers,
timeout=600.0,
)
put_response.raise_for_status()
is_local_storage = bool(form_fields)
# 3. Upload the file content to storage
try:
body = await request.body()
if is_local_storage:
# Local/database file storage: POST multipart form with fields from Outline
logger.info(f"Uploading via local storage endpoint (size={size} bytes)")
files = {"file": (filename, body, content_type)}
async with httpx.AsyncClient() as client:
post_response = await client.post(
upload_url,
data=form_fields,
files=files,
headers={"Authorization": f"Bearer {OUTLINE_API_TOKEN}"},
timeout=600.0,
)
post_response.raise_for_status()
else:
# S3-compatible storage: PUT directly to pre-signed URL
headers = {
"Content-Type": content_type,
"Content-Length": str(size),
}
logger.info(f"Streaming file content to S3 storage (size={size} bytes)")
async with httpx.AsyncClient() as client:
put_response = await client.put(
upload_url,
content=body,
headers=headers,
timeout=600.0,
)
put_response.raise_for_status()
logger.info("Upload to storage completed successfully")