157 lines
5.7 KiB
Python
157 lines
5.7 KiB
Python
from fastapi import APIRouter, Header, HTTPException, Query, Request, status
|
|
|
|
from app.core.deps import DbSession, EditorAdmin, SettingsDep
|
|
from app.schemas.admin import (
|
|
AdminLoginRequest,
|
|
AdminToken,
|
|
AdminUserPublic,
|
|
DashboardMetrics,
|
|
EventAdmin,
|
|
EventCreate,
|
|
EventStatsRow,
|
|
EventUpdate,
|
|
OrderAdmin,
|
|
PaymentAdmin,
|
|
TicketTypeAdmin,
|
|
TicketTypeCreate,
|
|
TicketTypeUpdate,
|
|
VenueAdmin,
|
|
VenueCreate,
|
|
VenueUpdate,
|
|
)
|
|
from app.schemas.tickets import TicketVerifyRequest, TicketVerifyResponse
|
|
from app.core.config import get_settings
|
|
from app.core.rate_limit import enforce_rate_limit
|
|
from app.core.security import create_admin_access_token, verify_password
|
|
from app.models.entities import AdminUser
|
|
from app.models.enums import OrderStatus
|
|
from app.services.admin.catalog import (
|
|
create_event,
|
|
create_ticket_type,
|
|
create_venue,
|
|
list_events_admin,
|
|
list_ticket_types,
|
|
list_venues_admin,
|
|
ticket_type_to_admin,
|
|
update_event,
|
|
update_ticket_type,
|
|
update_venue,
|
|
)
|
|
from app.services.admin.dashboard import get_dashboard_metrics, get_event_statistics
|
|
from app.services.admin.orders import get_order_admin, list_orders_admin, list_payments_admin
|
|
from app.services.tickets import assert_checkin_authorized, verify_ticket_at_gate
|
|
|
|
router = APIRouter(prefix="/admin", tags=["admin"])
|
|
|
|
|
|
@router.post("/auth/login", response_model=AdminToken)
|
|
def admin_login(body: AdminLoginRequest, request: Request, db: DbSession) -> AdminToken:
|
|
enforce_rate_limit(request, scope="admin_login", limit=20, window_seconds=60)
|
|
admin = db.query(AdminUser).filter(AdminUser.email == body.email.strip().lower()).first()
|
|
if not admin or not verify_password(body.password, admin.hashed_password):
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
|
|
token = create_admin_access_token(admin.id, get_settings())
|
|
return AdminToken(access_token=token)
|
|
|
|
|
|
@router.get("/auth/me", response_model=AdminUserPublic)
|
|
def admin_me(admin: EditorAdmin) -> AdminUser:
|
|
return admin
|
|
|
|
|
|
@router.get("/dashboard/metrics", response_model=DashboardMetrics)
|
|
def dashboard_metrics(db: DbSession, _: EditorAdmin) -> DashboardMetrics:
|
|
return get_dashboard_metrics(db)
|
|
|
|
|
|
@router.get("/stats/events", response_model=list[EventStatsRow])
|
|
def event_statistics(db: DbSession, _: EditorAdmin) -> list[EventStatsRow]:
|
|
return get_event_statistics(db)
|
|
|
|
|
|
@router.get("/venues", response_model=list[VenueAdmin])
|
|
def venues_list(db: DbSession, _: EditorAdmin) -> list[VenueAdmin]:
|
|
return [VenueAdmin.model_validate(v) for v in list_venues_admin(db)]
|
|
|
|
|
|
@router.post("/venues", response_model=VenueAdmin, status_code=201)
|
|
def venues_create(body: VenueCreate, db: DbSession, _: EditorAdmin) -> VenueAdmin:
|
|
return VenueAdmin.model_validate(create_venue(db, body))
|
|
|
|
|
|
@router.patch("/venues/{venue_id}", response_model=VenueAdmin)
|
|
def venues_update(venue_id: int, body: VenueUpdate, db: DbSession, _: EditorAdmin) -> VenueAdmin:
|
|
return VenueAdmin.model_validate(update_venue(db, venue_id, body))
|
|
|
|
|
|
@router.get("/events", response_model=list[EventAdmin])
|
|
def events_list(db: DbSession, _: EditorAdmin) -> list[EventAdmin]:
|
|
return [EventAdmin.model_validate(e) for e in list_events_admin(db)]
|
|
|
|
|
|
@router.post("/events", response_model=EventAdmin, status_code=201)
|
|
def events_create(body: EventCreate, db: DbSession, _: EditorAdmin) -> EventAdmin:
|
|
return EventAdmin.model_validate(create_event(db, body))
|
|
|
|
|
|
@router.patch("/events/{event_id}", response_model=EventAdmin)
|
|
def events_update(event_id: int, body: EventUpdate, db: DbSession, _: EditorAdmin) -> EventAdmin:
|
|
return EventAdmin.model_validate(update_event(db, event_id, body))
|
|
|
|
|
|
@router.get("/ticket-types", response_model=list[TicketTypeAdmin])
|
|
def ticket_types_list(
|
|
db: DbSession,
|
|
_: EditorAdmin,
|
|
event_id: int | None = Query(default=None),
|
|
) -> list[TicketTypeAdmin]:
|
|
return [TicketTypeAdmin.model_validate(row) for row in list_ticket_types(db, event_id)]
|
|
|
|
|
|
@router.post("/ticket-types", response_model=TicketTypeAdmin, status_code=201)
|
|
def ticket_types_create(body: TicketTypeCreate, db: DbSession, _: EditorAdmin) -> TicketTypeAdmin:
|
|
tier = create_ticket_type(db, body)
|
|
return TicketTypeAdmin.model_validate(ticket_type_to_admin(tier))
|
|
|
|
|
|
@router.patch("/ticket-types/{tier_id}", response_model=TicketTypeAdmin)
|
|
def ticket_types_update(
|
|
tier_id: int,
|
|
body: TicketTypeUpdate,
|
|
db: DbSession,
|
|
_: EditorAdmin,
|
|
) -> TicketTypeAdmin:
|
|
tier = update_ticket_type(db, tier_id, body)
|
|
return TicketTypeAdmin.model_validate(ticket_type_to_admin(tier))
|
|
|
|
|
|
@router.get("/orders", response_model=list[OrderAdmin])
|
|
def orders_list(
|
|
db: DbSession,
|
|
_: EditorAdmin,
|
|
status: OrderStatus | None = Query(default=None),
|
|
) -> list[OrderAdmin]:
|
|
return [OrderAdmin.model_validate(o) for o in list_orders_admin(db, status)]
|
|
|
|
|
|
@router.get("/orders/{order_id}", response_model=OrderAdmin)
|
|
def orders_detail(order_id: int, db: DbSession, _: EditorAdmin) -> OrderAdmin:
|
|
return OrderAdmin.model_validate(get_order_admin(db, order_id))
|
|
|
|
|
|
@router.get("/payments", response_model=list[PaymentAdmin])
|
|
def payments_list(db: DbSession, _: EditorAdmin) -> list[PaymentAdmin]:
|
|
return [PaymentAdmin.model_validate(row) for row in list_payments_admin(db)]
|
|
|
|
|
|
@router.post("/tickets/verify", response_model=TicketVerifyResponse)
|
|
def admin_verify_ticket(
|
|
body: TicketVerifyRequest,
|
|
db: DbSession,
|
|
_: EditorAdmin,
|
|
settings: SettingsDep,
|
|
x_check_in_key: str | None = Header(default=None, alias="X-Check-In-Key"),
|
|
) -> TicketVerifyResponse:
|
|
assert_checkin_authorized(settings, x_check_in_key)
|
|
result = verify_ticket_at_gate(db, body.qr_token.strip())
|
|
return TicketVerifyResponse.model_validate(result)
|