feat: organizer ticket scanning over nostr-transport + secure legacy HTTP register endpoint #19

Merged
padreug merged 5 commits from ticket-scanner-nostr into main 2026-05-24 16:54:01 +00:00

5 commits

Author SHA1 Message Date
3606fd9a0a feat(admin): Owner column on All Users' Events card
Some checks failed
lint.yml / feat(admin): Owner column on All Users' Events card (pull_request) Failing after 0s
Adds the event's wallet owner (user_id) as the first column of the
admin-only All Users' Events table so cross-tenant rows are
attributable at a glance. Server-side join: GET /events/all now
resolves each event.wallet -> wallet.user and stamps the result on
the response as wallet_user_id. Frontend gets a dedicated
allUsersEventsTable.columns definition so the user's own-events
table stays unchanged.

Follow-up #22 covers letting the admin actually edit those events
once attributed.
2026-05-24 18:51:51 +02:00
66d263ef14 ui(admin): Tickets card above All Users' Events on the admin index
Some checks failed
lint.yml / ui(admin): Tickets card above All Users' Events on the admin index (pull_request) Failing after 0s
The Tickets table is what an organiser actually scans during day-of
operations — it deserves the top slot. All Users' Events stays one
section down for the cross-tenant audit view (admin-only anyway).
2026-05-24 18:46:18 +02:00
02071e6541 feat: events_list_event_tickets RPC for organizer ticket roster
Second nostr-transport handler on this branch. Returns paid + registered
counts plus the per-ticket roster (id, name, registered status, timestamp)
for one calendar event, organizer-only.

Backs the door scanner's counts strip and "scanned" list with backend
truth so a second organizer scanning on another device, an operator
switching from mobile to laptop mid-event, or a refresh in incognito
all see the same numbers instead of diverging from a per-device
localStorage cache.

Same authorisation posture as events_ticket_register: dispatcher
binds caller pubkey to wallet via AUTH_WALLET, handler verifies the
event's wallet is in the caller's wallet set. Only paid tickets land
in the response — proposed/unpaid rows are irrelevant at the door.

Webapp consumes this in aiolabs/webapp#73.
2026-05-24 18:45:48 +02:00
1d8dacbaa3 fix: require admin_key + owner check on PUT /tickets/register
Some checks failed
lint.yml / fix: require admin_key + owner check on PUT /tickets/register (pull_request) Failing after 0s
The legacy register endpoint had no auth decorator and no
event-ownership check — any caller who knew a ticket id could
mark it registered. Add require_admin_key (matches the rest of
the wallet-bound endpoints in this file) and verify the caller's
user owns the event the ticket belongs to.

Breaking change for any external integration that hit this
endpoint unauthed; the in-tree Quasar register page
(static/js/register.js) already sends the session admin_key via
LNbits.api.request so it keeps working.

The Nostr-transport flow at events_ticket_register (previous
commit) is the preferred call site for new callers; this HTTP
path stays for the legacy LNbits admin UI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 16:32:29 +02:00
2b3d9df11d feat: events_ticket_register RPC over nostr transport
Organizer-side ticket scanning over LNbits's freshly-merged
nostr-transport (kind 21000, NIP-44 v2). The organizer signs the
RPC event with their Nostr key; the transport dispatcher resolves
pubkey → Account → wallet (AUTH_WALLET) and the handler verifies
event-level ownership (event.wallet ∈ caller_user.wallet_ids)
before flipping `registered = True`.

Idempotence + state transitions mirror the legacy HTTP endpoint:
"Ticket not paid for" / "Ticket already registered" / "Ticket
does not exist on this event" / "You do not own this event" come
back as ERROR responses. Registration in events_start() is
guarded with try/except ImportError so the extension still loads
on older LNbits versions that pre-date the transport (HTTP path
stays the fallback there).

Webapp uses this as the new primary scan call site instead of
the legacy HTTP endpoint — see companion webapp PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 16:32:18 +02:00