mirror of
http://88.130.71.182:3000/BlitTech/badoHair_be.git
synced 2026-06-12 23:23:22 +00:00
Initial Commit
This commit is contained in:
116
app/routers/admin/products.py
Normal file
116
app/routers/admin/products.py
Normal file
@@ -0,0 +1,116 @@
|
||||
from typing import Annotated
|
||||
from fastapi import APIRouter, Depends, Query, UploadFile, File
|
||||
import asyncpg
|
||||
|
||||
from app.core.pagination import pagination_params
|
||||
from app.core.responses import ok, paginated
|
||||
from app.dependencies import get_db, require_admin
|
||||
from app.exceptions import ValidationError
|
||||
from app.models.products import ProductCreate, ProductUpdate, BulkStockUpdateRequest
|
||||
from app.services import product_service, storage_service
|
||||
|
||||
router = APIRouter(prefix="/products", tags=["Admin — Products"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def list_products(
|
||||
pagination: Annotated[tuple, Depends(pagination_params)],
|
||||
search: str | None = Query(None),
|
||||
category: str | None = Query(None),
|
||||
bestseller: bool = Query(False),
|
||||
db: asyncpg.Connection = Depends(get_db),
|
||||
_: dict = Depends(require_admin),
|
||||
):
|
||||
page, per_page, offset = pagination
|
||||
products, total = await product_service.list_products(
|
||||
db, page, per_page, offset,
|
||||
include_hidden=True,
|
||||
category=category,
|
||||
bestseller=bestseller,
|
||||
search=search,
|
||||
)
|
||||
return paginated(products, total, page, per_page)
|
||||
|
||||
|
||||
@router.post("", status_code=201)
|
||||
async def create_product(
|
||||
body: ProductCreate,
|
||||
db: asyncpg.Connection = Depends(get_db),
|
||||
admin: dict = Depends(require_admin),
|
||||
):
|
||||
product = await product_service.create_product(db, body)
|
||||
await _log(db, admin, "product.created", str(product["id"]))
|
||||
return ok(product)
|
||||
|
||||
|
||||
@router.put("/{product_id}")
|
||||
async def update_product(
|
||||
product_id: str,
|
||||
body: ProductUpdate,
|
||||
db: asyncpg.Connection = Depends(get_db),
|
||||
admin: dict = Depends(require_admin),
|
||||
):
|
||||
product = await product_service.update_product(db, product_id, body)
|
||||
await _log(db, admin, "product.updated", product_id)
|
||||
return ok(product)
|
||||
|
||||
|
||||
@router.delete("/{product_id}", status_code=204)
|
||||
async def delete_product(
|
||||
product_id: str,
|
||||
db: asyncpg.Connection = Depends(get_db),
|
||||
admin: dict = Depends(require_admin),
|
||||
):
|
||||
await product_service.delete_product(db, product_id)
|
||||
await _log(db, admin, "product.deleted", product_id)
|
||||
|
||||
|
||||
@router.post("/{product_id}/images", status_code=201)
|
||||
async def upload_image(
|
||||
product_id: str,
|
||||
file: UploadFile = File(...),
|
||||
db: asyncpg.Connection = Depends(get_db),
|
||||
admin: dict = Depends(require_admin),
|
||||
):
|
||||
if not file.content_type or not file.content_type.startswith("image/"):
|
||||
raise ValidationError("Only image files are allowed")
|
||||
|
||||
content = await file.read()
|
||||
if len(content) > 10 * 1024 * 1024:
|
||||
raise ValidationError("Image must be under 10MB")
|
||||
|
||||
image_data = await storage_service.upload_product_image(product_id, content, file.content_type)
|
||||
product = await product_service.add_image(db, product_id, image_data["url"])
|
||||
return ok(product)
|
||||
|
||||
|
||||
@router.delete("/{product_id}/images")
|
||||
async def delete_image(
|
||||
product_id: str,
|
||||
url: str = Query(..., description="Full image URL to remove"),
|
||||
storage_path: str = Query(..., description="Storage path for deletion from bucket"),
|
||||
db: asyncpg.Connection = Depends(get_db),
|
||||
admin: dict = Depends(require_admin),
|
||||
):
|
||||
await storage_service.delete_product_image(storage_path)
|
||||
product = await product_service.remove_image(db, product_id, url)
|
||||
return ok(product)
|
||||
|
||||
|
||||
@router.post("/bulk-stock")
|
||||
async def bulk_stock(
|
||||
body: BulkStockUpdateRequest,
|
||||
db: asyncpg.Connection = Depends(get_db),
|
||||
admin: dict = Depends(require_admin),
|
||||
):
|
||||
updates = [u.model_dump() for u in body.updates]
|
||||
await product_service.bulk_stock_update(db, updates)
|
||||
await _log(db, admin, "product.bulk_stock_updated", None)
|
||||
return ok({"updated": len(updates)})
|
||||
|
||||
|
||||
async def _log(db, admin, action, entity_id, metadata=None):
|
||||
await db.execute(
|
||||
"INSERT INTO activity_log (actor_id, action, entity_type, entity_id, metadata) VALUES ($1, $2, 'product', $3, $4)",
|
||||
str(admin["id"]), action, entity_id, metadata,
|
||||
)
|
||||
Reference in New Issue
Block a user