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:
34
app/app.py
34
app/app.py
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user