""" Photo Management Endpoints for Mitai Jinkendo Handles progress photo uploads and retrieval. """ import os import uuid import logging from pathlib import Path from typing import Optional from fastapi import APIRouter, UploadFile, File, Header, HTTPException, Depends from fastapi.responses import FileResponse import aiofiles from db import get_db, get_cursor, r2d from auth import require_auth, require_auth_flexible, check_feature_access, increment_feature_usage from routers.profiles import get_pid from feature_logger import log_feature_usage router = APIRouter(prefix="/api/photos", tags=["photos"]) logger = logging.getLogger(__name__) PHOTOS_DIR = Path(os.getenv("PHOTOS_DIR", "./photos")) PHOTOS_DIR.mkdir(parents=True, exist_ok=True) @router.post("") async def upload_photo(file: UploadFile=File(...), date: str="", x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)): """Upload progress photo.""" pid = get_pid(x_profile_id) # Phase 2: Check feature access (non-blocking, log only) access = check_feature_access(pid, 'photos') log_feature_usage(pid, 'photos', access, 'upload') if not access['allowed']: logger.warning( f"[FEATURE-LIMIT] User {pid} would be blocked: " f"photos {access['reason']} (used: {access['used']}, limit: {access['limit']})" ) fid = str(uuid.uuid4()) ext = Path(file.filename).suffix or '.jpg' path = PHOTOS_DIR / f"{fid}{ext}" async with aiofiles.open(path,'wb') as f: await f.write(await file.read()) with get_db() as conn: cur = get_cursor(conn) cur.execute("INSERT INTO photos (id,profile_id,date,path,created) VALUES (%s,%s,%s,%s,CURRENT_TIMESTAMP)", (fid,pid,date,str(path))) # Phase 2: Increment usage counter increment_feature_usage(pid, 'photos') return {"id":fid,"date":date} @router.get("/{fid}") def get_photo(fid: str, session: dict=Depends(require_auth_flexible)): """Get photo by ID. Auth via header or query param (for tags).""" with get_db() as conn: cur = get_cursor(conn) cur.execute("SELECT path FROM photos WHERE id=%s", (fid,)) row = cur.fetchone() if not row: raise HTTPException(404, "Photo not found") photo_path = Path(PHOTOS_DIR) / row['path'] if not photo_path.exists(): raise HTTPException(404, "Photo file not found") return FileResponse(photo_path) @router.get("") def list_photos(x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)): """Get all photos for current profile.""" pid = get_pid(x_profile_id) with get_db() as conn: cur = get_cursor(conn) cur.execute( "SELECT * FROM photos WHERE profile_id=%s ORDER BY created DESC LIMIT 100", (pid,)) return [r2d(r) for r in cur.fetchall()]