lnurlp fastAPI port

This commit is contained in:
Tiago vasconcelos 2021-09-28 18:10:22 +01:00
parent de4f7fd07a
commit dcad7ea5fa
3 changed files with 132 additions and 66 deletions

View file

@ -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 from lnbits.db import Database
db = Database("ext_lnurlp") db = Database("ext_lnurlp")
lnurlp_ext: Blueprint = Blueprint( lnurlp_ext: APIRouter = APIRouter(
"lnurlp", __name__, static_folder="static", template_folder="templates" 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_api import * # noqa
from .views 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))

View file

@ -3,33 +3,42 @@ from http import HTTPStatus
from lnbits.decorators import check_user_exists, validate_uuids 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 .crud import get_pay_link
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from fastapi.params import Depends
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates") templates = Jinja2Templates(directory="templates")
@lnurlp_ext.route("/") @lnurlp_ext.get("/", response_class=HTMLResponse)
@validate_uuids(["usr"], required=True) @validate_uuids(["usr"], required=True)
@check_user_exists() # @check_user_exists()
async def index(request: Request): async def index(request: Request, user: User = Depends(check_user_exists)):
return await templates.TemplateResponse("lnurlp/index.html", {"request": request, "user":g.user}) return lnurlp_renderer().TemplateResponse("lnurlp/index.html", {"request": request, "user": user})
@lnurlp_ext.route("/<link_id>") @lnurlp_ext.get("/{link_id}", response_class=HTMLResponse)
async def display(request: Request,link_id): async def display(request: Request,link_id):
link = await get_pay_link(link_id) link = await get_pay_link(link_id)
if not link: 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/<link_id>") @lnurlp_ext.get("/print/{link_id}", response_class=HTMLResponse)
async def print_qr(request: Request,link_id): async def print_qr(request: Request,link_id):
link = await get_pay_link(link_id) link = await get_pay_link(link_id)
if not link: 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})

View file

@ -1,9 +1,13 @@
from typing import Optional from typing import Optional
from fastapi.params import Depends
from fastapi.param_functions import Query from fastapi.param_functions import Query
from pydantic.main import BaseModel from pydantic.main import BaseModel
from quart import g, jsonify, request
from http import HTTPStatus from http import HTTPStatus
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore 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.core.crud import get_user
from lnbits.decorators import api_check_wallet_key, api_validate_post_request 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") @lnurlp_ext.get("/api/v1/currencies")
async def api_list_currencies_available(): async def api_list_currencies_available():
return jsonify(list(currencies.keys())) return list(currencies.keys())
@lnurlp_ext.get("/api/v1/links") @lnurlp_ext.get("/api/v1/links", status_code=HTTPStatus.OK)
@api_check_wallet_key("invoice") # @api_check_wallet_key("invoice")
async def api_links(): async def api_links(wallet: WalletTypeInfo = Depends(get_key_type)):
wallet_ids = [g.wallet.id] wallet_ids = [wallet.wallet.id]
if "all_wallets" in request.args: if "all_wallets" in Request.path_parameters:
wallet_ids = (await get_user(g.wallet.user)).wallet_ids wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
try: try:
return ( return (
@ -37,29 +41,40 @@ async def api_links():
{**link._asdict(), **{"lnurl": link.lnurl}} {**link._asdict(), **{"lnurl": link.lnurl}}
for link in await get_pay_links(wallet_ids) for link in await get_pay_links(wallet_ids)
], ],
HTTPStatus.OK,
) )
except LnurlInvalidUrl: except LnurlInvalidUrl:
return ( raise HTTPException(
{ status_code=HTTPStatus.UPGRADE_REQUIRED,
"message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor." detail="LNURLs need to be delivered over a publically accessible `https` domain or Tor.",
},
HTTPStatus.UPGRADE_REQUIRED,
) )
# 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}") @lnurlp_ext.get("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
@api_check_wallet_key("invoice") # @api_check_wallet_key("invoice")
async def api_link_retrieve(link_id): async def api_link_retrieve(link_id, wallet: WalletTypeInfo = Depends(get_key_type)):
link = await get_pay_link(link_id) link = await get_pay_link(link_id)
if not link: 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: if link.wallet != wallet.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
return {**link._asdict(), **{"lnurl": link.lnurl}}, HTTPStatus.OK return {**link._asdict(), **{"lnurl": link.lnurl}}
class CreateData(BaseModel): class CreateData(BaseModel):
description: str description: str
@ -71,67 +86,92 @@ class CreateData(BaseModel):
success_text: Optional[str] success_text: Optional[str]
success_url: Optional[str] success_url: Optional[str]
@lnurlp_ext.post("/api/v1/links") @lnurlp_ext.post("/api/v1/links", status_code=HTTPStatus.CREATED)
@lnurlp_ext.put("/api/v1/links/{link_id}") @lnurlp_ext.put("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
@api_check_wallet_key("invoice") # @api_check_wallet_key("invoice")
async def api_link_create_or_update(data: CreateData, link_id=None): async def api_link_create_or_update(data: CreateData, link_id=None, wallet: WalletTypeInfo = Depends(get_key_type)):
if data.min > data.max: 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 ( if data.currency == None and (
round(data.min) != data.min or round(data.max) != data.max 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://": if "success_url" in data and data.success_url[:8] != "https://":
return ( raise HTTPException(
{"message": "Success URL must be secure https://..."}, detail="Success URL must be secure https://...",
HTTPStatus.BAD_REQUEST, status_code=HTTPStatus.BAD_REQUEST
) )
# return (
# {"message": "Success URL must be secure https://..."},
# HTTPStatus.BAD_REQUEST,
# )
if link_id: if link_id:
link = await get_pay_link(link_id) link = await get_pay_link(link_id)
if not link: if not link:
return ( raise HTTPException(
{"message": "Pay link does not exist."}, detail="Pay link does not exist.",
HTTPStatus.NOT_FOUND, status_code=HTTPStatus.NOT_FOUND
) )
# return (
# {"message": "Pay link does not exist."},
# HTTPStatus.NOT_FOUND,
# )
if link.wallet != g.wallet.id: 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) link = await update_pay_link(link_id, **data)
else: else:
link = await create_pay_link(wallet_id=g.wallet.id, **data) link = await create_pay_link(wallet_id=wallet.wallet.id, **data)
return ( return {**link._asdict(), **{"lnurl": link.lnurl}}
{**link._asdict(), **{"lnurl": link.lnurl}},
HTTPStatus.OK if link_id else HTTPStatus.CREATED,
)
@lnurlp_ext.delete("/api/v1/links/{link_id}") @lnurlp_ext.delete("/api/v1/links/{link_id}")
@api_check_wallet_key("invoice") # @api_check_wallet_key("invoice")
async def api_link_delete(link_id): async def api_link_delete(link_id, , wallet: WalletTypeInfo = Depends(get_key_type)):
link = await get_pay_link(link_id) link = await get_pay_link(link_id)
if not link: 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: if link.wallet != wallet.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
await delete_pay_link(link_id) await delete_pay_link(link_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
return "", 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): async def api_check_fiat_rate(currency):
try: try:
rate = await get_fiat_rate_satoshis(currency) rate = await get_fiat_rate_satoshis(currency)
except AssertionError: except AssertionError:
rate = None rate = None
return {"rate": rate}, HTTPStatus.OK return {"rate": rate}