From 8c5989d4655179f9d3f2c1818e87d6789f315002 Mon Sep 17 00:00:00 2001 From: Stefan Stammberger Date: Tue, 28 Sep 2021 21:10:51 +0200 Subject: [PATCH 1/4] fix: crash when an ext doesn't have static files --- lnbits/app.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 288caa5ba..b0289b932 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -96,9 +96,10 @@ def register_routes(app: FastAPI) -> None: ext_module = importlib.import_module(f"lnbits.extensions.{ext.code}") ext_route = getattr(ext_module, f"{ext.code}_ext") - ext_statics = getattr(ext_module, f"{ext.code}_static_files") - for s in ext_statics: - app.mount(s["path"], s["app"], s["name"]) + if hasattr(ext_module, f"{ext.code}_static_files"): + ext_statics = getattr(ext_module, f"{ext.code}_static_files") + for s in ext_statics: + app.mount(s["path"], s["app"], s["name"]) app.include_router(ext_route) except Exception as e: From 2a4314ca8490dfbc936790d3c95a22213686b73e Mon Sep 17 00:00:00 2001 From: Stefan Stammberger Date: Tue, 28 Sep 2021 21:13:04 +0200 Subject: [PATCH 2/4] fix: raise bad req error on call without X-API-key --- lnbits/decorators.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index ff42d0fd5..440f8fa12 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -96,6 +96,8 @@ async def get_key_type(r: Request, await checker.__call__(r) return WalletTypeInfo(0, checker.wallet) except HTTPException as e: + if e.status_code == HTTPStatus.BAD_REQUEST: + raise if e.status_code == HTTPStatus.UNAUTHORIZED: pass except: @@ -106,6 +108,8 @@ async def get_key_type(r: Request, await checker.__call__(r) return WalletTypeInfo(1, checker.wallet) except HTTPException as e: + if e.status_code == HTTPStatus.BAD_REQUEST: + raise if e.status_code == HTTPStatus.UNAUTHORIZED: return WalletTypeInfo(2, None) except: From 85d9a3fcc375d17262c69e04d21bd7bd0e1a3023 Mon Sep 17 00:00:00 2001 From: Stefan Stammberger Date: Tue, 28 Sep 2021 21:18:15 +0200 Subject: [PATCH 3/4] fix: some errors in lnticket ext --- lnbits/extensions/lnticket/__init__.py | 8 ++--- lnbits/extensions/lnticket/crud.py | 14 +++----- lnbits/extensions/lnticket/models.py | 18 +++++++++- lnbits/extensions/lnticket/tasks.py | 20 +++++------ lnbits/extensions/lnticket/views.py | 12 ++++--- lnbits/extensions/lnticket/views_api.py | 44 ++++++++----------------- 6 files changed, 57 insertions(+), 59 deletions(-) diff --git a/lnbits/extensions/lnticket/__init__.py b/lnbits/extensions/lnticket/__init__.py index 28ef8067b..0a872e8cf 100644 --- a/lnbits/extensions/lnticket/__init__.py +++ b/lnbits/extensions/lnticket/__init__.py @@ -1,6 +1,4 @@ -from fastapi import APIRouter, FastAPI -from fastapi.staticfiles import StaticFiles -from starlette.routing import Mount +from fastapi import APIRouter from lnbits.db import Database from lnbits.helpers import template_renderer @@ -23,7 +21,9 @@ def lnticket_renderer(): from .views_api import * # noqa from .views import * # noqa +from .tasks import register_listeners -@lntickets_ext.on_event("startup") +@lnticket_ext.on_event("startup") def _do_it(): + # FIXME: isn't called yet register_listeners() diff --git a/lnbits/extensions/lnticket/crud.py b/lnbits/extensions/lnticket/crud.py index 5c1f1e021..327fb90e5 100644 --- a/lnbits/extensions/lnticket/crud.py +++ b/lnbits/extensions/lnticket/crud.py @@ -1,9 +1,10 @@ +from lnbits.core.models import Wallet from typing import List, Optional, Union from lnbits.helpers import urlsafe_short_hash from . import db -from .models import Tickets, Forms +from .models import CreateFormData, Tickets, Forms import httpx @@ -103,13 +104,8 @@ async def delete_ticket(ticket_id: str) -> None: async def create_form( - *, - wallet: str, - name: str, - webhook: Optional[str] = None, - description: str, - amount: int, - flatrate: int, + data: CreateFormData, + wallet: Wallet, ) -> Forms: form_id = urlsafe_short_hash() await db.execute( @@ -117,7 +113,7 @@ async def create_form( INSERT INTO lnticket.form2 (id, wallet, name, webhook, description, flatrate, amount, amountmade) VALUES (?, ?, ?, ?, ?, ?, ?, ?) """, - (form_id, wallet, name, webhook, description, flatrate, amount, 0), + (form_id, wallet.id, wallet.name, data.webhook, data.description, data.flatrate, data.amount, 0), ) form = await get_form(form_id) diff --git a/lnbits/extensions/lnticket/models.py b/lnbits/extensions/lnticket/models.py index 1bc8237c7..6fac90c27 100644 --- a/lnbits/extensions/lnticket/models.py +++ b/lnbits/extensions/lnticket/models.py @@ -1,10 +1,26 @@ +from typing import Optional +from fastapi.param_functions import Query from pydantic import BaseModel +class CreateFormData(BaseModel): + name: str = Query(...) + webhook: str = Query(None) + description: str = Query(..., min_length=0) + amount: int = Query(..., ge=0) + flatrate: int = Query(...) + +class CreateTicketData(BaseModel): + form: str = Query(...) + name: str = Query(...) + email: str = Query("") + ltext: str = Query(...) + sats: int = Query(..., ge=0) + class Forms(BaseModel): id: str wallet: str name: str - webhook: str + webhook: Optional[str] description: str amount: int flatrate: int diff --git a/lnbits/extensions/lnticket/tasks.py b/lnbits/extensions/lnticket/tasks.py index 5160de1dd..d08d74c92 100644 --- a/lnbits/extensions/lnticket/tasks.py +++ b/lnbits/extensions/lnticket/tasks.py @@ -1,23 +1,21 @@ -import json -import trio # type: ignore +import asyncio from lnbits.core.models import Payment -from lnbits.core.crud import create_payment -from lnbits.core import db as core_db -from lnbits.tasks import register_invoice_listener, internal_invoice_paid -from lnbits.helpers import urlsafe_short_hash +from lnbits.tasks import register_invoice_listener from .crud import get_ticket, set_ticket_paid async def register_listeners(): - invoice_paid_chan_send, invoice_paid_chan_recv = trio.open_memory_channel(2) - register_invoice_listener(invoice_paid_chan_send) - await wait_for_paid_invoices(invoice_paid_chan_recv) + send_queue = asyncio.Queue() + recv_queue = asyncio.Queue() + register_invoice_listener(send_queue) + await wait_for_paid_invoices(recv_queue) -async def wait_for_paid_invoices(invoice_paid_chan: trio.MemoryReceiveChannel): - async for payment in invoice_paid_chan: +async def wait_for_paid_invoices(invoice_paid_queue: asyncio.Queue): + while True: + payment = await invoice_paid_queue.get() await on_invoice_paid(payment) diff --git a/lnbits/extensions/lnticket/views.py b/lnbits/extensions/lnticket/views.py index dbf3cbbd2..62002b766 100644 --- a/lnbits/extensions/lnticket/views.py +++ b/lnbits/extensions/lnticket/views.py @@ -1,7 +1,9 @@ -from quart import g, abort, render_template - +from fastapi.param_functions import Depends +from starlette.exceptions import HTTPException +from starlette.responses import HTMLResponse +from lnbits.core.models import User from lnbits.core.crud import get_wallet -from lnbits.decorators import check_user_exists, validate_uuids +from lnbits.decorators import check_user_exists from http import HTTPStatus from . import lnticket_ext, lnticket_renderer @@ -12,7 +14,9 @@ from fastapi.templating import Jinja2Templates templates = Jinja2Templates(directory="templates") @lnticket_ext.get("/", response_class=HTMLResponse) -@validate_uuids(["usr"], required=True) +# not needed as we automatically get the user with the given ID +# If no user with this ID is found, an error is raised +# @validate_uuids(["usr"], required=True) # @check_user_exists() async def index(request: Request, user: User = Depends(check_user_exists)): return lnticket_renderer().TemplateResponse("lnticket/index.html", {"request": request,"user": user.dict()}) diff --git a/lnbits/extensions/lnticket/views_api.py b/lnbits/extensions/lnticket/views_api.py index f1094e7e6..eaf73e93f 100644 --- a/lnbits/extensions/lnticket/views_api.py +++ b/lnbits/extensions/lnticket/views_api.py @@ -1,10 +1,10 @@ +from lnbits.extensions.lnticket.models import CreateFormData, CreateTicketData import re -from quart import g, jsonify, request from http import HTTPStatus +from typing import List -from fastapi import FastAPI, Query +from fastapi import Query from fastapi.params import Depends -from fastapi.encoders import jsonable_encoder from pydantic import BaseModel from starlette.exceptions import HTTPException @@ -13,7 +13,7 @@ from starlette.responses import HTMLResponse, JSONResponse # type: ignore from lnbits.core.crud import get_user, get_wallet from lnbits.core.services import create_invoice, check_invoice_status -from lnbits.decorators import api_check_wallet_key, api_validate_post_request +from lnbits.decorators import WalletTypeInfo, get_key_type from . import lnticket_ext from .crud import ( @@ -33,26 +33,17 @@ from .crud import ( # FORMS -@lnticket_ext.get("/api/v1/forms", status_code=HTTPStatus.OK) -# @api_check_wallet_key("invoice") -async def api_forms(r: Request, wallet: WalletTypeInfo = Depends(get_key_type)): +@lnticket_ext.get("/api/v1/forms") +async def api_forms_get(r: Request, all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)): wallet_ids = [wallet.wallet.id] - if "all_wallets" in r.args: + if all_wallets: wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids return ( - [form._asdict() for form in await get_forms(wallet_ids)], + [form.dict() for form in await get_forms(wallet_ids)], ) -class CreateData(BaseModel): - wallet: str = Query(...) - name: str = Query(...) - webhook: str = Query(None) - description: str = Query(..., min_length=0) - amount: int = Query(..., ge=0) - flatrate: int = Query(...) - @lnticket_ext.post("/api/v1/forms", status_code=HTTPStatus.CREATED) @lnticket_ext.put("/api/v1/forms/{form_id}") # @api_check_wallet_key("invoice") @@ -66,7 +57,7 @@ class CreateData(BaseModel): # "flatrate": {"type": "integer", "required": True}, # } # ) -async def api_form_create(data: CreateData, form_id=None, wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_form_create(data: CreateFormData, form_id=None, wallet: WalletTypeInfo = Depends(get_key_type)): if form_id: form = await get_form(form_id) @@ -86,8 +77,8 @@ async def api_form_create(data: CreateData, form_id=None, wallet: WalletTypeInfo form = await update_form(form_id, **data) else: - form = await create_form(**data) - return form._asdict() + form = await create_form(data, wallet.wallet) + return form.dict() @lnticket_ext.delete("/api/v1/forms/{form_id}") @@ -118,25 +109,18 @@ async def api_form_delete(form_id, wallet: WalletTypeInfo = Depends(get_key_type #########tickets########## -@lnticket_ext.get("/api/v1/tickets", status_code=HTTPStatus.OK) +@lnticket_ext.get("/api/v1/tickets") # @api_check_wallet_key("invoice") -async def api_tickets(all_wallets: bool = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_tickets(all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)): wallet_ids = [wallet.wallet.id] if all_wallets: wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids return ( - [form._asdict() for form in await get_tickets(wallet_ids)] + [form.dict() for form in await get_tickets(wallet_ids)] ) -class CreateTicketData(BaseModel): - form: str = Query(...) - name: str = Query(...) - email: str = Query("") - ltext: str = Query(...) - sats: int = Query(..., ge=0) - @lnticket_ext.post("/api/v1/tickets/{form_id}", status_code=HTTPStatus.CREATED) # @api_validate_post_request( # schema={ From f827d2ce181d97368161d46ab8de2e9f061b9872 Mon Sep 17 00:00:00 2001 From: Stefan Stammberger Date: Tue, 28 Sep 2021 22:03:45 +0200 Subject: [PATCH 4/4] fix: fetch tickets and forms JS and endpoints --- lnbits/extensions/lnticket/templates/lnticket/index.html | 4 ++-- lnbits/extensions/lnticket/views_api.py | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lnbits/extensions/lnticket/templates/lnticket/index.html b/lnbits/extensions/lnticket/templates/lnticket/index.html index bc9fe9a43..1607925b5 100644 --- a/lnbits/extensions/lnticket/templates/lnticket/index.html +++ b/lnbits/extensions/lnticket/templates/lnticket/index.html @@ -337,7 +337,7 @@ LNbits.api .request( 'GET', - '/lnticket/api/v1/tickets?all_wallets', + '/lnticket/api/v1/tickets?all_wallets=true', this.g.user.wallets[0].inkey ) .then(function (response) { @@ -382,7 +382,7 @@ LNbits.api .request( 'GET', - '/lnticket/api/v1/forms?all_wallets', + '/lnticket/api/v1/forms?all_wallets=true', this.g.user.wallets[0].inkey ) .then(function (response) { diff --git a/lnbits/extensions/lnticket/views_api.py b/lnbits/extensions/lnticket/views_api.py index eaf73e93f..8ead81a31 100644 --- a/lnbits/extensions/lnticket/views_api.py +++ b/lnbits/extensions/lnticket/views_api.py @@ -40,9 +40,7 @@ async def api_forms_get(r: Request, all_wallets: bool = Query(False), wallet: Wa if all_wallets: wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids - return ( - [form.dict() for form in await get_forms(wallet_ids)], - ) + return [form.dict() for form in await get_forms(wallet_ids)] @lnticket_ext.post("/api/v1/forms", status_code=HTTPStatus.CREATED) @lnticket_ext.put("/api/v1/forms/{form_id}") @@ -117,9 +115,7 @@ async def api_tickets(all_wallets: bool = Query(False), wallet: WalletTypeInfo = if all_wallets: wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids - return ( - [form.dict() for form in await get_tickets(wallet_ids)] - ) + return [form.dict() for form in await get_tickets(wallet_ids)] @lnticket_ext.post("/api/v1/tickets/{form_id}", status_code=HTTPStatus.CREATED) # @api_validate_post_request(