From 814581f3079fddf4963186432e2d9fdf89c8f73e Mon Sep 17 00:00:00 2001 From: Padreug Date: Sat, 23 May 2026 20:30:03 +0200 Subject: [PATCH] feat: expose GET /tickets/user/{user_id} endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The webapp My Tickets view + the owned-ticket badges in the activities feed both rely on this endpoint to enumerate a buyer's tickets across all events. The CRUD function already existed (`get_tickets_by_user_id`); just expose it. Auth: Bearer access token (the same shape the webapp already sends to other LNbits endpoints). The path param must match the token- bound user.id — users can only enumerate their own tickets, not anyone else's by ID-guessing. Returns full `Ticket` rows rather than `PublicTicket` because the owner needs the payment_hash (for the QR) + the `extra` envelope (for refund / promo / notification state) in My Tickets. Co-Authored-By: Claude Opus 4.7 (1M context) --- views_api.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/views_api.py b/views_api.py index 08e94ab..7fd3aa0 100644 --- a/views_api.py +++ b/views_api.py @@ -14,11 +14,12 @@ from fastapi import ( ) from lnbits.core.crud import get_user from lnbits.core.crud.wallets import get_wallet -from lnbits.core.models import Account, WalletTypeInfo +from lnbits.core.models import Account, User, WalletTypeInfo from lnbits.core.models.payments import CreateInvoice from lnbits.core.services import create_payment_request from lnbits.decorators import ( check_admin, + check_user_exists, require_admin_key, require_invoice_key, ) @@ -45,6 +46,7 @@ from .crud import ( get_settings, get_ticket, get_tickets, + get_tickets_by_user_id, purge_unpaid_tickets, update_event, update_settings, @@ -399,6 +401,27 @@ async def api_tickets( return await get_tickets(wallet_ids) +@tickets_api_router.get("/user/{user_id}") +async def api_tickets_by_user( + user_id: str, + user: User = Depends(check_user_exists), +) -> list[Ticket]: + """All tickets for the authenticated user. + + The `user_id` path param must match the token-bound user so a + Bearer-authenticated session can only enumerate its own tickets. + Returns full `Ticket` rows (not `PublicTicket`) since the owner + needs the payment_hash to render the QR + the `extra` envelope + to surface payment/refund state in My Tickets. + """ + if user_id != user.id: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail="Can only fetch your own tickets.", + ) + return await get_tickets_by_user_id(user_id) + + @tickets_api_router.get("/{ticket_id}", response_model=PublicTicket) async def api_get_ticket(ticket_id: str) -> Ticket: ticket = await get_ticket(ticket_id)