Compare commits
1 commit
main
...
v1.2.1-aio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2740d73678 |
4 changed files with 105 additions and 9 deletions
27
crud.py
27
crud.py
|
|
@ -1,4 +1,5 @@
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from lnbits.db import Database
|
from lnbits.db import Database
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
@ -9,7 +10,13 @@ db = Database("ext_events")
|
||||||
|
|
||||||
|
|
||||||
async def create_ticket(
|
async def create_ticket(
|
||||||
payment_hash: str, wallet: str, event: str, name: str, email: str, extra: dict
|
payment_hash: str,
|
||||||
|
wallet: str,
|
||||||
|
event: str,
|
||||||
|
name: str = "",
|
||||||
|
email: str = "",
|
||||||
|
user_id: Optional[str] = None,
|
||||||
|
extra: Optional[dict] = None,
|
||||||
) -> Ticket:
|
) -> Ticket:
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
ticket = Ticket(
|
ticket = Ticket(
|
||||||
|
|
@ -18,6 +25,7 @@ async def create_ticket(
|
||||||
event=event,
|
event=event,
|
||||||
name=name,
|
name=name,
|
||||||
email=email,
|
email=email,
|
||||||
|
user_id=user_id,
|
||||||
registered=False,
|
registered=False,
|
||||||
paid=False,
|
paid=False,
|
||||||
reg_timestamp=now,
|
reg_timestamp=now,
|
||||||
|
|
@ -102,6 +110,23 @@ async def get_events(wallet_ids: str | list[str]) -> list[Event]:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_all_events() -> list[Event]:
|
||||||
|
"""Get all events without wallet filtering (public endpoint)."""
|
||||||
|
return await db.fetchall(
|
||||||
|
"SELECT * FROM events.events ORDER BY time DESC",
|
||||||
|
model=Event,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_tickets_by_user_id(user_id: str) -> list[Ticket]:
|
||||||
|
"""Get all tickets for a specific user by their user_id."""
|
||||||
|
return await db.fetchall(
|
||||||
|
"SELECT * FROM events.ticket WHERE user_id = :user_id ORDER BY time DESC",
|
||||||
|
{"user_id": user_id},
|
||||||
|
model=Ticket,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def delete_event(event_id: str) -> None:
|
async def delete_event(event_id: str) -> None:
|
||||||
await db.execute("DELETE FROM events.events WHERE id = :id", {"id": event_id})
|
await db.execute("DELETE FROM events.events WHERE id = :id", {"id": event_id})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -175,3 +175,11 @@ async def m006_add_extra_fields(db):
|
||||||
|
|
||||||
# Add 'extra' column to ticket table
|
# Add 'extra' column to ticket table
|
||||||
await db.execute("ALTER TABLE events.ticket ADD COLUMN extra TEXT;")
|
await db.execute("ALTER TABLE events.ticket ADD COLUMN extra TEXT;")
|
||||||
|
|
||||||
|
|
||||||
|
async def m007_add_user_id(db):
|
||||||
|
"""
|
||||||
|
Add user_id column to tickets table.
|
||||||
|
Allows ticket purchase via LNbits user-id without name/email.
|
||||||
|
"""
|
||||||
|
await db.execute("ALTER TABLE events.ticket ADD COLUMN user_id TEXT;")
|
||||||
|
|
|
||||||
22
models.py
22
models.py
|
|
@ -1,7 +1,8 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import Query
|
from fastapi import Query
|
||||||
from pydantic import BaseModel, EmailStr, Field, validator
|
from pydantic import BaseModel, EmailStr, Field, root_validator, validator
|
||||||
|
|
||||||
|
|
||||||
class PromoCode(BaseModel):
|
class PromoCode(BaseModel):
|
||||||
|
|
@ -66,18 +67,29 @@ class TicketExtra(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class CreateTicket(BaseModel):
|
class CreateTicket(BaseModel):
|
||||||
name: str
|
name: Optional[str] = None
|
||||||
email: EmailStr
|
email: Optional[str] = None
|
||||||
|
user_id: Optional[str] = None
|
||||||
promo_code: str | None = None
|
promo_code: str | None = None
|
||||||
refund_address: str | None = None
|
refund_address: str | None = None
|
||||||
|
|
||||||
|
@root_validator
|
||||||
|
def validate_identifiers(cls, values):
|
||||||
|
user_id = values.get("user_id")
|
||||||
|
name = values.get("name")
|
||||||
|
email = values.get("email")
|
||||||
|
if not user_id and not (name and email):
|
||||||
|
raise ValueError("Either user_id or both name and email must be provided")
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
class Ticket(BaseModel):
|
class Ticket(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
wallet: str
|
wallet: str
|
||||||
event: str
|
event: str
|
||||||
name: str
|
name: str = ""
|
||||||
email: str
|
email: str = ""
|
||||||
|
user_id: Optional[str] = None
|
||||||
registered: bool
|
registered: bool
|
||||||
paid: bool
|
paid: bool
|
||||||
time: datetime
|
time: datetime
|
||||||
|
|
|
||||||
57
views_api.py
57
views_api.py
|
|
@ -21,11 +21,13 @@ from .crud import (
|
||||||
delete_event,
|
delete_event,
|
||||||
delete_event_tickets,
|
delete_event_tickets,
|
||||||
delete_ticket,
|
delete_ticket,
|
||||||
|
get_all_events,
|
||||||
get_event,
|
get_event,
|
||||||
get_event_tickets,
|
get_event_tickets,
|
||||||
get_events,
|
get_events,
|
||||||
get_ticket,
|
get_ticket,
|
||||||
get_tickets,
|
get_tickets,
|
||||||
|
get_tickets_by_user_id,
|
||||||
update_event,
|
update_event,
|
||||||
update_ticket,
|
update_ticket,
|
||||||
)
|
)
|
||||||
|
|
@ -49,6 +51,12 @@ async def api_events(
|
||||||
return [event.dict() for event in await get_events(wallet_ids)]
|
return [event.dict() for event in await get_events(wallet_ids)]
|
||||||
|
|
||||||
|
|
||||||
|
@events_api_router.get("/api/v1/events/public")
|
||||||
|
async def api_events_public():
|
||||||
|
"""Retrieve all events (read-only, no auth required)."""
|
||||||
|
return [event.dict() for event in await get_all_events()]
|
||||||
|
|
||||||
|
|
||||||
@events_api_router.post("/api/v1/events")
|
@events_api_router.post("/api/v1/events")
|
||||||
@events_api_router.put("/api/v1/events/{event_id}")
|
@events_api_router.put("/api/v1/events/{event_id}")
|
||||||
async def api_event_create(
|
async def api_event_create(
|
||||||
|
|
@ -131,14 +139,20 @@ async def api_tickets(
|
||||||
return await get_tickets(wallet_ids)
|
return await get_tickets(wallet_ids)
|
||||||
|
|
||||||
|
|
||||||
|
@events_api_router.get("/api/v1/tickets/user/{user_id}")
|
||||||
|
async def api_tickets_by_user_id(user_id: str) -> list[Ticket]:
|
||||||
|
"""Get all tickets for a specific user by their user_id."""
|
||||||
|
return await get_tickets_by_user_id(user_id)
|
||||||
|
|
||||||
|
|
||||||
@events_api_router.post("/api/v1/tickets/{event_id}")
|
@events_api_router.post("/api/v1/tickets/{event_id}")
|
||||||
async def api_ticket_create(event_id: str, data: CreateTicket):
|
async def api_ticket_create(event_id: str, data: CreateTicket):
|
||||||
name = data.name
|
if data.user_id:
|
||||||
email = data.email
|
return await api_ticket_make_ticket_with_user_id(event_id, data.user_id)
|
||||||
promo_code = data.promo_code.upper() if data.promo_code else None
|
promo_code = data.promo_code.upper() if data.promo_code else None
|
||||||
refund_address = data.refund_address
|
refund_address = data.refund_address
|
||||||
return await api_ticket_make_ticket(
|
return await api_ticket_make_ticket(
|
||||||
event_id, name, email, promo_code, refund_address
|
event_id, data.name, data.email, promo_code, refund_address
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -198,6 +212,43 @@ async def api_ticket_make_ticket(event_id, name, email, promo_code, refund_addre
|
||||||
return {"payment_hash": payment.payment_hash, "payment_request": payment.bolt11}
|
return {"payment_hash": payment.payment_hash, "payment_request": payment.bolt11}
|
||||||
|
|
||||||
|
|
||||||
|
async def api_ticket_make_ticket_with_user_id(event_id: str, user_id: str):
|
||||||
|
event = await get_event(event_id)
|
||||||
|
if not event:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist."
|
||||||
|
)
|
||||||
|
|
||||||
|
price = event.price_per_ticket
|
||||||
|
extra = {"tag": "events", "user_id": user_id}
|
||||||
|
|
||||||
|
if event.currency != "sats":
|
||||||
|
extra["fiat"] = True
|
||||||
|
extra["currency"] = event.currency
|
||||||
|
extra["fiatAmount"] = event.price_per_ticket
|
||||||
|
extra["rate"] = await get_fiat_rate_satoshis(event.currency)
|
||||||
|
price = await fiat_amount_as_satoshis(event.price_per_ticket, event.currency)
|
||||||
|
|
||||||
|
try:
|
||||||
|
payment = await create_invoice(
|
||||||
|
wallet_id=event.wallet,
|
||||||
|
amount=price,
|
||||||
|
memo=f"{event_id}",
|
||||||
|
extra=extra,
|
||||||
|
)
|
||||||
|
await create_ticket(
|
||||||
|
payment_hash=payment.payment_hash,
|
||||||
|
wallet=event.wallet,
|
||||||
|
event=event.id,
|
||||||
|
user_id=user_id,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||||
|
) from exc
|
||||||
|
return {"payment_hash": payment.payment_hash, "payment_request": payment.bolt11}
|
||||||
|
|
||||||
|
|
||||||
@events_api_router.post("/api/v1/tickets/{event_id}/{payment_hash}")
|
@events_api_router.post("/api/v1/tickets/{event_id}/{payment_hash}")
|
||||||
async def api_ticket_send_ticket(event_id, payment_hash):
|
async def api_ticket_send_ticket(event_id, payment_hash):
|
||||||
event = await get_event(event_id)
|
event = await get_event(event_id)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue