From 81826f3c1325314b7d2da75704a49e612937a0c2 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 5 Oct 2021 09:19:21 +0100 Subject: [PATCH] fix: lnurlp, withdraw --- lnbits/__main__.py | 1 - lnbits/extensions/lnurlp/__init__.py | 26 +++++++++++---- lnbits/extensions/lnurlp/crud.py | 32 +++++++------------ lnbits/extensions/lnurlp/lnurl.py | 2 +- lnbits/extensions/lnurlp/models.py | 24 ++++++++++---- lnbits/extensions/lnurlp/tasks.py | 15 ++++----- .../lnurlp/templates/lnurlp/_api_docs.html | 23 ++++++++----- lnbits/extensions/lnurlp/views.py | 4 +-- lnbits/extensions/lnurlp/views_api.py | 24 +++++--------- lnbits/extensions/withdraw/__init__.py | 19 ++++++++--- lnbits/extensions/withdraw/lnurl.py | 1 - lnbits/extensions/withdraw/models.py | 10 +++--- lnbits/extensions/withdraw/views.py | 6 ++-- lnbits/extensions/withdraw/views_api.py | 6 ++-- 14 files changed, 108 insertions(+), 85 deletions(-) diff --git a/lnbits/__main__.py b/lnbits/__main__.py index 186b29014..e9c43cdae 100644 --- a/lnbits/__main__.py +++ b/lnbits/__main__.py @@ -27,4 +27,3 @@ print( - service fee: {SERVICE_FEE} """ ) - diff --git a/lnbits/extensions/lnurlp/__init__.py b/lnbits/extensions/lnurlp/__init__.py index 804449497..8467d0199 100644 --- a/lnbits/extensions/lnurlp/__init__.py +++ b/lnbits/extensions/lnurlp/__init__.py @@ -1,34 +1,48 @@ +import asyncio + from fastapi import APIRouter, FastAPI from fastapi.staticfiles import StaticFiles from starlette.routing import Mount from lnbits.db import Database +from lnbits.helpers import template_renderer +from lnbits.tasks import catch_everything_and_restart db = Database("ext_lnurlp") +lnurlp_static_files = [ + { + "path": "/lnurlp/static", + "app": StaticFiles(directory="lnbits/extensions/lnurlp/static"), + "name": "lnurlp_static", + } +] + lnurlp_ext: APIRouter = APIRouter( prefix="/lnurlp", - static_folder="static", + tags=["lnurlp"] # "lnurlp", __name__, static_folder="static", template_folder="templates" ) def lnurlp_renderer(): return template_renderer( [ - "lnbits/extensions/lnticket/templates", + "lnbits/extensions/lnurlp/templates", ] ) from .views_api import * # noqa from .views import * # noqa +from .tasks import wait_for_paid_invoices + +def lnurlp_start(): + loop = asyncio.get_event_loop() + loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) + -@lnurlp_ext.on_event("startup") -def _do_it(): - register_listeners() # from .lnurl import * # noqa -# from .tasks import register_listeners # from lnbits.tasks import record_async diff --git a/lnbits/extensions/lnurlp/crud.py b/lnbits/extensions/lnurlp/crud.py index b1744a64e..01cd2828b 100644 --- a/lnbits/extensions/lnurlp/crud.py +++ b/lnbits/extensions/lnurlp/crud.py @@ -2,25 +2,17 @@ from typing import List, Optional, Union from lnbits.db import SQLITE from . import db -from .models import PayLink +from .models import PayLink, CreatePayLinkData async def create_pay_link( - *, - wallet_id: str, - description: str, - min: int, - max: int, - comment_chars: int = 0, - currency: Optional[str] = None, - webhook_url: Optional[str] = None, - success_text: Optional[str] = None, - success_url: Optional[str] = None, + data: CreatePayLinkData, + wallet_id: str ) -> PayLink: returning = "" if db.type == SQLITE else "RETURNING ID" method = db.execute if db.type == SQLITE else db.fetchone - + print("CPL", wallet_id, data) result = await (method)( f""" INSERT INTO lnurlp.pay_links ( @@ -41,14 +33,14 @@ async def create_pay_link( """, ( wallet_id, - description, - min, - max, - webhook_url, - success_text, - success_url, - comment_chars, - currency, + data.description, + data.min, + data.max, + data.webhook_url, + data.success_text, + data.success_url, + data.comment_chars, + data.currency, ), ) if db.type == SQLITE: diff --git a/lnbits/extensions/lnurlp/lnurl.py b/lnbits/extensions/lnurlp/lnurl.py index bb9eb3332..1df4e7445 100644 --- a/lnbits/extensions/lnurlp/lnurl.py +++ b/lnbits/extensions/lnurlp/lnurl.py @@ -11,7 +11,7 @@ from . import lnurlp_ext from .crud import increment_pay_link -@lnurlp_ext.get("/api/v1/lnurl/{link_id}", status_code=HTTPStatus.OK) +@lnurlp_ext.get("/api/v1/lnurl/{link_id}", status_code=HTTPStatus.OK, name="lnurlp.api_lnurl_response") async def api_lnurl_response(request: Request, link_id): link = await increment_pay_link(link_id, served_meta=1) if not link: diff --git a/lnbits/extensions/lnurlp/models.py b/lnbits/extensions/lnurlp/models.py index f1d4fff1f..279a7116b 100644 --- a/lnbits/extensions/lnurlp/models.py +++ b/lnbits/extensions/lnurlp/models.py @@ -1,12 +1,23 @@ import json from urllib.parse import urlparse, urlunparse, parse_qs, urlencode, ParseResult -from quart import url_for +from starlette.requests import Request +from fastapi.param_functions import Query from typing import Optional, Dict from lnbits.lnurl import encode as lnurl_encode # type: ignore from lnurl.types import LnurlPayMetadata # type: ignore from sqlite3 import Row from pydantic import BaseModel +class CreatePayLinkData(BaseModel): + description: str + min: int = Query(0.01, ge=0.01) + max: int = Query(0.01, ge=0.01) + currency: str = Query(None) + comment_chars: int = Query(0, ge=0, lt=800) + webhook_url: str = Query(None) + success_text: str = Query(None) + success_url: str = Query(None) + class PayLink(BaseModel): id: int wallet: str @@ -14,10 +25,10 @@ class PayLink(BaseModel): min: int served_meta: int served_pr: int - webhook_url: str - success_text: str - success_url: str - currency: str + webhook_url: Optional[str] + success_text: Optional[str] + success_url: Optional[str] + currency: Optional[str] comment_chars: int max: int @@ -28,7 +39,8 @@ class PayLink(BaseModel): @property def lnurl(self) -> str: - url = url_for("lnurlp.api_lnurl_response", link_id=self.id, _external=True) + r = Request + url = r.url_for("lnurlp.api_lnurl_response", link_id=self.id, _external=True) return lnurl_encode(url) @property diff --git a/lnbits/extensions/lnurlp/tasks.py b/lnbits/extensions/lnurlp/tasks.py index e8d6a453f..470fdea93 100644 --- a/lnbits/extensions/lnurlp/tasks.py +++ b/lnbits/extensions/lnurlp/tasks.py @@ -1,4 +1,4 @@ -import trio +import asyncio import json import httpx @@ -9,17 +9,14 @@ from lnbits.tasks import register_invoice_listener from .crud import get_pay_link -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) +async def wait_for_paid_invoices(): + invoice_queue = asyncio.Queue() + register_invoice_listener(invoice_queue) - -async def wait_for_paid_invoices(invoice_paid_chan: trio.MemoryReceiveChannel): - async for payment in invoice_paid_chan: + while True: + payment = await invoice_queue.get() await on_invoice_paid(payment) - async def on_invoice_paid(payment: Payment) -> None: if "lnurlp" != payment.extra.get("tag"): # not an lnurlp invoice diff --git a/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html b/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html index d47ab1f1c..6bea55945 100644 --- a/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html +++ b/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html @@ -18,7 +18,7 @@
Curl example
curl -X GET {{ request.url_root }}api/v1/links -H "X-Api-Key: {{ - g.user.wallets[0].inkey }}" + user.wallets[0].inkey }}" @@ -27,7 +27,8 @@ GET /lnurlp/api/v1/links/<pay_id>GET + /lnurlp/api/v1/links/<pay_id>
Headers
{"X-Api-Key": <invoice_key>}
@@ -39,7 +40,7 @@
Curl example
curl -X GET {{ request.url_root }}api/v1/links/<pay_id> -H - "X-Api-Key: {{ g.user.wallets[0].inkey }}" + "X-Api-Key: {{ user.wallets[0].inkey }}"
@@ -56,7 +57,11 @@
Headers
{"X-Api-Key": <admin_key>}
Body (application/json)
- {"description": <string> "amount": <integer> "max": <integer> "min": <integer> "comment_chars": <integer>} + {"description": <string> "amount": <integer> "max": + <integer> "min": <integer> "comment_chars": + <integer>}
Returns 201 CREATED (application/json)
@@ -64,8 +69,10 @@
Curl example
curl -X POST {{ request.url_root }}api/v1/links -d '{"description": - <string>, "amount": <integer>, "max": <integer>, "min": <integer>, "comment_chars": <integer>}' -H "Content-type: - application/json" -H "X-Api-Key: {{ g.user.wallets[0].adminkey }}" + <string>, "amount": <integer>, "max": <integer>, + "min": <integer>, "comment_chars": <integer>}' -H + "Content-type: application/json" -H "X-Api-Key: {{ + user.wallets[0].adminkey }}" @@ -95,7 +102,7 @@ >curl -X PUT {{ request.url_root }}api/v1/links/<pay_id> -d '{"description": <string>, "amount": <integer>}' -H "Content-type: application/json" -H "X-Api-Key: {{ - g.user.wallets[0].adminkey }}" + user.wallets[0].adminkey }}" @@ -120,7 +127,7 @@
Curl example
curl -X DELETE {{ request.url_root }}api/v1/links/<pay_id> -H - "X-Api-Key: {{ g.user.wallets[0].adminkey }}" + "X-Api-Key: {{ user.wallets[0].adminkey }}" diff --git a/lnbits/extensions/lnurlp/views.py b/lnbits/extensions/lnurlp/views.py index c49c28074..27a769f74 100644 --- a/lnbits/extensions/lnurlp/views.py +++ b/lnbits/extensions/lnurlp/views.py @@ -1,6 +1,6 @@ from http import HTTPStatus -from lnbits.decorators import check_user_exists, validate_uuids +from lnbits.decorators import check_user_exists from . import lnurlp_ext, lnurlp_renderer from .crud import get_pay_link @@ -15,7 +15,7 @@ from lnbits.core.models import User templates = Jinja2Templates(directory="templates") @lnurlp_ext.get("/", response_class=HTMLResponse) -@validate_uuids(["usr"], required=True) +# @validate_uuids(["usr"], required=True) # @check_user_exists() async def index(request: Request, user: User = Depends(check_user_exists)): return lnurlp_renderer().TemplateResponse("lnurlp/index.html", {"request": request, "user": user.dict()}) diff --git a/lnbits/extensions/lnurlp/views_api.py b/lnbits/extensions/lnurlp/views_api.py index ac9b4dd9b..9af7b8814 100644 --- a/lnbits/extensions/lnurlp/views_api.py +++ b/lnbits/extensions/lnurlp/views_api.py @@ -10,8 +10,9 @@ from starlette.requests import Request from starlette.responses import HTMLResponse, JSONResponse # type: ignore from lnbits.core.crud import get_user -from lnbits.decorators import api_check_wallet_key, api_validate_post_request +from lnbits.decorators import WalletTypeInfo, get_key_type from lnbits.utils.exchange_rates import currencies, get_fiat_rate_satoshis +from .models import CreatePayLinkData from . import lnurlp_ext from .crud import ( @@ -75,20 +76,11 @@ async def api_link_retrieve(link_id, wallet: WalletTypeInfo = Depends(get_key_ty return {**link._asdict(), **{"lnurl": link.lnurl}} -class CreateData(BaseModel): - description: str - min: int = Query(0.01, ge=0.01) - max: int = Query(0.01, ge=0.01) - currency: Optional[str] - comment_chars: int = Query(0, ge=0, lt=800) - webhook_url: Optional[str] - success_text: Optional[str] - success_url: Optional[str] @lnurlp_ext.post("/api/v1/links", status_code=HTTPStatus.CREATED) @lnurlp_ext.put("/api/v1/links/{link_id}", status_code=HTTPStatus.OK) # @api_check_wallet_key("invoice") -async def api_link_create_or_update(data: CreateData, link_id=None, wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_link_create_or_update(data: CreatePayLinkData, link_id=None, wallet: WalletTypeInfo = Depends(get_key_type)): if data.min > data.max: raise HTTPException( detail="Min is greater than max.", @@ -128,18 +120,18 @@ async def api_link_create_or_update(data: CreateData, link_id=None, wallet: Wall # HTTPStatus.NOT_FOUND, # ) - if link.wallet != g.wallet.id: + if link.wallet != wallet.wallet.id: raise HTTPException( detail="Not your pay link.", status_code=HTTPStatus.FORBIDDEN ) # return {"message": "Not your pay link."}, HTTPStatus.FORBIDDEN - link = await update_pay_link(link_id, **data) + link = await update_pay_link(link_id, data) else: - link = await create_pay_link(wallet_id=wallet.wallet.id, **data) - - return {**link._asdict(), **{"lnurl": link.lnurl}} + link = await create_pay_link(data, wallet_id=wallet.wallet.id) + print("LINK", link) + return {**link.dict(), "lnurl": link.lnurl} @lnurlp_ext.delete("/api/v1/links/{link_id}") diff --git a/lnbits/extensions/withdraw/__init__.py b/lnbits/extensions/withdraw/__init__.py index 5a90179c1..184b4f01f 100644 --- a/lnbits/extensions/withdraw/__init__.py +++ b/lnbits/extensions/withdraw/__init__.py @@ -1,13 +1,23 @@ from fastapi import APIRouter +from fastapi.staticfiles import StaticFiles from lnbits.db import Database +from lnbits.helpers import template_renderer db = Database("ext_withdraw") +withdraw_static_files = [ + { + "path": "/withdraw/static", + "app": StaticFiles(directory="lnbits/extensions/withdraw/static"), + "name": "withdraw_static", + } +] + withdraw_ext: APIRouter = APIRouter( prefix="/withdraw", - static_folder="static" + tags=["withdraw"], # "withdraw", __name__, static_folder="static", template_folder="templates" ) @@ -23,6 +33,7 @@ from .views_api import * # noqa from .views import * # noqa from .lnurl import * # noqa -@withdraw_ext.on_event("startup") -def _do_it(): - register_listeners() + +# @withdraw_ext.on_event("startup") +# def _do_it(): +# register_listeners() diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index 32f4a1c3f..f1974f2a1 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -1,7 +1,6 @@ import shortuuid # type: ignore from http import HTTPStatus from datetime import datetime -from quart import jsonify, request from lnbits.core.services import pay_invoice diff --git a/lnbits/extensions/withdraw/models.py b/lnbits/extensions/withdraw/models.py index da32ee7d0..aaa8b04e1 100644 --- a/lnbits/extensions/withdraw/models.py +++ b/lnbits/extensions/withdraw/models.py @@ -1,4 +1,4 @@ -from quart import url_for +from fastapi import Request from lnurl import Lnurl, LnurlWithdrawResponse, encode as lnurl_encode # type: ignore from sqlite3 import Row from pydantic import BaseModel @@ -33,19 +33,19 @@ class WithdrawLink(BaseModel): return self.used >= self.uses @property - def lnurl(self) -> Lnurl: + def lnurl(self, req: Request) -> Lnurl: if self.is_unique: usescssv = self.usescsv.split(",") tohash = self.id + self.unique_hash + usescssv[self.number] multihash = shortuuid.uuid(name=tohash) - url = url_for( + url = req.url_for( "withdraw.api_lnurl_multi_response", unique_hash=self.unique_hash, id_unique_hash=multihash, _external=True, ) else: - url = url_for( + url = req.url_for( "withdraw.api_lnurl_response", unique_hash=self.unique_hash, _external=True, @@ -55,7 +55,7 @@ class WithdrawLink(BaseModel): @property def lnurl_response(self) -> LnurlWithdrawResponse: - url = url_for( + url = req.url_for( "withdraw.api_lnurl_callback", unique_hash=self.unique_hash, _external=True ) return LnurlWithdrawResponse( diff --git a/lnbits/extensions/withdraw/views.py b/lnbits/extensions/withdraw/views.py index 8caf8f287..bf519d6bc 100644 --- a/lnbits/extensions/withdraw/views.py +++ b/lnbits/extensions/withdraw/views.py @@ -1,7 +1,7 @@ from http import HTTPStatus import pyqrcode from io import BytesIO -from lnbits.decorators import check_user_exists, validate_uuids +from lnbits.decorators import check_user_exists from . import withdraw_ext, withdraw_renderer from .crud import get_withdraw_link, chunks @@ -16,7 +16,7 @@ from lnbits.core.models import User templates = Jinja2Templates(directory="templates") @withdraw_ext.get("/", response_class=HTMLResponse) -@validate_uuids(["usr"], required=True) +# @validate_uuids(["usr"], required=True) # @check_user_exists() async def index(request: Request, user: User = Depends(check_user_exists)): return withdraw_renderer().TemplateResponse("withdraw/index.html", {"request":request,"user": user.dict()}) @@ -36,7 +36,7 @@ async def display(request: Request, link_id): @withdraw_ext.get("/img/{link_id}", response_class=HTMLResponse) -async def img(request: Request, link_id, response: Response): +async def img(request: Request, link_id): link = await get_withdraw_link(link_id, 0) if not link: raise HTTPException( diff --git a/lnbits/extensions/withdraw/views_api.py b/lnbits/extensions/withdraw/views_api.py index eec046e3f..54aee937d 100644 --- a/lnbits/extensions/withdraw/views_api.py +++ b/lnbits/extensions/withdraw/views_api.py @@ -9,7 +9,7 @@ from starlette.requests import Request from starlette.responses import HTMLResponse, JSONResponse # type: ignore from lnbits.core.crud import get_user -from lnbits.decorators import api_check_wallet_key, api_validate_post_request +from lnbits.decorators import WalletTypeInfo, get_key_type # from fastapi import FastAPI, Query, Response @@ -83,8 +83,8 @@ class CreateData(BaseModel): @withdraw_ext.post("/api/v1/links", status_code=HTTPStatus.CREATED) @withdraw_ext.put("/api/v1/links/{link_id}", status_code=HTTPStatus.OK) -@api_check_wallet_key("admin") -async def api_link_create_or_update(data: CreateData, link_id: str = None, response: Response): +# @api_check_wallet_key("admin") +async def api_link_create_or_update(data: CreateData, link_id: str = None): if data.max_withdrawable < data.min_withdrawable: raise HTTPException( detail="`max_withdrawable` needs to be at least `min_withdrawable`.",