feat: Add "Download All" button and outside-click close to gallery

Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
2026-05-18 16:01:03 +00:00
parent d00e1dceeb
commit 2523410c84
2 changed files with 41 additions and 3 deletions

View File

@@ -20,6 +20,7 @@ import sqlite3
import binascii
import base64
import mimetypes
import zipfile
import pytz
from datetime import datetime
from typing import Dict, List, Optional, Tuple
@@ -28,7 +29,7 @@ import math
import logging
import httpx
from fastapi import FastAPI, UploadFile, WebSocket, WebSocketDisconnect, Request, Form
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse, Response
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse, Response, StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from starlette.websockets import WebSocketState
@@ -1385,6 +1386,37 @@ async def api_files_full(path_b64: str, request: Request):
return FileResponse(file_path)
@app.get("/api/files/zip/{path_b64}")
async def api_files_zip(path_b64: str, request: Request):
if not request.session.get("accessToken"):
return Response(status_code=401)
try:
rel_path = base64.urlsafe_b64decode(path_b64).decode()
dir_path = os.path.join(UPLOAD_ROOT, rel_path)
except Exception:
return Response(status_code=400)
if not _is_safe_path(UPLOAD_ROOT, dir_path) or not os.path.isdir(dir_path):
return Response(status_code=404)
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
for filename in sorted(os.listdir(dir_path)):
file_path = os.path.join(dir_path, filename)
if os.path.isfile(file_path):
zip_file.write(file_path, filename)
zip_buffer.seek(0)
zip_filename = f"{os.path.basename(rel_path) or 'download'}.zip"
return StreamingResponse(
iter([zip_buffer.getvalue()]),
media_type="application/zip",
headers={"Content-Disposition": f"attachment; filename=\"{zip_filename}\""}
)
# ---------- Invites (one-time/expiring links) ----------
def ensure_invites_table() -> None: