Files
bellsystems-cp/backend/crm/router.py

94 lines
3.3 KiB
Python

from fastapi import APIRouter, Depends, Query, UploadFile, File, HTTPException
from fastapi.responses import FileResponse
from typing import Optional
import os
import shutil
from auth.models import TokenPayload
from auth.dependencies import require_permission
from crm.models import ProductCreate, ProductUpdate, ProductInDB, ProductListResponse
from crm import service
router = APIRouter(prefix="/api/crm/products", tags=["crm-products"])
PHOTO_DIR = os.path.join(os.path.dirname(__file__), "..", "storage", "product_images")
os.makedirs(PHOTO_DIR, exist_ok=True)
@router.get("", response_model=ProductListResponse)
def list_products(
search: Optional[str] = Query(None),
category: Optional[str] = Query(None),
active_only: bool = Query(False),
_user: TokenPayload = Depends(require_permission("crm", "view")),
):
products = service.list_products(search=search, category=category, active_only=active_only)
return ProductListResponse(products=products, total=len(products))
@router.get("/{product_id}", response_model=ProductInDB)
def get_product(
product_id: str,
_user: TokenPayload = Depends(require_permission("crm", "view")),
):
return service.get_product(product_id)
@router.post("", response_model=ProductInDB, status_code=201)
def create_product(
body: ProductCreate,
_user: TokenPayload = Depends(require_permission("crm", "edit")),
):
return service.create_product(body)
@router.put("/{product_id}", response_model=ProductInDB)
def update_product(
product_id: str,
body: ProductUpdate,
_user: TokenPayload = Depends(require_permission("crm", "edit")),
):
return service.update_product(product_id, body)
@router.delete("/{product_id}", status_code=204)
def delete_product(
product_id: str,
_user: TokenPayload = Depends(require_permission("crm", "edit")),
):
service.delete_product(product_id)
@router.post("/{product_id}/photo", response_model=ProductInDB)
async def upload_product_photo(
product_id: str,
file: UploadFile = File(...),
_user: TokenPayload = Depends(require_permission("crm", "edit")),
):
"""Upload a product photo. Accepts JPG or PNG, stored on disk."""
if file.content_type not in ("image/jpeg", "image/png", "image/webp"):
raise HTTPException(status_code=400, detail="Only JPG, PNG, or WebP images are accepted.")
ext = {"image/jpeg": "jpg", "image/png": "png", "image/webp": "webp"}.get(file.content_type, "jpg")
photo_path = os.path.join(PHOTO_DIR, f"{product_id}.{ext}")
# Remove any old photo files for this product
for old_ext in ("jpg", "png", "webp"):
old_path = os.path.join(PHOTO_DIR, f"{product_id}.{old_ext}")
if os.path.exists(old_path) and old_path != photo_path:
os.remove(old_path)
with open(photo_path, "wb") as f:
shutil.copyfileobj(file.file, f)
photo_url = f"/crm/products/{product_id}/photo"
return service.update_product(product_id, ProductUpdate(photo_url=photo_url))
@router.get("/{product_id}/photo")
def get_product_photo(
product_id: str,
):
"""Serve a product photo from disk."""
for ext in ("jpg", "png", "webp"):
photo_path = os.path.join(PHOTO_DIR, f"{product_id}.{ext}")
if os.path.exists(photo_path):
return FileResponse(photo_path)
raise HTTPException(status_code=404, detail="No photo found for this product.")