chore: rebase onto upstream v1.6.1 + bump to v1.6.1-aio.1
Some checks failed
lint.yml / chore: rebase onto upstream v1.6.1 + bump to v1.6.1-aio.1 (push) Failing after 0s
Some checks failed
lint.yml / chore: rebase onto upstream v1.6.1 + bump to v1.6.1-aio.1 (push) Failing after 0s
Rebases the aio fork onto upstream v1.6.1 (4bf867e), pulling in:
- fiat checkout + email/Nostr DM ticket notifications (PR #50)
- currency-conversion fix (v1.5.0)
- custom notification subject/body (v1.6.0)
- resend-email button on the ticket list (PR #51)
Notable merges:
- views_api.api_event_update keeps the explicit-field-list gating from
the aio.4 security fix, with allow_fiat + fiat_currency added so an
owner editing a fiat-enabled event keeps the fiat config.
- models.PublicEvent now exposes both upstream's fiat fields and our
location / categories / status fields.
- migrations.py reverts to byte-identical to upstream v1.6.1 (no aio
entries); fork schema lives in migrations_fork.py (per aiolabs/lnbits#8).
- Lint reformatted with black + ruff to match upstream style.
Contributors entry adds `padreug` (aio fork maintainer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b576a490d2
commit
27cc8d2f1c
7 changed files with 22 additions and 34 deletions
|
|
@ -75,9 +75,7 @@ def events_start():
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(f"[EVENTS] Nostr sync task failed: {exc}")
|
logger.error(f"[EVENTS] Nostr sync task failed: {exc}")
|
||||||
|
|
||||||
task3 = create_permanent_unique_task(
|
task3 = create_permanent_unique_task("ext_events_nostr_sync", _sync_nostr_events)
|
||||||
"ext_events_nostr_sync", _sync_nostr_events
|
|
||||||
)
|
|
||||||
scheduled_tasks.append(task3)
|
scheduled_tasks.append(task3)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"id": "events",
|
"id": "events",
|
||||||
"version": "1.6.1",
|
"version": "1.6.1-aio.1",
|
||||||
"name": "Events",
|
"name": "Events",
|
||||||
"repo": "https://github.com/lnbits/events",
|
"repo": "https://git.atitlan.io/aiolabs/events",
|
||||||
"short_description": "Sell and register event tickets",
|
"short_description": "Sell and register event tickets",
|
||||||
"description": "",
|
"description": "",
|
||||||
"tile": "/events/static/image/events.png",
|
"tile": "/events/static/image/events.png",
|
||||||
|
|
@ -32,6 +32,11 @@
|
||||||
"name": "motorina0",
|
"name": "motorina0",
|
||||||
"uri": "https://github.com/motorina0",
|
"uri": "https://github.com/motorina0",
|
||||||
"role": "Developer"
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "padreug",
|
||||||
|
"uri": "https://git.atitlan.io/padreug",
|
||||||
|
"role": "Developer (aio fork: approval workflow + NIP-52 Nostr sync + edit gating)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"images": [
|
"images": [
|
||||||
|
|
|
||||||
|
|
@ -140,9 +140,7 @@ class CreateTicket(BaseModel):
|
||||||
email = values.get("email")
|
email = values.get("email")
|
||||||
user_id = values.get("user_id")
|
user_id = values.get("user_id")
|
||||||
if not user_id and not (name and email):
|
if not user_id and not (name and email):
|
||||||
raise ValueError(
|
raise ValueError("Either user_id or both name and email must be provided")
|
||||||
"Either user_id or both name and email must be provided"
|
|
||||||
)
|
|
||||||
if user_id and (name or email):
|
if user_id and (name or email):
|
||||||
raise ValueError("Cannot provide both user_id and name/email")
|
raise ValueError("Cannot provide both user_id and name/email")
|
||||||
return values
|
return values
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,7 @@ class NostrClient:
|
||||||
async def connect(self) -> WebSocketApp:
|
async def connect(self) -> WebSocketApp:
|
||||||
relay_endpoint = encrypt_internal_message("relay", urlsafe=True)
|
relay_endpoint = encrypt_internal_message("relay", urlsafe=True)
|
||||||
ws_url = (
|
ws_url = (
|
||||||
f"ws://localhost:{settings.port}"
|
f"ws://localhost:{settings.port}" f"/nostrclient/api/v1/{relay_endpoint}"
|
||||||
f"/nostrclient/api/v1/{relay_endpoint}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info("[EVENTS] Connecting to nostrclient WebSocket...")
|
logger.info("[EVENTS] Connecting to nostrclient WebSocket...")
|
||||||
|
|
@ -58,12 +57,8 @@ class NostrClient:
|
||||||
logger.warning(f"[EVENTS] WebSocket error: {error}")
|
logger.warning(f"[EVENTS] WebSocket error: {error}")
|
||||||
|
|
||||||
def on_close(_, status_code, message):
|
def on_close(_, status_code, message):
|
||||||
logger.warning(
|
logger.warning(f"[EVENTS] WebSocket closed: {status_code} {message}")
|
||||||
f"[EVENTS] WebSocket closed: {status_code} {message}"
|
self.receive_event_queue.put_nowait(ValueError("WebSocket closed"))
|
||||||
)
|
|
||||||
self.receive_event_queue.put_nowait(
|
|
||||||
ValueError("WebSocket closed")
|
|
||||||
)
|
|
||||||
|
|
||||||
ws = WebSocketApp(
|
ws = WebSocketApp(
|
||||||
ws_url,
|
ws_url,
|
||||||
|
|
@ -118,9 +113,7 @@ class NostrClient:
|
||||||
async def subscribe(self, filters: list[dict]):
|
async def subscribe(self, filters: list[dict]):
|
||||||
"""Subscribe to events matching the given filters."""
|
"""Subscribe to events matching the given filters."""
|
||||||
self.subscription_id = "events-" + urlsafe_short_hash()[:32]
|
self.subscription_id = "events-" + urlsafe_short_hash()[:32]
|
||||||
await self.send_req_queue.put(
|
await self.send_req_queue.put(["REQ", self.subscription_id, *filters])
|
||||||
["REQ", self.subscription_id, *filters]
|
|
||||||
)
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[EVENTS] Subscribed to NIP-52 events "
|
f"[EVENTS] Subscribed to NIP-52 events "
|
||||||
f"(sub: {self.subscription_id[:20]}...)"
|
f"(sub: {self.subscription_id[:20]}...)"
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ def build_nip52_event(event: Event, pubkey: str) -> NostrEvent:
|
||||||
tags.append(["image", event.banner])
|
tags.append(["image", event.banner])
|
||||||
if event.location:
|
if event.location:
|
||||||
tags.append(["location", event.location])
|
tags.append(["location", event.location])
|
||||||
for cat in (event.categories or []):
|
for cat in event.categories or []:
|
||||||
tags.append(["t", cat])
|
tags.append(["t", cat])
|
||||||
|
|
||||||
nostr_event = NostrEvent(
|
nostr_event = NostrEvent(
|
||||||
|
|
|
||||||
|
|
@ -137,9 +137,11 @@ async def wait_for_nostr_events(nostr_client: NostrClient):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
# Subscribe to NIP-52 calendar events
|
# Subscribe to NIP-52 calendar events
|
||||||
await nostr_client.subscribe([
|
await nostr_client.subscribe(
|
||||||
{"kinds": [31922, 31923]},
|
[
|
||||||
])
|
{"kinds": [31922, 31923]},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# Process incoming events
|
# Process incoming events
|
||||||
while True:
|
while True:
|
||||||
|
|
|
||||||
14
views_api.py
14
views_api.py
|
|
@ -161,9 +161,7 @@ async def api_get_event(event_id: str) -> Event:
|
||||||
|
|
||||||
# closing_date is filled in by create_event (defaults to end_date or
|
# closing_date is filled in by create_event (defaults to end_date or
|
||||||
# start_date) but the field is typed Optional, so guard for the typechecker.
|
# start_date) but the field is typed Optional, so guard for the typechecker.
|
||||||
closing_date = (
|
closing_date = event.closing_date or event.event_end_date or event.event_start_date
|
||||||
event.closing_date or event.event_end_date or event.event_start_date
|
|
||||||
)
|
|
||||||
# Accept either YYYY-MM-DD or full ISO 8601 datetime (event_end_date
|
# Accept either YYYY-MM-DD or full ISO 8601 datetime (event_end_date
|
||||||
# may carry a time component since v1.3.0-aio.3 / our start-end-time
|
# may carry a time component since v1.3.0-aio.3 / our start-end-time
|
||||||
# feature).
|
# feature).
|
||||||
|
|
@ -210,10 +208,7 @@ async def api_event_create(
|
||||||
|
|
||||||
ext_settings = await get_settings()
|
ext_settings = await get_settings()
|
||||||
user_id = wallet.wallet.user
|
user_id = wallet.wallet.user
|
||||||
is_admin = (
|
is_admin = user_id == settings.super_user or user_id in settings.lnbits_admin_users
|
||||||
user_id == settings.super_user
|
|
||||||
or user_id in settings.lnbits_admin_users
|
|
||||||
)
|
|
||||||
if not is_admin and not ext_settings.auto_approve:
|
if not is_admin and not ext_settings.auto_approve:
|
||||||
data.status = "proposed"
|
data.status = "proposed"
|
||||||
|
|
||||||
|
|
@ -249,9 +244,7 @@ async def api_event_update(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist."
|
status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist."
|
||||||
)
|
)
|
||||||
if event.wallet != wallet.wallet.id:
|
if event.wallet != wallet.wallet.id:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your event.")
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="Not your event."
|
|
||||||
)
|
|
||||||
|
|
||||||
from lnbits.settings import settings
|
from lnbits.settings import settings
|
||||||
|
|
||||||
|
|
@ -292,7 +285,6 @@ async def api_event_update(
|
||||||
|
|
||||||
event.status = "approved" if (is_admin or ext_settings.auto_approve) else "proposed"
|
event.status = "approved" if (is_admin or ext_settings.auto_approve) else "proposed"
|
||||||
|
|
||||||
|
|
||||||
event = await update_event(event)
|
event = await update_event(event)
|
||||||
|
|
||||||
if event.status == "approved":
|
if event.status == "approved":
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue