feat(storage): inline preview for json/csv/log/text files

Add a /preview endpoint that range-reads the first chunk of an object and
returns it as text. Browser gets an eye action (list + gallery) opening a
modal that pretty-prints JSON, renders CSV/TSV as a table, and shows logs/
text/yaml/xml verbatim, with a truncation note and download link.
This commit is contained in:
mo
2026-06-27 23:08:59 +00:00
parent a408ed4423
commit 1432a8429a
2 changed files with 130 additions and 9 deletions
+33
View File
@@ -257,6 +257,39 @@ async def download_object(bucket: str, key: str = Query(...), inline: bool = Que
return JSONResponse({"ok": False, "error": str(exc)}, status_code=404)
@router.get("/buckets/{bucket}/preview")
async def preview_object(bucket: str, key: str = Query(...), max_bytes: int = Query(131072, le=1048576)):
"""Return the first chunk of an object as text for inline preview."""
try:
_track("preview")
s3 = _client()
head = None
try:
head = s3.head_object(Bucket=bucket, Key=key)
except Exception:
pass
total = int(head.get("ContentLength", 0)) if head else 0
obj = s3.get_object(Bucket=bucket, Key=key, Range=f"bytes=0-{max_bytes - 1}")
raw = obj["Body"].read()
truncated = (total > len(raw)) or (len(raw) >= max_bytes)
text = raw.decode("utf-8", errors="replace")
return {
"ok": True,
"bucket": bucket,
"key": key,
"ext": _ext(key),
"size": total or len(raw),
"size_human": _human_size(total or len(raw)),
"bytes_read": len(raw),
"truncated": truncated,
"content": text,
}
except ClientError as exc:
return JSONResponse({"ok": False, "error": str(exc)}, status_code=404)
except Exception as exc:
return JSONResponse({"ok": False, "error": str(exc)}, status_code=502)
# ── Analytics ────────────────────────────────────────────────────────────────