fix: Support local file storage uploads in addition to S3
Build and Push Outline MCP Docker Image / build (push) Successful in 8s
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:
@@ -633,27 +633,50 @@ async def upload_endpoint(request: Request) -> JSONResponse:
|
|||||||
status_code=502,
|
status_code=502,
|
||||||
)
|
)
|
||||||
|
|
||||||
attachment_data = create_response.get("data", {})
|
response_data = create_response.get("data", {})
|
||||||
upload_url = attachment_data.get("uploadUrl")
|
upload_url = response_data.get("uploadUrl")
|
||||||
|
form_fields = response_data.get("form", {})
|
||||||
|
attachment_data = response_data.get("attachment", {})
|
||||||
if not upload_url:
|
if not upload_url:
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
{"ok": False, "error": "Outline API did not return an uploadUrl"},
|
{"ok": False, "error": "Outline API did not return an uploadUrl"},
|
||||||
status_code=502,
|
status_code=502,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. Stream the request body directly to S3 upload_url
|
# Resolve relative uploadUrl against the Outline base URL
|
||||||
|
if upload_url.startswith("/"):
|
||||||
|
upload_url = f"{OUTLINE_API_URL}{upload_url}"
|
||||||
|
|
||||||
|
is_local_storage = bool(form_fields)
|
||||||
|
|
||||||
|
# 3. Upload the file content to storage
|
||||||
try:
|
try:
|
||||||
# Pre-signed S3 PUT URLs usually enforce exact headers
|
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 = {
|
headers = {
|
||||||
"Content-Type": content_type,
|
"Content-Type": content_type,
|
||||||
"Content-Length": str(size),
|
"Content-Length": str(size),
|
||||||
}
|
}
|
||||||
|
logger.info(f"Streaming file content to S3 storage (size={size} bytes)")
|
||||||
logger.info(f"Streaming file content to storage (size={size} bytes)")
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
put_response = await client.put(
|
put_response = await client.put(
|
||||||
upload_url,
|
upload_url,
|
||||||
content=request.stream(),
|
content=body,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
timeout=600.0,
|
timeout=600.0,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user