Add background task that subscribes to kind 31922/31923 events
from relays and processes them into the local database. Starts
15s after NostrClient connects (sequenced after publish client).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Subscribe to kind 31922/31923 events and upsert into local DB:
- New events discovered from relays are auto-approved
- Existing events are updated if incoming version is newer
- Deduplication via event ID and d-tag correlation
- Events from Nostr have empty wallet (not ticketed locally)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add receive queue, subscription management, and event deduplication
to support incoming NIP-52 calendar events from relays.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove separate /events/propose endpoint. POST /events now uses
invoice key (any user) and determines approval status based on:
- LNbits admin → auto-approved
- auto_approve setting → auto-approved
- Otherwise → proposed (requires admin approval)
Separate PUT /events/{id} for updates (admin key, event owner).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The propose endpoint always set status to 'proposed' regardless of
the auto_approve setting. Now checks the setting and auto-approves
(+ publishes to Nostr) when enabled.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add location (text) and categories (JSON list) to Event model
- Make most CreateEvent fields optional: only title + start date required
- Default end_date to start_date, closing_date to end_date
- Categories stored as JSON text, parsed via validator
- NIP-52 publisher includes location tag and t tags for categories
- Migration m011 adds location and categories columns
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- On approve: publish kind 31922 calendar event to Nostr
- On admin create (auto-approved): publish immediately
- On update (approved event): republish (kind 31922 is replaceable)
- On cancel/delete: publish kind 5 delete event
- All Nostr calls are wrapped in try/except for graceful degradation
- Event creator's Account keypair used for signing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Start a publish-only NostrClient as a background task (10s delay
for nostrclient readiness). Graceful degradation: if nostrclient
is unavailable, events extension continues without Nostr publishing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Migration m009 adds nostr_event_id and nostr_event_created_at to
track published NIP-52 calendar events. Enables correlation between
LNbits events and their Nostr representations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stripped-down Nostr client that connects to nostrclient's internal
WebSocket for publishing NIP-52 calendar events. No subscription
capabilities — publish queue only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Events created by non-admin users via POST /events are now set to
'proposed' status, requiring LNbits admin approval. Admin-created
events are auto-approved.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Admin sees two tables: "Events" (own wallet events) and "All Users'
Events" (events from other users' wallets, admin only). Non-admin
users only see their own events table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
require_admin_key only checks that the API key is a wallet admin key,
which ANY user has. check_admin verifies the user is a LNbits admin
(super_user or lnbits_admin_users). JS updated to omit API key on
admin endpoints, relying on session cookie auth instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- GET /api/v1/events/all — returns all events regardless of wallet (admin key)
- Admin UI tries /events/all first, falls back to own wallet events
- Approved events from other users now visible in admin events table
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pending events from other users' wallets weren't visible because
getEvents() only returns events scoped to the admin's wallets.
Add separate getPendingEvents() that calls /events/pending endpoint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Self-closing tags on custom elements (q-icon, q-badge) cause
Vue compiler-30 (missing end tag) errors in HTML-parsed templates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Avoids Jinja/Vue template delimiter conflicts that cause Vue
compiler-30 errors (missing end tag from unescaped > in expressions).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Separate "Pending Approvals" card with approve/reject buttons
(appears only when proposed events exist)
- Status badge column in events table (green/orange/red)
- Inline approve/reject buttons on proposed events in table
- Following castle extension's approval UI pattern
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The propose endpoint sets wallet from the authenticated user's
invoice key. Making wallet optional in the model allows the
request body to omit it. The admin create endpoint falls back
to the auth wallet if not provided.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove unused purge_unpaid_tickets import (add TODO comment)
- Break long line in ticket GET endpoint signature
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These were required query params on the GET ticket endpoint,
causing 400 errors when not provided.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- POST /api/v1/events/propose — submit event for approval (invoice key)
- GET /api/v1/events/pending — list proposed events (admin key)
- PUT /api/v1/events/{id}/approve — approve proposed event (admin key)
- PUT /api/v1/events/{id}/reject — reject proposed event (admin key)
- GET /api/v1/events/public — now returns only approved, non-canceled events
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 'status' column (proposed/approved/rejected) to the events
table with default 'approved' for backward compatibility. Existing
events are unaffected.
Migration m008 adds the column.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous fix used db.insert() which serializes the extra field to JSON.
However, the read functions (get_ticket, get_tickets, etc.) use fetchone/fetchall
without a model parameter, so the extra field comes back as a JSON string.
Added _parse_ticket_row() helper that:
- Converts empty strings to None for name/email (existing logic)
- Parses extra field from JSON string if needed (new)
The isinstance(extra, str) check ensures compatibility with both:
- SQLite: returns JSON as string
- PostgreSQL/CockroachDB: may return native JSONB as dict
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The previous implementation used db.execute() with a raw dict, which
failed on SQLite because the 'extra' field (TicketExtra model) was
passed as a Python dict that SQLite cannot serialize.
Using db.insert() with the Pydantic model ensures proper JSON
serialization of the extra field across all database backends
(SQLite, PostgreSQL, CockroachDB).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
SQLite doesn't support adding multiple columns in a single ALTER TABLE
statement. Split into separate statements for each column.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a public events endpoint that allows read-only access to all events.
Improves ticket management by adding support for user IDs as an identifier, alongside name and email.
This simplifies ticket creation for authenticated users and enhances security.
Also introduces an API endpoint to fetch tickets by user ID.
Imports the `Optional` type hint from the `typing` module into `crud.py` and `models.py`.
This provides more explicit type annotations where values can be `None`.
Adds a public events endpoint that allows read-only access to all events.
Improves ticket management by adding support for user IDs as an identifier, alongside name and email.
This simplifies ticket creation for authenticated users and enhances security.
Also introduces an API endpoint to fetch tickets by user ID.