Allow paste/drag temporary image assets so vision turns work without a live camera frame. Co-authored-by: Cursor <cursoragent@cursor.com>
60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
"""Authenticated upload endpoint for pending debug-chat image attachments."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
|
|
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
|
from pydantic import BaseModel
|
|
|
|
import settings
|
|
from services.auth import require_admin
|
|
from services.input_assets import discard_input_image, store_input_image
|
|
|
|
|
|
router = APIRouter(
|
|
prefix="/api/input-assets",
|
|
tags=["input-assets"],
|
|
dependencies=[Depends(require_admin)],
|
|
)
|
|
|
|
|
|
class InputImageAssetOut(BaseModel):
|
|
assetToken: str
|
|
width: int
|
|
height: int
|
|
sizeBytes: int
|
|
|
|
|
|
@router.post("/image", response_model=InputImageAssetOut)
|
|
async def upload_input_image(file: UploadFile = File(...)):
|
|
data = await file.read(settings.INPUT_IMAGE_MAX_BYTES + 1)
|
|
if len(data) > settings.INPUT_IMAGE_MAX_BYTES:
|
|
limit_mb = settings.INPUT_IMAGE_MAX_BYTES // 1024 // 1024
|
|
raise HTTPException(413, f"图片不能超过 {limit_mb} MB")
|
|
if not data:
|
|
raise HTTPException(400, "图片文件不能为空")
|
|
try:
|
|
stored = await asyncio.to_thread(store_input_image, data)
|
|
except ValueError as exc:
|
|
raise HTTPException(400, str(exc)) from exc
|
|
except Exception as exc:
|
|
raise HTTPException(503, "图片暂时无法上传,请确认对象存储可用") from exc
|
|
return InputImageAssetOut(
|
|
assetToken=stored.token,
|
|
width=stored.width,
|
|
height=stored.height,
|
|
sizeBytes=stored.size_bytes,
|
|
)
|
|
|
|
|
|
@router.delete("/{asset_token}")
|
|
async def delete_input_image(asset_token: str):
|
|
try:
|
|
await asyncio.to_thread(discard_input_image, asset_token)
|
|
except ValueError as exc:
|
|
raise HTTPException(400, str(exc)) from exc
|
|
except Exception as exc:
|
|
raise HTTPException(503, "图片附件暂时无法清理") from exc
|
|
return {"ok": True}
|