From dcad7ea5fa0ed1a3f4a3cadd8b6c6d8396fda53b Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 28 Sep 2021 18:10:22 +0100 Subject: [PATCH] lnurlp fastAPI port --- lnbits/extensions/lnurlp/__init__.py | 31 ++++-- lnbits/extensions/lnurlp/views.py | 31 +++--- lnbits/extensions/lnurlp/views_api.py | 136 +++++++++++++++++--------- 3 files changed, 132 insertions(+), 66 deletions(-) diff --git a/lnbits/extensions/lnurlp/__init__.py b/lnbits/extensions/lnurlp/__init__.py index d820b197..80444949 100644 --- a/lnbits/extensions/lnurlp/__init__.py +++ b/lnbits/extensions/lnurlp/__init__.py @@ -1,18 +1,35 @@ -from quart import Blueprint +from fastapi import APIRouter, FastAPI +from fastapi.staticfiles import StaticFiles +from starlette.routing import Mount + from lnbits.db import Database db = Database("ext_lnurlp") -lnurlp_ext: Blueprint = Blueprint( - "lnurlp", __name__, static_folder="static", template_folder="templates" +lnurlp_ext: APIRouter = APIRouter( + prefix="/lnurlp", + static_folder="static", + # "lnurlp", __name__, static_folder="static", template_folder="templates" ) +def lnurlp_renderer(): + return template_renderer( + [ + "lnbits/extensions/lnticket/templates", + ] + ) + from .views_api import * # noqa from .views import * # noqa -from .lnurl import * # noqa -from .tasks import register_listeners -from lnbits.tasks import record_async +@lnurlp_ext.on_event("startup") +def _do_it(): + register_listeners() -lnurlp_ext.record(record_async(register_listeners)) +# from .lnurl import * # noqa +# from .tasks import register_listeners + +# from lnbits.tasks import record_async + +# lnurlp_ext.record(record_async(register_listeners)) diff --git a/lnbits/extensions/lnurlp/views.py b/lnbits/extensions/lnurlp/views.py index dcb77f44..30416284 100644 --- a/lnbits/extensions/lnurlp/views.py +++ b/lnbits/extensions/lnurlp/views.py @@ -3,33 +3,42 @@ from http import HTTPStatus from lnbits.decorators import check_user_exists, validate_uuids -from . import lnurlp_ext +from . import lnurlp_ext, lnurlp_renderer from .crud import get_pay_link from fastapi import FastAPI, Request +from fastapi.params import Depends from fastapi.templating import Jinja2Templates templates = Jinja2Templates(directory="templates") -@lnurlp_ext.route("/") +@lnurlp_ext.get("/", response_class=HTMLResponse) @validate_uuids(["usr"], required=True) -@check_user_exists() -async def index(request: Request): - return await templates.TemplateResponse("lnurlp/index.html", {"request": request, "user":g.user}) +# @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}) -@lnurlp_ext.route("/") +@lnurlp_ext.get("/{link_id}", response_class=HTMLResponse) async def display(request: Request,link_id): link = await get_pay_link(link_id) if not link: - abort(HTTPStatus.NOT_FOUND, "Pay link does not exist.") + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail="Pay link does not exist." + ) + # abort(HTTPStatus.NOT_FOUND, "Pay link does not exist.") - return await templates.TemplateResponse("lnurlp/display.html", {"request": request, "link":link}) + return await lnurlp_renderer().TemplateResponse("lnurlp/display.html", {"request": request, "link":link}) -@lnurlp_ext.route("/print/") +@lnurlp_ext.get("/print/{link_id}", response_class=HTMLResponse) async def print_qr(request: Request,link_id): link = await get_pay_link(link_id) if not link: - abort(HTTPStatus.NOT_FOUND, "Pay link does not exist.") + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail="Pay link does not exist." + ) + # abort(HTTPStatus.NOT_FOUND, "Pay link does not exist.") - return await templates.TemplateResponse("lnurlp/print_qr.html", {"request": request, "link":link}) + return await lnurlp_renderer().TemplateResponse("lnurlp/print_qr.html", {"request": request, "link":link}) diff --git a/lnbits/extensions/lnurlp/views_api.py b/lnbits/extensions/lnurlp/views_api.py index a41e510e..a77e4c32 100644 --- a/lnbits/extensions/lnurlp/views_api.py +++ b/lnbits/extensions/lnurlp/views_api.py @@ -1,9 +1,13 @@ from typing import Optional +from fastapi.params import Depends from fastapi.param_functions import Query from pydantic.main import BaseModel -from quart import g, jsonify, request + from http import HTTPStatus from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore +from starlette.exceptions import HTTPException +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 @@ -20,16 +24,16 @@ from .crud import ( @lnurlp_ext.get("/api/v1/currencies") async def api_list_currencies_available(): - return jsonify(list(currencies.keys())) + return list(currencies.keys()) -@lnurlp_ext.get("/api/v1/links") -@api_check_wallet_key("invoice") -async def api_links(): - wallet_ids = [g.wallet.id] +@lnurlp_ext.get("/api/v1/links", status_code=HTTPStatus.OK) +# @api_check_wallet_key("invoice") +async def api_links(wallet: WalletTypeInfo = Depends(get_key_type)): + wallet_ids = [wallet.wallet.id] - if "all_wallets" in request.args: - wallet_ids = (await get_user(g.wallet.user)).wallet_ids + if "all_wallets" in Request.path_parameters: + wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids try: return ( @@ -37,29 +41,40 @@ async def api_links(): {**link._asdict(), **{"lnurl": link.lnurl}} for link in await get_pay_links(wallet_ids) ], - HTTPStatus.OK, ) except LnurlInvalidUrl: - return ( - { - "message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor." - }, - HTTPStatus.UPGRADE_REQUIRED, + raise HTTPException( + status_code=HTTPStatus.UPGRADE_REQUIRED, + detail="LNURLs need to be delivered over a publically accessible `https` domain or Tor.", ) + # return ( + # { + # "message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor." + # }, + # HTTPStatus.UPGRADE_REQUIRED, + # ) -@lnurlp_ext.get("/api/v1/links/{link_id}") -@api_check_wallet_key("invoice") -async def api_link_retrieve(link_id): +@lnurlp_ext.get("/api/v1/links/{link_id}", status_code=HTTPStatus.OK) +# @api_check_wallet_key("invoice") +async def api_link_retrieve(link_id, wallet: WalletTypeInfo = Depends(get_key_type)): link = await get_pay_link(link_id) if not link: - return {"message": "Pay link does not exist."}, HTTPStatus.NOT_FOUND + raise HTTPException( + detail="Pay link does not exist.", + status_code=HTTPStatus.NOT_FOUND + ) + # return {"message": "Pay link does not exist."}, HTTPStatus.NOT_FOUND - if link.wallet != g.wallet.id: - return {"message": "Not your pay link."}, HTTPStatus.FORBIDDEN + 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 - return {**link._asdict(), **{"lnurl": link.lnurl}}, HTTPStatus.OK + return {**link._asdict(), **{"lnurl": link.lnurl}} class CreateData(BaseModel): description: str @@ -71,67 +86,92 @@ class CreateData(BaseModel): success_text: Optional[str] success_url: Optional[str] -@lnurlp_ext.post("/api/v1/links") -@lnurlp_ext.put("/api/v1/links/{link_id}") -@api_check_wallet_key("invoice") -async def api_link_create_or_update(data: CreateData, link_id=None): +@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)): if data.min > data.max: - return {"message": "Min is greater than max."}, HTTPStatus.BAD_REQUEST + raise HTTPException( + detail="Min is greater than max.", + status_code=HTTPStatus.BAD_REQUEST + ) + # return {"message": "Min is greater than max."}, HTTPStatus.BAD_REQUEST if data.currency == None and ( round(data.min) != data.min or round(data.max) != data.max ): - return {"message": "Must use full satoshis."}, HTTPStatus.BAD_REQUEST + raise HTTPException( + detail="Must use full satoshis.", + status_code=HTTPStatus.BAD_REQUEST + ) + # return {"message": "Must use full satoshis."}, HTTPStatus.BAD_REQUEST if "success_url" in data and data.success_url[:8] != "https://": - return ( - {"message": "Success URL must be secure https://..."}, - HTTPStatus.BAD_REQUEST, + raise HTTPException( + detail="Success URL must be secure https://...", + status_code=HTTPStatus.BAD_REQUEST ) + # return ( + # {"message": "Success URL must be secure https://..."}, + # HTTPStatus.BAD_REQUEST, + # ) if link_id: link = await get_pay_link(link_id) if not link: - return ( - {"message": "Pay link does not exist."}, - HTTPStatus.NOT_FOUND, + raise HTTPException( + detail="Pay link does not exist.", + status_code=HTTPStatus.NOT_FOUND ) + # return ( + # {"message": "Pay link does not exist."}, + # HTTPStatus.NOT_FOUND, + # ) if link.wallet != g.wallet.id: - return {"message": "Not your pay link."}, HTTPStatus.FORBIDDEN + 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) else: - link = await create_pay_link(wallet_id=g.wallet.id, **data) + link = await create_pay_link(wallet_id=wallet.wallet.id, **data) - return ( - {**link._asdict(), **{"lnurl": link.lnurl}}, - HTTPStatus.OK if link_id else HTTPStatus.CREATED, - ) + return {**link._asdict(), **{"lnurl": link.lnurl}} @lnurlp_ext.delete("/api/v1/links/{link_id}") -@api_check_wallet_key("invoice") -async def api_link_delete(link_id): +# @api_check_wallet_key("invoice") +async def api_link_delete(link_id, , wallet: WalletTypeInfo = Depends(get_key_type)): link = await get_pay_link(link_id) if not link: - return {"message": "Pay link does not exist."}, HTTPStatus.NOT_FOUND + raise HTTPException( + detail="Pay link does not exist.", + status_code=HTTPStatus.NOT_FOUND + ) + # return {"message": "Pay link does not exist."}, HTTPStatus.NOT_FOUND - if link.wallet != g.wallet.id: - return {"message": "Not your pay link."}, HTTPStatus.FORBIDDEN + 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 await delete_pay_link(link_id) - - return "", HTTPStatus.NO_CONTENT + raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + # return "", HTTPStatus.NO_CONTENT -@lnurlp_ext.get("/api/v1/rate/{currency}") +@lnurlp_ext.get("/api/v1/rate/{currency}", status_code=HTTPStatus.OK) async def api_check_fiat_rate(currency): try: rate = await get_fiat_rate_satoshis(currency) except AssertionError: rate = None - return {"rate": rate}, HTTPStatus.OK + return {"rate": rate}