feat: republish endpoints + polling + multi-ticket via N-rows model #16

Merged
padreug merged 7 commits from tickets-nostr-sync into main 2026-05-23 21:11:38 +00:00
3 changed files with 79 additions and 3 deletions
Showing only changes of commit ced6ca2b2b - Show all commits

feat: organizer-side "Republish mine" button + scoped endpoint

The admin /republish-all hits every approved event regardless of
owner — useful for the catalog migration, but heavy. Organizers
who want to re-emit just THEIR own events (e.g. after the AIO
publisher gained the tickets_* tags and an organizer's events
should pick them up) need a lighter knob.

Backend: new POST /republish-mine wallet-scoped via require_admin_key,
mirrors api_tickets's `all_wallets=true` shape so the page can
re-emit across every wallet the user owns. Filters to approved +
non-canceled rows.

UI: "Republish mine" button alongside "New Event" so every
logged-in user sees it (no isAdmin gate). Loading state +
confirm dialog + success count notification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Padreug 2026-05-23 21:02:36 +02:00

View file

@ -10,6 +10,7 @@ window.PageEvents = {
allUserEvents: [],
isAdmin: false,
republishing: false,
republishingMine: false,
settings: {
auto_approve: false
},
@ -303,6 +304,36 @@ window.PageEvents = {
})
})
},
republishMyEvents() {
LNbits.utils
.confirmDialog(
'Re-emit your approved events to Nostr relays?'
)
.onOk(() => {
this.republishingMine = true
LNbits.api
.request(
'POST',
'/events/api/v1/events/republish-mine?all_wallets=true',
this.g.user.wallets[0].adminkey
)
.then(response => {
Quasar.Notify.create({
type: 'positive',
message:
'Republished ' +
response.data.republished +
' of your ' +
response.data.total +
' events'
})
})
.catch(LNbits.utils.notifyApiError)
.finally(() => {
this.republishingMine = false
})
})
},
foldDateTime(day, time) {
// Combine separate date/time inputs into the wire format
// expected by the events extension: "YYYY-MM-DD" or

View file

@ -42,9 +42,23 @@
<q-card>
<q-card-section>
<div class="row items-center q-gutter-sm">
<q-btn unelevated color="primary" @click="openEventDialog"
>New Event</q-btn
>
<q-btn
outline
color="primary"
icon="cloud_upload"
label="Republish mine"
:loading="republishingMine"
@click="republishMyEvents"
></q-btn>
</div>
<div class="text-caption q-mt-sm" style="color: #aaa">
Re-emit your approved events to Nostr relays. Useful after
a publisher upgrade or if a relay dropped your events.
</div>
</q-card-section>
</q-card>

View file

@ -136,6 +136,37 @@ async def api_republish_all(
return {"republished": len(approved), "total": len(events)}
@events_api_router.post("/republish-mine")
async def api_republish_mine(
all_wallets: bool = Query(False),
key_info: WalletTypeInfo = Depends(require_admin_key),
) -> dict:
"""Force-republish the caller's own approved events to Nostr relays.
Same shape as /republish-all but scoped to events owned by the
authenticated wallet (or all wallets belonging to the wallet's
user when `?all_wallets=true`). Lets the organizer trigger the
same migration the admin uses, without needing instance-admin
rights useful when the AIO publisher gains a new tag set and
an organizer wants their published events to carry it.
Only events with `status == "approved"` are republished; pending
and rejected rows aren't on relays in the first place, so a
republish for them would be a no-op (or worse, surface a
proposed-but-not-approved row to subscribers).
"""
wallet_ids: list[str] = [key_info.wallet.id]
if all_wallets:
user = await get_user(key_info.wallet.user)
wallet_ids = user.wallet_ids if user else []
events = await get_events(wallet_ids)
approved = [e for e in events if e.status == "approved" and not e.canceled]
for event in approved:
await publish_or_delete_nostr_event(event)
return {"republished": len(approved), "total": len(events)}
@events_api_router.get("/settings")
async def api_get_settings(
admin: Account = Depends(check_admin),