From 92ba2db2763039c97416564937a9b2367d7a1375 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 26 Oct 2021 22:50:29 +0100 Subject: [PATCH] bleskomat with import error in views_api --- lnbits/extensions/bleskomat/__init__.py | 14 +- lnbits/extensions/bleskomat/crud.py | 19 ++- lnbits/extensions/bleskomat/helpers.py | 13 +- lnbits/extensions/bleskomat/lnurl_api.py | 32 ++--- lnbits/extensions/bleskomat/models.py | 43 +++++- .../extensions/bleskomat/static/js/index.js | 2 +- lnbits/extensions/bleskomat/views.py | 25 ++-- lnbits/extensions/bleskomat/views_api.py | 124 +++++++----------- 8 files changed, 136 insertions(+), 136 deletions(-) diff --git a/lnbits/extensions/bleskomat/__init__.py b/lnbits/extensions/bleskomat/__init__.py index 42f9bb460..094488f49 100644 --- a/lnbits/extensions/bleskomat/__init__.py +++ b/lnbits/extensions/bleskomat/__init__.py @@ -1,12 +1,18 @@ -from quart import Blueprint +from fastapi import APIRouter + from lnbits.db import Database +from lnbits.helpers import template_renderer db = Database("ext_bleskomat") -bleskomat_ext: Blueprint = Blueprint( - "bleskomat", __name__, static_folder="static", template_folder="templates" +bleskomat_ext: APIRouter = APIRouter( + prefix="/bleskomat", + tags=["Bleskomat"] ) +def bleskomat_renderer(): + return template_renderer(["lnbits/extensions/events/templates"]) + from .lnurl_api import * # noqa -from .views_api import * # noqa from .views import * # noqa +from .views_api import * # noqa diff --git a/lnbits/extensions/bleskomat/crud.py b/lnbits/extensions/bleskomat/crud.py index 1cc445769..5efb08e0b 100644 --- a/lnbits/extensions/bleskomat/crud.py +++ b/lnbits/extensions/bleskomat/crud.py @@ -1,19 +1,16 @@ import secrets import time -from uuid import uuid4 from typing import List, Optional, Union +from uuid import uuid4 + from . import db -from .models import Bleskomat, BleskomatLnurl from .helpers import generate_bleskomat_lnurl_hash +from .models import Bleskomat, BleskomatLnurl, CreateBleskomat async def create_bleskomat( - *, + data: CreateBleskomat, wallet_id: str, - name: str, - fiat_currency: str, - exchange_rate_provider: str, - fee: str, ) -> Bleskomat: bleskomat_id = uuid4().hex api_key_id = secrets.token_hex(8) @@ -30,10 +27,10 @@ async def create_bleskomat( api_key_id, api_key_secret, api_key_encoding, - name, - fiat_currency, - exchange_rate_provider, - fee, + data.name, + data.fiat_currency, + data.exchange_rate_provider, + data.fee, ), ) bleskomat = await get_bleskomat(bleskomat_id) diff --git a/lnbits/extensions/bleskomat/helpers.py b/lnbits/extensions/bleskomat/helpers.py index a3857b773..3d3550488 100644 --- a/lnbits/extensions/bleskomat/helpers.py +++ b/lnbits/extensions/bleskomat/helpers.py @@ -1,11 +1,12 @@ import base64 import hashlib import hmac -from http import HTTPStatus -from binascii import unhexlify -from typing import Dict -from quart import url_for import urllib +from binascii import unhexlify +from http import HTTPStatus +from typing import Dict + +from starlette.requests import Request def generate_bleskomat_lnurl_hash(secret: str): @@ -34,8 +35,8 @@ def generate_bleskomat_lnurl_secret(api_key_id: str, signature: str): return m.hexdigest() -def get_callback_url(): - return url_for("bleskomat.api_bleskomat_lnurl", _external=True) +def get_callback_url(request: Request): + return request.url_for("bleskomat.api_bleskomat_lnurl", _external=True) def is_supported_lnurl_subprotocol(tag: str) -> bool: diff --git a/lnbits/extensions/bleskomat/lnurl_api.py b/lnbits/extensions/bleskomat/lnurl_api.py index 086562d1c..fa3e6133d 100644 --- a/lnbits/extensions/bleskomat/lnurl_api.py +++ b/lnbits/extensions/bleskomat/lnurl_api.py @@ -1,8 +1,9 @@ import json import math -from quart import jsonify, request -from http import HTTPStatus import traceback +from http import HTTPStatus + +from starlette.requests import Request from . import bleskomat_ext from .crud import ( @@ -10,16 +11,12 @@ from .crud import ( get_bleskomat_by_api_key_id, get_bleskomat_lnurl, ) - -from .exchange_rates import ( - fetch_fiat_exchange_rate, -) - +from .exchange_rates import fetch_fiat_exchange_rate from .helpers import ( - generate_bleskomat_lnurl_signature, - generate_bleskomat_lnurl_secret, LnurlHttpError, LnurlValidationError, + generate_bleskomat_lnurl_secret, + generate_bleskomat_lnurl_signature, prepare_lnurl_params, query_to_signing_payload, unshorten_lnurl_query, @@ -27,10 +24,10 @@ from .helpers import ( # Handles signed URL from Bleskomat ATMs and "action" callback of auto-generated LNURLs. -@bleskomat_ext.route("/u", methods=["GET"]) -async def api_bleskomat_lnurl(): +@bleskomat_ext.get("/u", name="bleskomat.api_bleskomat_lnurl") +async def api_bleskomat_lnurl(request: Request): try: - query = request.args.to_dict() + query = request.query_params # Unshorten query if "s" is used instead of "signature". if "s" in query: @@ -99,7 +96,7 @@ async def api_bleskomat_lnurl(): ) # Reply with LNURL response object. - return jsonify(lnurl.get_info_response_object(secret)), HTTPStatus.OK + return lnurl.get_info_response_object(secret) # No signature provided. # Treat as "action" callback. @@ -123,12 +120,9 @@ async def api_bleskomat_lnurl(): raise LnurlHttpError(str(e), HTTPStatus.BAD_REQUEST) except LnurlHttpError as e: - return jsonify({"status": "ERROR", "reason": str(e)}), e.http_status + return {"status": "ERROR", "reason": str(e)} except Exception: traceback.print_exc() - return ( - jsonify({"status": "ERROR", "reason": "Unexpected error"}), - HTTPStatus.INTERNAL_SERVER_ERROR, - ) + return {"status": "ERROR", "reason": "Unexpected error"} - return jsonify({"status": "OK"}), HTTPStatus.OK + return {"status": "OK"} diff --git a/lnbits/extensions/bleskomat/models.py b/lnbits/extensions/bleskomat/models.py index 216f83c60..295e0865d 100644 --- a/lnbits/extensions/bleskomat/models.py +++ b/lnbits/extensions/bleskomat/models.py @@ -1,13 +1,44 @@ import json import time -from typing import NamedTuple, Dict +from typing import Dict + +from fastapi.params import Query +from pydantic import BaseModel, validator +from starlette.requests import Request + from lnbits import bolt11 from lnbits.core.services import pay_invoice + from . import db -from .helpers import get_callback_url, LnurlValidationError +from .exchange_rates import exchange_rate_providers, fiat_currencies +from .helpers import LnurlValidationError, get_callback_url -class Bleskomat(NamedTuple): +class CreateBleskomat(BaseModel): + name: str = Query(...) + fiat_currency: str = Query(...) + exchange_rate_provider: str = Query(...) + fee: str = Query(...) + + @validator('fiat_currency') + def allowed_fiat_currencies(cls, v): + if(v not in fiat_currencies.keys()): + raise ValueError('Not allowed currency') + return v + + @validator('exchange_rate_provider') + def allowed_providers(cls, v): + if(v not in exchange_rate_providers.keys()): + raise ValueError('Not allowed provider') + return v + + @validator('fee') + def fee_type(cls, v): + if(not isinstance(v, (str, float, int))): + raise ValueError('Fee type not allowed') + return v + +class Bleskomat(BaseModel): id: str wallet: str api_key_id: str @@ -19,7 +50,7 @@ class Bleskomat(NamedTuple): fee: str -class BleskomatLnurl(NamedTuple): +class BleskomatLnurl(BaseModel): id: str bleskomat: str wallet: str @@ -36,14 +67,14 @@ class BleskomatLnurl(NamedTuple): # When initial uses is 0 then the LNURL has unlimited uses. return self.initial_uses == 0 or self.remaining_uses > 0 - def get_info_response_object(self, secret: str) -> Dict[str, str]: + def get_info_response_object(self, secret: str, req: Request) -> Dict[str, str]: tag = self.tag params = json.loads(self.params) response = {"tag": tag} if tag == "withdrawRequest": for key in ["minWithdrawable", "maxWithdrawable", "defaultDescription"]: response[key] = params[key] - response["callback"] = get_callback_url() + response["callback"] = get_callback_url(req) response["k1"] = secret return response diff --git a/lnbits/extensions/bleskomat/static/js/index.js b/lnbits/extensions/bleskomat/static/js/index.js index fd166ff39..4e8f993fe 100644 --- a/lnbits/extensions/bleskomat/static/js/index.js +++ b/lnbits/extensions/bleskomat/static/js/index.js @@ -84,7 +84,7 @@ new Vue({ LNbits.api .request( 'GET', - '/bleskomat/api/v1/bleskomats?all_wallets', + '/bleskomat/api/v1/bleskomats?all_wallets=True', this.g.user.wallets[0].adminkey ) .then(function (response) { diff --git a/lnbits/extensions/bleskomat/views.py b/lnbits/extensions/bleskomat/views.py index 3a7f72637..7e58f284b 100644 --- a/lnbits/extensions/bleskomat/views.py +++ b/lnbits/extensions/bleskomat/views.py @@ -1,22 +1,27 @@ -from quart import g, render_template -from lnbits.decorators import check_user_exists, validate_uuids +from http import HTTPStatus -from . import bleskomat_ext +from fastapi import Request +from fastapi.params import Depends +from fastapi.templating import Jinja2Templates +from starlette.exceptions import HTTPException +from starlette.responses import HTMLResponse +from lnbits.core.models import User +from lnbits.decorators import check_user_exists + +from . import bleskomat_ext, bleskomat_renderer from .exchange_rates import exchange_rate_providers_serializable, fiat_currencies from .helpers import get_callback_url -@bleskomat_ext.route("/") -@validate_uuids(["usr"], required=True) -@check_user_exists() -async def index(): +@bleskomat_ext.get("/", response_class=HTMLResponse) +async def index(request: Request, user: User = Depends(check_user_exists)): bleskomat_vars = { - "callback_url": get_callback_url(), + "callback_url": get_callback_url(request), "exchange_rate_providers": exchange_rate_providers_serializable, "fiat_currencies": fiat_currencies, } - return await render_template( - "bleskomat/index.html", user=g.user, bleskomat_vars=bleskomat_vars + return bleskomat_renderer().TemplateResponse( + "bleskomat/index.html", {"request": request, "user": user.dict(), "bleskomat_vars": bleskomat_vars} ) diff --git a/lnbits/extensions/bleskomat/views_api.py b/lnbits/extensions/bleskomat/views_api.py index 2971b0669..53062d9af 100644 --- a/lnbits/extensions/bleskomat/views_api.py +++ b/lnbits/extensions/bleskomat/views_api.py @@ -1,120 +1,86 @@ -from quart import g, jsonify, request from http import HTTPStatus +from fastapi.params import Query +from starlette.exceptions import HTTPException + from lnbits.core.crud import get_user -from lnbits.decorators import api_check_wallet_key, api_validate_post_request +from lnbits.decorators import WalletTypeInfo, require_admin_key +from lnbits.extensions.bleskomat.models import CreateBleskomat from . import bleskomat_ext from .crud import ( create_bleskomat, + delete_bleskomat, get_bleskomat, get_bleskomats, update_bleskomat, - delete_bleskomat, -) - -from .exchange_rates import ( - exchange_rate_providers, - fetch_fiat_exchange_rate, - fiat_currencies, ) +from .exchange_rates import fetch_fiat_exchange_rate -@bleskomat_ext.route("/api/v1/bleskomats", methods=["GET"]) -@api_check_wallet_key("admin") -async def api_bleskomats(): - wallet_ids = [g.wallet.id] +@bleskomat_ext.get("/api/v1/bleskomats") +async def api_bleskomats(wallet: WalletTypeInfo(require_admin_key), all_wallets: bool = Query(False)): + wallet_ids = [wallet.wallet.id] - if "all_wallets" in request.args: - wallet_ids = (await get_user(g.wallet.user)).wallet_ids + if all_wallets: + wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids - return ( - jsonify( - [bleskomat._asdict() for bleskomat in await get_bleskomats(wallet_ids)] - ), - HTTPStatus.OK, - ) + return [bleskomat.dict() for bleskomat in await get_bleskomats(wallet_ids)] -@bleskomat_ext.route("/api/v1/bleskomat/", methods=["GET"]) -@api_check_wallet_key("admin") -async def api_bleskomat_retrieve(bleskomat_id): +@bleskomat_ext.get("/api/v1/bleskomat/{bleskomat_id}") +async def api_bleskomat_retrieve(wallet: WalletTypeInfo(require_admin_key), bleskomat_id): bleskomat = await get_bleskomat(bleskomat_id) - if not bleskomat or bleskomat.wallet != g.wallet.id: - return ( - jsonify({"message": "Bleskomat configuration not found."}), - HTTPStatus.NOT_FOUND, - ) + if not bleskomat or bleskomat.wallet != wallet.wallet.id: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail="Bleskomat configuration not found." + ) - return jsonify(bleskomat._asdict()), HTTPStatus.OK + return bleskomat.dict() -@bleskomat_ext.route("/api/v1/bleskomat", methods=["POST"]) -@bleskomat_ext.route("/api/v1/bleskomat/", methods=["PUT"]) -@api_check_wallet_key("admin") -@api_validate_post_request( - schema={ - "name": {"type": "string", "empty": False, "required": True}, - "fiat_currency": { - "type": "string", - "allowed": fiat_currencies.keys(), - "required": True, - }, - "exchange_rate_provider": { - "type": "string", - "allowed": exchange_rate_providers.keys(), - "required": True, - }, - "fee": {"type": ["string", "float", "number", "integer"], "required": True}, - } -) -async def api_bleskomat_create_or_update(bleskomat_id=None): +@bleskomat_ext.post("/api/v1/bleskomat") +@bleskomat_ext.put("/api/v1/bleskomat/{bleskomat_id}",) +async def api_bleskomat_create_or_update(data: CreateBleskomat, wallet: WalletTypeInfo(require_admin_key), bleskomat_id=None): try: - fiat_currency = g.data["fiat_currency"] - exchange_rate_provider = g.data["exchange_rate_provider"] + fiat_currency = data.fiat_currency + exchange_rate_provider = data.exchange_rate_provider await fetch_fiat_exchange_rate( currency=fiat_currency, provider=exchange_rate_provider ) except Exception as e: print(e) - return ( - jsonify( - { - "message": f'Failed to fetch BTC/{fiat_currency} currency pair from "{exchange_rate_provider}"' - } - ), - HTTPStatus.INTERNAL_SERVER_ERROR, + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, + detail=f'Failed to fetch BTC/{fiat_currency} currency pair from "{exchange_rate_provider}"' ) if bleskomat_id: bleskomat = await get_bleskomat(bleskomat_id) - if not bleskomat or bleskomat.wallet != g.wallet.id: - return ( - jsonify({"message": "Bleskomat configuration not found."}), - HTTPStatus.NOT_FOUND, + if not bleskomat or bleskomat.wallet != wallet.wallet.id: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail="Bleskomat configuration not found." ) - bleskomat = await update_bleskomat(bleskomat_id, **g.data) + + bleskomat = await update_bleskomat(bleskomat_id, **data.dict()) else: - bleskomat = await create_bleskomat(wallet_id=g.wallet.id, **g.data) + bleskomat = await create_bleskomat(wallet_id=wallet.wallet.id, data=data) - return ( - jsonify(bleskomat._asdict()), - HTTPStatus.OK if bleskomat_id else HTTPStatus.CREATED, - ) + return bleskomat.dict() -@bleskomat_ext.route("/api/v1/bleskomat/", methods=["DELETE"]) -@api_check_wallet_key("admin") -async def api_bleskomat_delete(bleskomat_id): +@bleskomat_ext.delete("/api/v1/bleskomat/{bleskomat_id}") +async def api_bleskomat_delete(wallet: WalletTypeInfo(require_admin_key), bleskomat_id): bleskomat = await get_bleskomat(bleskomat_id) - if not bleskomat or bleskomat.wallet != g.wallet.id: - return ( - jsonify({"message": "Bleskomat configuration not found."}), - HTTPStatus.NOT_FOUND, - ) + if not bleskomat or bleskomat.wallet != wallet.wallet.id: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail="Bleskomat configuration not found." + ) await delete_bleskomat(bleskomat_id) - - return "", HTTPStatus.NO_CONTENT + raise HTTPException(status_code=HTTPStatus.NO_CONTENT)