diff --git a/lnbits/__main__.py b/lnbits/__main__.py index 186b2901..e9c43cda 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 80444949..8467d019 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 b1744a64..01cd2828 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 bb9eb333..1df4e744 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 f1d4fff1..279a7116 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 e8d6a453..470fdea9 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 d47ab1f1..6bea5594 100644 --- a/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html +++ b/lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html @@ -18,7 +18,7 @@
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>
{"X-Api-Key": <invoice_key>}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 }}"
{"X-Api-Key": <admin_key>}{"description": <string> "amount": <integer> "max": <integer> "min": <integer> "comment_chars": <integer>}
+ {"description": <string> "amount": <integer> "max":
+ <integer> "min": <integer> "comment_chars":
+ <integer>}
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 -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 c49c2807..27a769f7 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 ac9b4dd9..9af7b881 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 5a90179c..184b4f01 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 32f4a1c3..f1974f2a 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 da32ee7d..aaa8b04e 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 8caf8f28..bf519d6b 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 eec046e3..54aee937 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`.",