from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Request, UploadFile from fastapi.responses import FileResponse from sqlalchemy.orm import Session from app.db import get_db from app.schemas import ItemCreate, ItemOut, TTLOption, WallResponse from app.services import items as items_service from app.services import storage as storage_service router = APIRouter(prefix="/api/items", tags=["items"]) _INLINE_IMAGE_PREFIXES = ("image/",) def _client_ip(request: Request) -> str: forwarded = request.headers.get("x-forwarded-for") if forwarded: return forwarded.split(",")[0].strip() if request.client and request.client.host: return request.client.host return "" @router.post("", response_model=ItemOut) def create_item( payload: ItemCreate, request: Request, db: Session = Depends(get_db), ): item = items_service.create_text_item(db, payload, _client_ip(request)) return items_service.item_to_dict(item) @router.post("/upload", response_model=ItemOut) async def upload_item( request: Request, db: Session = Depends(get_db), file: UploadFile = File(...), is_public: bool = Form(True), burn_after_read: bool = Form(False), ttl: TTLOption = Form("24h"), title: str | None = Form(None), ): rel_path, size, mime, safe_name = await storage_service.save_upload(file) item = items_service.create_file_item( db, file_name=safe_name, file_path=rel_path, mime=mime, size_bytes=size, is_public=is_public, burn_after_read=burn_after_read, ttl=ttl, created_ip=_client_ip(request), title=title, ) return items_service.item_to_dict(item) @router.get("/wall", response_model=WallResponse) def wall( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), db: Session = Depends(get_db), ): rows, total = items_service.list_wall(db, page=page, page_size=page_size) return WallResponse( items=[ItemOut.model_validate(items_service.item_to_dict(i)) for i in rows], page=page, page_size=page_size, total=total, ) @router.get("/{slug}/file") def download_file(slug: str, db: Session = Depends(get_db)): item = items_service.get_file_item(db, slug) if item is None: raise HTTPException(status_code=404, detail="Not found") path = items_service.resolve_file_path(item) if path is None or not path.is_file(): raise HTTPException(status_code=404, detail="Not found") media_type = item.mime or "application/octet-stream" filename = item.file_name or path.name inline = media_type.startswith(_INLINE_IMAGE_PREFIXES) return FileResponse( path, media_type=media_type, filename=filename, content_disposition_type="inline" if inline else "attachment", ) @router.get("/{slug}", response_model=ItemOut) def get_item(slug: str, db: Session = Depends(get_db)): item = items_service.get_item(db, slug) if item is None: raise HTTPException(status_code=404, detail="Not found") return items_service.item_to_dict(item)