feat: event proposal and approval workflow #9

Closed
padreug wants to merge 38 commits from feat/event-approval-workflow into main
Showing only changes of commit 4d91426e82 - Show all commits

refactor: consolidate create and propose endpoints into single POST /events
Some checks failed
lint.yml / refactor: consolidate create and propose endpoints into single POST /events (pull_request) Failing after 0s

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>
Padreug 2026-04-27 18:24:10 +02:00

View file

@ -105,49 +105,62 @@ async def api_events_all(
@events_api_router.post("/api/v1/events") @events_api_router.post("/api/v1/events")
@events_api_router.put("/api/v1/events/{event_id}")
async def api_event_create( async def api_event_create(
data: CreateEvent, data: CreateEvent,
wallet: WalletTypeInfo = Depends(require_admin_key), wallet: WalletTypeInfo = Depends(require_invoice_key),
event_id: str | None = None,
): ):
if event_id: """
event = await get_event(event_id) Create a new event. Any authenticated user can create events.
if not event: Admin-created events are auto-approved. Non-admin events require
raise HTTPException( approval unless auto_approve is enabled in extension settings.
status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist." """
) if not data.wallet:
data.wallet = wallet.wallet.id
if event.wallet != wallet.wallet.id: from lnbits.settings import settings
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not your event."
)
for k, v in data.dict().items():
setattr(event, k, v)
event = await update_event(event)
# Republish to Nostr if event is approved (kind 31922 is replaceable) ext_settings = await get_settings()
if event.status == "approved" and event.nostr_event_id: user_id = wallet.wallet.user
await _publish_or_delete_nostr_event(event) is_admin = (
else: user_id == settings.super_user
if not data.wallet: or user_id in settings.lnbits_admin_users
data.wallet = wallet.wallet.id )
# Check if approval is required for non-admin users if not is_admin and not ext_settings.auto_approve:
from lnbits.settings import settings data.status = "proposed"
ext_settings = await get_settings() event = await create_event(data)
user_id = wallet.wallet.user
is_admin = ( # Publish to Nostr if approved
user_id == settings.super_user if event.status == "approved":
or user_id in settings.lnbits_admin_users await _publish_or_delete_nostr_event(event)
return event.dict()
@events_api_router.put("/api/v1/events/{event_id}")
async def api_event_update(
event_id: str,
data: CreateEvent,
wallet: WalletTypeInfo = Depends(require_admin_key),
):
"""Update an existing event. Requires admin key (event owner)."""
event = await get_event(event_id)
if not event:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist."
) )
if not is_admin and not ext_settings.auto_approve:
data.status = "proposed"
event = await create_event(data)
# Publish to Nostr if auto-approved (admin-created) if event.wallet != wallet.wallet.id:
if event.status == "approved": raise HTTPException(
await _publish_or_delete_nostr_event(event) status_code=HTTPStatus.FORBIDDEN, detail="Not your event."
)
for k, v in data.dict().items():
setattr(event, k, v)
event = await update_event(event)
# Republish to Nostr if event is approved (kind 31922 is replaceable)
if event.status == "approved" and event.nostr_event_id:
await _publish_or_delete_nostr_event(event)
return event.dict() return event.dict()
@ -201,28 +214,6 @@ async def api_form_delete(
#########Event Approval########## #########Event Approval##########
@events_api_router.post("/api/v1/events/propose")
async def api_event_propose(
data: CreateEvent,
wallet: WalletTypeInfo = Depends(require_invoice_key),
):
"""
Propose a new event for admin approval.
Requires invoice key (any authenticated user, not admin-only).
Auto-approved if the admin has enabled auto_approve in settings.
"""
ext_settings = await get_settings()
data.status = "approved" if ext_settings.auto_approve else "proposed"
data.wallet = wallet.wallet.id
event = await create_event(data)
# Publish to Nostr if auto-approved
if event.status == "approved":
await _publish_or_delete_nostr_event(event)
return event.dict()
@events_api_router.get("/api/v1/events/pending") @events_api_router.get("/api/v1/events/pending")
async def api_events_pending( async def api_events_pending(
admin: Account = Depends(check_admin), admin: Account = Depends(check_admin),