feat: add event approval workflow with admin UI
Non-admin event submissions now land in a "proposed" queue that LNbits
admins review before the event becomes ticketable and publicly listed.
- m008 adds events.events.status (proposed/approved/rejected); m010 seeds
an events.settings singleton row with the auto_approve toggle.
- Models: Event/CreateEvent.status, EventsSettings, optional date fields
with sensible defaults (closing_date defaults to event_end_date which
defaults to event_start_date), PublicEvent.status surfaces the workflow
state on the public endpoint.
- crud: get_all/public/pending_events for the admin views; get/update_settings
for the auto_approve toggle; create_event auto-fills missing date defaults.
- views_api:
* POST /api/v1/events accepts wallet invoice keys so anyone can submit;
handler stamps status="proposed" for non-admins when auto_approve is off
* /public, /all, /pending, /settings (GET+PUT), /{id}/{approve,reject},
/{id}/tickets endpoints; literal-prefix routes declared before /{event_id}
so FastAPI matches them correctly
* Public GET /{event_id} bypasses sold-out / closing-window gates for
proposed/rejected events and returns the trimmed PublicEvent so the SFC
can render a "pending approval" banner
* POST /tickets/{event_id} rejects when event.status != "approved"
- Frontend: index.vue gains an admin Settings card, Pending Approvals list,
status badge column and approve/reject row actions, plus an All Users'
Events admin table; index.js gains the data + methods + an isAdmin probe
via GET /events/all; display.vue shows pending/rejected banners and
hides the Buy Ticket form unless status === "approved".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
11043ec8a7
commit
4c8e06a6a9
7 changed files with 526 additions and 51 deletions
42
models.py
42
models.py
|
|
@ -1,6 +1,5 @@
|
|||
from datetime import datetime
|
||||
|
||||
from fastapi import Query
|
||||
from pydantic import BaseModel, EmailStr, Field, root_validator, validator
|
||||
|
||||
|
||||
|
|
@ -27,46 +26,55 @@ class EventExtra(BaseModel):
|
|||
|
||||
|
||||
class CreateEvent(BaseModel):
|
||||
wallet: str
|
||||
name: str
|
||||
info: str
|
||||
closing_date: str
|
||||
event_start_date: str
|
||||
event_end_date: str
|
||||
wallet: str | None = None # filled from caller's wallet if absent
|
||||
name: str # title (required)
|
||||
info: str = "" # description (optional)
|
||||
closing_date: str | None = None # defaults to event_end_date
|
||||
event_start_date: str # required
|
||||
event_end_date: str | None = None # defaults to event_start_date
|
||||
currency: str = "sat"
|
||||
amount_tickets: int = Query(..., ge=0)
|
||||
price_per_ticket: float = Query(..., ge=0)
|
||||
amount_tickets: int = 0 # 0 = unlimited / not ticketed
|
||||
price_per_ticket: float = 0 # 0 = free
|
||||
banner: str | None = None
|
||||
extra: EventExtra = Field(default_factory=EventExtra)
|
||||
status: str = "approved" # proposed, approved, rejected
|
||||
|
||||
|
||||
class Event(BaseModel):
|
||||
id: str
|
||||
wallet: str
|
||||
name: str
|
||||
info: str
|
||||
closing_date: str
|
||||
info: str = ""
|
||||
closing_date: str | None = None
|
||||
canceled: bool = False
|
||||
event_start_date: str
|
||||
event_end_date: str
|
||||
currency: str
|
||||
amount_tickets: int
|
||||
price_per_ticket: float
|
||||
event_end_date: str | None = None
|
||||
currency: str = "sat"
|
||||
amount_tickets: int = 0
|
||||
price_per_ticket: float = 0
|
||||
time: datetime
|
||||
sold: int = 0
|
||||
banner: str | None = None
|
||||
extra: EventExtra = Field(default_factory=EventExtra)
|
||||
status: str = "approved"
|
||||
|
||||
|
||||
class PublicEvent(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
info: str
|
||||
closing_date: str
|
||||
closing_date: str | None = None
|
||||
canceled: bool
|
||||
event_start_date: str
|
||||
event_end_date: str
|
||||
event_end_date: str | None = None
|
||||
banner: str | None
|
||||
status: str = "approved" # surfaces "proposed"/"rejected" so SFC can render banner
|
||||
|
||||
|
||||
class EventsSettings(BaseModel):
|
||||
"""Extension-level settings for the events extension."""
|
||||
|
||||
auto_approve: bool = False # Skip approval workflow for non-admin users
|
||||
|
||||
|
||||
class TicketExtra(BaseModel):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue