Files
fpov-exchange/trade-hub/backend/app/available.py
2026-11-12 13:16:57 -07:00

70 lines
2.1 KiB
Python

"""Public listing of inventory items owners marked as `available`.
Read-only and unauthenticated, like the catalog. Notes are never included: the
listing only reveals the owner's username, quantity and catalog details.
"""
import math
from fastapi import APIRouter, Depends, Query
from sqlalchemy import select
from sqlalchemy.orm import Session as DbSession
from app.db import get_db
from app.inventory import SORT_KEYS, enrich_entry
from app.models import InventoryEntry, User
from app.schemas import AvailablePage
router = APIRouter(prefix="/api/v1/available", tags=["available"])
MAX_PAGE_SIZE = 100
@router.get("", response_model=AvailablePage)
def list_available(
category: str | None = None,
search: str | None = None,
sort: str = Query(default="updated", pattern="^(name|category|quantity|updated)$"),
page: int = Query(default=1, ge=1),
page_size: int = Query(default=25, ge=1),
db: DbSession = Depends(get_db),
):
page_size = min(page_size, MAX_PAGE_SIZE)
rows = db.execute(
select(InventoryEntry, User.username)
.join(User, User.id == InventoryEntry.user_id)
.where(InventoryEntry.visibility == "available")
).all()
items = []
for entry, username in rows:
enriched = enrich_entry(entry)
if not enriched["available"]:
continue
enriched["username"] = username
items.append(enriched)
if category is not None and category.strip():
wanted = category.strip()
items = [i for i in items if i["category"] == wanted]
if search is not None and search.strip():
needle = search.strip().lower()
items = [
i
for i in items
if needle in i["name"].lower()
or needle in i["class_name"].lower()
or needle in i["username"].lower()
]
items.sort(key=SORT_KEYS[sort])
total = len(items)
total_pages = max(1, math.ceil(total / page_size))
start = (page - 1) * page_size
return {
"total": total,
"page": page,
"page_size": page_size,
"total_pages": total_pages,
"items": items[start : start + page_size],
}