feat(storage): image gallery with thumbnails + lightbox

Add a List/Gallery toggle to the bucket browser that auto-switches to a
thumbnail grid when a listing is mostly images, and a keyboard-navigable
lightbox (prev/next/esc) for full-size previews. Download endpoint serves
inline with the correct image/* content-type (ECS stores octet-stream) so
previews render instead of forcing a download.
This commit is contained in:
mo
2026-06-27 22:59:50 +00:00
parent 930cd15f87
commit a408ed4423
2 changed files with 141 additions and 5 deletions
+19 -3
View File
@@ -218,24 +218,40 @@ async def list_objects(
return JSONResponse({"ok": False, "error": str(exc)}, status_code=502)
_MEDIA_BY_EXT = {
"jpeg": "image/jpeg", "jpg": "image/jpeg", "png": "image/png", "gif": "image/gif",
"webp": "image/webp", "bmp": "image/bmp", "svg": "image/svg+xml",
}
@router.get("/buckets/{bucket}/download")
async def download_object(bucket: str, key: str = Query(...)):
async def download_object(bucket: str, key: str = Query(...), inline: bool = Query(False)):
try:
_track("download")
s3 = _client()
obj = s3.get_object(Bucket=bucket, Key=key)
body = obj["Body"]
filename = key.split("/")[-1] or "download"
media = obj.get("ContentType") or "application/octet-stream"
ext_media = _MEDIA_BY_EXT.get(_ext(key))
stored = obj.get("ContentType")
# ECS often stores everything as octet-stream — trust the extension for
# known image types so previews render inline instead of downloading.
media = ext_media or stored or "application/octet-stream"
if ext_media and (not stored or stored == "application/octet-stream"):
media = ext_media
def stream():
while chunk := body.read(1024 * 256):
yield chunk
disp = "inline" if inline else "attachment"
return StreamingResponse(
stream(),
media_type=media,
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
headers={
"Content-Disposition": f'{disp}; filename="{filename}"',
"Cache-Control": "private, max-age=300",
},
)
except ClientError as exc:
return JSONResponse({"ok": False, "error": str(exc)}, status_code=404)