Spaces:
Running
Running
| import os | |
| import pathlib | |
| import uuid | |
| from urllib.parse import urlparse | |
| import httpx | |
| from fastapi import FastAPI, File, HTTPException, Request, UploadFile | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import FileResponse, StreamingResponse | |
| DATA_DIR = pathlib.Path(os.environ.get("DATA_DIR", "/tmp/uploads")) | |
| DATA_DIR.mkdir(parents=True, exist_ok=True) | |
| MAX_BYTES = int(os.environ.get("MAX_BYTES", str(30 * 1024 * 1024))) | |
| # /mirror streams somebody else's object storage back with CORS headers. It stores nothing. | |
| MIRROR_MAX_BYTES = int(os.environ.get("MIRROR_MAX_BYTES", str(300 * 1024 * 1024))) | |
| DEFAULT_MIRROR_HOSTS = ( | |
| # object storage the generated videos actually live on | |
| "aliyuncs.com,r2.cloudflarestorage.com,amazonaws.com,myqcloud.com,bcebos.com," | |
| "volces.com,byteimg.com,volccdn.com,googleapis.com,cloudfront.net,oaiusercontent.com," | |
| # relay gateways whose own /v1/videos/.../content links may need mirroring | |
| "diwdiw.cn,hjmie.cc.cd" | |
| ) | |
| MIRROR_ALLOW_HOSTS = tuple(host.strip().lower() for host in os.environ.get("MIRROR_ALLOW_HOSTS", DEFAULT_MIRROR_HOSTS).split(",") if host.strip()) | |
| # Optional shared upload token. If UPLOAD_TOKEN is unset, uploads are open (temp use). | |
| UPLOAD_TOKEN = os.environ.get("UPLOAD_TOKEN", "").strip() | |
| # Optional public base override, e.g. https://looknicemm1-tmp-files.hf.space | |
| PUBLIC_BASE = os.environ.get("PUBLIC_BASE", "").strip().rstrip("/") | |
| EXT_BY_TYPE = { | |
| "image/jpeg": "jpg", | |
| "image/jpg": "jpg", | |
| "image/png": "png", | |
| "image/webp": "webp", | |
| "image/gif": "gif", | |
| "video/mp4": "mp4", | |
| "video/quicktime": "mov", | |
| "video/webm": "webm", | |
| "audio/mpeg": "mp3", | |
| "audio/mp4": "m4a", | |
| "audio/wav": "wav", | |
| "audio/x-wav": "wav", | |
| "audio/aac": "aac", | |
| "audio/ogg": "ogg", | |
| } | |
| app = FastAPI(title="tmp-file-service") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| def public_base(request: Request) -> str: | |
| if PUBLIC_BASE: | |
| return PUBLIC_BASE | |
| host = request.headers.get("x-forwarded-host") or request.headers.get("host") or "localhost" | |
| proto = request.headers.get("x-forwarded-proto", "https") | |
| return f"{proto}://{host}" | |
| def check_auth(request: Request) -> None: | |
| if not UPLOAD_TOKEN: | |
| return | |
| header = request.headers.get("authorization", "") | |
| token = header[7:].strip() if header.lower().startswith("bearer ") else request.query_params.get("token", "") | |
| if token != UPLOAD_TOKEN: | |
| raise HTTPException(401, "invalid upload token") | |
| def pick_ext(file: UploadFile) -> str: | |
| ext = EXT_BY_TYPE.get((file.content_type or "").lower()) | |
| if ext: | |
| return ext | |
| suffix = pathlib.Path(file.filename or "").suffix.lstrip(".").lower() | |
| return suffix or "bin" | |
| def check_mirror_url(raw: str) -> str: | |
| url = (raw or "").strip() | |
| parsed = urlparse(url) | |
| if parsed.scheme not in ("http", "https"): | |
| raise HTTPException(400, "url must be http(s)") | |
| host = (parsed.hostname or "").lower() | |
| if not host: | |
| raise HTTPException(400, "url has no host") | |
| if not any(host == allowed or host.endswith(f".{allowed}") for allowed in MIRROR_ALLOW_HOSTS): | |
| raise HTTPException(403, f"host not allowed: {host}") | |
| return url | |
| def health(): | |
| return {"ok": True, "service": "tmp-file-service", "auth": bool(UPLOAD_TOKEN), "mirror_hosts": list(MIRROR_ALLOW_HOSTS)} | |
| async def mirror(request: Request, url: str = ""): | |
| """Fetch a signed object-storage URL server-side and stream it back, so browsers can read | |
| bytes that the origin serves without CORS headers. Nothing is written to disk.""" | |
| target = check_mirror_url(url) | |
| client = httpx.AsyncClient(follow_redirects=True, timeout=httpx.Timeout(connect=15.0, read=120.0, write=60.0, pool=15.0)) | |
| try: | |
| upstream = await client.send(client.build_request("GET", target), stream=True) | |
| except httpx.HTTPError as error: | |
| await client.aclose() | |
| raise HTTPException(502, f"fetch failed: {error}") from error | |
| if upstream.status_code >= 400: | |
| await upstream.aclose() | |
| await client.aclose() | |
| raise HTTPException(502, f"upstream returned {upstream.status_code}") | |
| declared = upstream.headers.get("content-length") | |
| if declared and declared.isdigit() and int(declared) > MIRROR_MAX_BYTES: | |
| await upstream.aclose() | |
| await client.aclose() | |
| raise HTTPException(413, f"object too large (> {MIRROR_MAX_BYTES} bytes)") | |
| async def body(): | |
| sent = 0 | |
| try: | |
| async for chunk in upstream.aiter_bytes(chunk_size=256 * 1024): | |
| sent += len(chunk) | |
| if sent > MIRROR_MAX_BYTES: | |
| break | |
| yield chunk | |
| finally: | |
| await upstream.aclose() | |
| await client.aclose() | |
| headers = {"Cache-Control": "no-store"} | |
| if declared: | |
| headers["Content-Length"] = declared | |
| return StreamingResponse(body(), media_type=upstream.headers.get("content-type", "application/octet-stream"), headers=headers) | |
| async def upload(request: Request, file: UploadFile = File(...)): | |
| check_auth(request) | |
| data = await file.read() | |
| if not data: | |
| raise HTTPException(400, "empty file") | |
| if len(data) > MAX_BYTES: | |
| raise HTTPException(413, f"file too large (> {MAX_BYTES} bytes)") | |
| name = f"{uuid.uuid4().hex}.{pick_ext(file)}" | |
| (DATA_DIR / name).write_bytes(data) | |
| url = f"{public_base(request)}/files/{name}" | |
| return {"success": True, "url": url, "filename": name, "size": len(data)} | |
| def get_file(name: str): | |
| if "/" in name or ".." in name: | |
| raise HTTPException(400, "bad name") | |
| path = DATA_DIR / name | |
| if not path.is_file(): | |
| raise HTTPException(404, "not found") | |
| return FileResponse(path) | |