Spaces:
Runtime error
Runtime error
Download backend/app/routers/storage.py from firebird-technologies/NoteStack-Test: direct link, hf CLI and curl.
- Browser
- Download file 4.48 kB
-
https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/main/backend/app/routers/storage.py
- Command line
-
hf download hf://spaces/firebird-technologies/NoteStack-Test/backend/app/routers/storage.py
-
curl -L -o storage.py https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/main/backend/app/routers/storage.py
4.48 kB
| """Browser uploads go straight to R2 with presigned PUTs; reads redirect to presigned GETs.""" | |
| import mimetypes | |
| import uuid | |
| from fastapi import APIRouter, Depends, HTTPException, Request | |
| from fastapi.responses import FileResponse, RedirectResponse | |
| from pydantic import BaseModel, Field | |
| from sqlalchemy import select | |
| from app.auth import Ctx, get_ctx | |
| from app.config import settings | |
| from app.models import Upload | |
| from app.services.storage import LocalStorage, keys, safe_filename, storage, verify_local_sig, workspace_owns_key | |
| router = APIRouter(prefix="/api/storage", tags=["storage"]) | |
| ALLOWED_TYPES = { | |
| "image/png", | |
| "image/jpeg", | |
| "image/gif", | |
| "image/webp", | |
| "image/svg+xml", | |
| "audio/mpeg", | |
| "audio/wav", | |
| "audio/webm", | |
| "audio/mp4", | |
| "audio/ogg", | |
| "video/mp4", | |
| "application/pdf", | |
| "text/markdown", | |
| "text/plain", | |
| "text/html", | |
| "text/vtt", | |
| } | |
| class UploadStartIn(BaseModel): | |
| filename: str = Field(max_length=300) | |
| content_type: str | |
| size_bytes: int = Field(gt=0) | |
| def start_upload(body: UploadStartIn, ctx: Ctx = Depends(get_ctx)): | |
| if body.content_type not in ALLOWED_TYPES: | |
| raise HTTPException(415, "File type not supported") | |
| if body.size_bytes > settings.max_upload_bytes: | |
| raise HTTPException(413, "File too large") | |
| upload_id = uuid.uuid4() | |
| key = keys.upload(ctx.workspace.id, upload_id, body.filename) | |
| upload = Upload( | |
| id=upload_id, | |
| workspace_id=ctx.workspace.id, | |
| user_id=ctx.user.id, | |
| key=key, | |
| filename=body.filename, | |
| content_type=body.content_type, | |
| ) | |
| ctx.db.add(upload) | |
| ctx.db.commit() | |
| return { | |
| "upload_id": str(upload_id), | |
| "key": key, | |
| "upload_url": storage.presign_put(key, body.content_type, ttl=900), | |
| "headers": {"Content-Type": body.content_type}, | |
| } | |
| def complete_upload(upload_id: uuid.UUID, ctx: Ctx = Depends(get_ctx)): | |
| upload = ctx.db.scalar( | |
| select(Upload).where(Upload.id == upload_id, Upload.workspace_id == ctx.workspace.id) | |
| ) | |
| if not upload: | |
| raise HTTPException(404, "Upload not found") | |
| head = storage.head(upload.key) | |
| if not head: | |
| raise HTTPException(409, "Object not found in storage yet") | |
| size = int(head.get("ContentLength") or 0) | |
| if size > settings.max_upload_bytes: | |
| storage.delete(upload.key) | |
| raise HTTPException(413, "File too large") | |
| upload.size_bytes = size | |
| upload.status = "complete" | |
| ctx.db.commit() | |
| return {"upload_id": str(upload.id), "key": upload.key, "size_bytes": size} | |
| def read_object(key: str, download: bool = False, ctx: Ctx = Depends(get_ctx)): | |
| if not workspace_owns_key(ctx.workspace.id, key): | |
| raise HTTPException(404, "Not found") | |
| name = key.rsplit("/", 1)[-1] if download else None | |
| return RedirectResponse(storage.presign_get(key, download_name=name), status_code=302) | |
| # Local disk backend: the "presigned" URLs LocalStorage hands out point here. | |
| local_router = APIRouter(prefix="/api/storage/local", tags=["storage"], include_in_schema=False) | |
| def _local_path(method: str, key: str, exp: int, sig: str): | |
| if not isinstance(storage, LocalStorage) or not verify_local_sig(method, key, exp, sig): | |
| raise HTTPException(403, "Invalid or expired link") | |
| try: | |
| return storage.path(key) | |
| except ValueError as exc: | |
| raise HTTPException(404, "Not found") from exc | |
| def local_get(key: str, exp: int, sig: str, download: str | None = None): | |
| path = _local_path("GET", key, exp, sig) | |
| if not path.is_file(): | |
| raise HTTPException(404, "Not found") | |
| media_type = mimetypes.guess_type(key)[0] or "application/octet-stream" | |
| # FileResponse answers Range requests, so audio and video can seek. | |
| return FileResponse(path, media_type=media_type, filename=safe_filename(download) if download else None, | |
| content_disposition_type="attachment" if download else "inline") | |
| async def local_put(key: str, exp: int, sig: str, request: Request): | |
| _local_path("PUT", key, exp, sig) | |
| body = await request.body() | |
| if len(body) > settings.max_upload_bytes: | |
| raise HTTPException(413, "File too large") | |
| storage.put_bytes(key, body) | |
| return {"ok": True, "size_bytes": len(body)} | |