From a9dc087f618aac270ae36707d1524a8251c2fbbf Mon Sep 17 00:00:00 2001 From: Ben Arc Date: Sat, 21 Aug 2021 00:34:48 +0100 Subject: [PATCH] All extensions semi-switched --- lnbits/extensions/bleskomat/views.py | 2 +- lnbits/extensions/bleskomat/views_api.py | 52 +++----- lnbits/extensions/lnurlp/views_api.py | 82 ++++++------ lnbits/extensions/ngrok/views.py | 2 +- lnbits/extensions/offlineshop/lnurl.py | 24 ++-- lnbits/extensions/offlineshop/views.py | 6 +- lnbits/extensions/offlineshop/views_api.py | 80 +++++------ lnbits/extensions/paywall/views_api.py | 7 +- lnbits/extensions/splitpayments/views.py | 2 +- lnbits/extensions/splitpayments/views_api.py | 37 ++---- lnbits/extensions/streamalerts/views.py | 4 +- lnbits/extensions/streamalerts/views_api.py | 131 +++++++++---------- lnbits/extensions/subdomains/views.py | 4 +- lnbits/extensions/subdomains/views_api.py | 122 ++++++++--------- lnbits/extensions/usermanager/views.py | 2 +- lnbits/extensions/usermanager/views_api.py | 96 ++++++-------- lnbits/extensions/withdraw/views_api.py | 1 - 17 files changed, 282 insertions(+), 372 deletions(-) diff --git a/lnbits/extensions/bleskomat/views.py b/lnbits/extensions/bleskomat/views.py index 3a7f72637..1188f7c64 100644 --- a/lnbits/extensions/bleskomat/views.py +++ b/lnbits/extensions/bleskomat/views.py @@ -8,7 +8,7 @@ from .exchange_rates import exchange_rate_providers_serializable, fiat_currencie from .helpers import get_callback_url -@bleskomat_ext.route("/") +@bleskomat_ext.get("/") @validate_uuids(["usr"], required=True) @check_user_exists() async def index(): diff --git a/lnbits/extensions/bleskomat/views_api.py b/lnbits/extensions/bleskomat/views_api.py index 2971b0669..23d858683 100644 --- a/lnbits/extensions/bleskomat/views_api.py +++ b/lnbits/extensions/bleskomat/views_api.py @@ -20,7 +20,7 @@ from .exchange_rates import ( ) -@bleskomat_ext.route("/api/v1/bleskomats", methods=["GET"]) +@bleskomat_ext.get("/api/v1/bleskomats") @api_check_wallet_key("admin") async def api_bleskomats(): wallet_ids = [g.wallet.id] @@ -29,14 +29,12 @@ async def api_bleskomats(): wallet_ids = (await get_user(g.wallet.user)).wallet_ids return ( - jsonify( - [bleskomat._asdict() for bleskomat in await get_bleskomats(wallet_ids)] - ), + [bleskomat._asdict() for bleskomat in await get_bleskomats(wallet_ids)], HTTPStatus.OK, ) -@bleskomat_ext.route("/api/v1/bleskomat/", methods=["GET"]) +@bleskomat_ext.get("/api/v1/bleskomat/") @api_check_wallet_key("admin") async def api_bleskomat_retrieve(bleskomat_id): bleskomat = await get_bleskomat(bleskomat_id) @@ -50,40 +48,28 @@ async def api_bleskomat_retrieve(bleskomat_id): return jsonify(bleskomat._asdict()), HTTPStatus.OK -@bleskomat_ext.route("/api/v1/bleskomat", methods=["POST"]) -@bleskomat_ext.route("/api/v1/bleskomat/", methods=["PUT"]) +class CreateData(BaseModel): + name: str + fiat_currency: str = fiat_currencies.keys() + exchange_rate_provider: str = exchange_rate_providers.keys() + fee: Optional[str, int, float] = Query(...) + +@bleskomat_ext.post("/api/v1/bleskomat") +@bleskomat_ext.put("/api/v1/bleskomat/") @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): +async def api_bleskomat_create_or_update(data: CreateData, 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, ) @@ -94,17 +80,17 @@ async def api_bleskomat_create_or_update(bleskomat_id=None): jsonify({"message": "Bleskomat configuration not found."}), HTTPStatus.NOT_FOUND, ) - bleskomat = await update_bleskomat(bleskomat_id, **g.data) + bleskomat = await update_bleskomat(bleskomat_id, **data) else: - bleskomat = await create_bleskomat(wallet_id=g.wallet.id, **g.data) + bleskomat = await create_bleskomat(wallet_id=g.wallet.id, **data) return ( - jsonify(bleskomat._asdict()), + bleskomat._asdict(), HTTPStatus.OK if bleskomat_id else HTTPStatus.CREATED, ) -@bleskomat_ext.route("/api/v1/bleskomat/", methods=["DELETE"]) +@bleskomat_ext.delete("/api/v1/bleskomat/") @api_check_wallet_key("admin") async def api_bleskomat_delete(bleskomat_id): bleskomat = await get_bleskomat(bleskomat_id) diff --git a/lnbits/extensions/lnurlp/views_api.py b/lnbits/extensions/lnurlp/views_api.py index af670c839..3d2a4709b 100644 --- a/lnbits/extensions/lnurlp/views_api.py +++ b/lnbits/extensions/lnurlp/views_api.py @@ -15,13 +15,12 @@ from .crud import ( delete_pay_link, ) - -@lnurlp_ext.route("/api/v1/currencies", methods=["GET"]) +@lnurlp_ext.get("/api/v1/currencies") async def api_list_currencies_available(): return jsonify(list(currencies.keys())) -@lnurlp_ext.route("/api/v1/links", methods=["GET"]) +@lnurlp_ext.get("/api/v1/links") @api_check_wallet_key("invoice") async def api_links(): wallet_ids = [g.wallet.id] @@ -31,66 +30,59 @@ async def api_links(): try: return ( - jsonify( [ {**link._asdict(), **{"lnurl": link.lnurl}} for link in await get_pay_links(wallet_ids) - ] - ), + ], HTTPStatus.OK, ) except LnurlInvalidUrl: return ( - jsonify( { "message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor." - } - ), + }, HTTPStatus.UPGRADE_REQUIRED, ) -@lnurlp_ext.route("/api/v1/links/", methods=["GET"]) +@lnurlp_ext.get("/api/v1/links/") @api_check_wallet_key("invoice") async def api_link_retrieve(link_id): link = await get_pay_link(link_id) if not link: - return jsonify({"message": "Pay link does not exist."}), HTTPStatus.NOT_FOUND + return {"message": "Pay link does not exist."}, HTTPStatus.NOT_FOUND if link.wallet != g.wallet.id: - return jsonify({"message": "Not your pay link."}), HTTPStatus.FORBIDDEN + return {"message": "Not your pay link."}, HTTPStatus.FORBIDDEN - return jsonify({**link._asdict(), **{"lnurl": link.lnurl}}), HTTPStatus.OK + return {**link._asdict(), **{"lnurl": link.lnurl}}, HTTPStatus.OK +class CreateData(BaseModel): + description: str + min: int = Query(ge=0.01) + max: int = Query(ge=0.01) + currency: Optional[str] + comment_chars: int = Query(ge=0, lt=800) + webhook_url: Optional[str] + success_text: Optional[str] + success_url: Optional[str] -@lnurlp_ext.route("/api/v1/links", methods=["POST"]) -@lnurlp_ext.route("/api/v1/links/", methods=["PUT"]) +@lnurlp_ext.post("/api/v1/links") +@lnurlp_ext.put("/api/v1/links/") @api_check_wallet_key("invoice") -@api_validate_post_request( - schema={ - "description": {"type": "string", "empty": False, "required": True}, - "min": {"type": "number", "min": 0.01, "required": True}, - "max": {"type": "number", "min": 0.01, "required": True}, - "currency": {"type": "string", "nullable": True, "required": False}, - "comment_chars": {"type": "integer", "required": True, "min": 0, "max": 800}, - "webhook_url": {"type": "string", "required": False}, - "success_text": {"type": "string", "required": False}, - "success_url": {"type": "string", "required": False}, - } -) -async def api_link_create_or_update(link_id=None): - if g.data["min"] > g.data["max"]: - return jsonify({"message": "Min is greater than max."}), HTTPStatus.BAD_REQUEST +async def api_link_create_or_update(data: CreateData, link_id=None): + if data.min > data.max: + return {"message": "Min is greater than max."}, HTTPStatus.BAD_REQUEST - if g.data.get("currency") == None and ( - round(g.data["min"]) != g.data["min"] or round(g.data["max"]) != g.data["max"] + if data.currency == None and ( + round(data.min) != data.min or round(data.max) != data.max ): - return jsonify({"message": "Must use full satoshis."}), HTTPStatus.BAD_REQUEST + return {"message": "Must use full satoshis."}, HTTPStatus.BAD_REQUEST - if "success_url" in g.data and g.data["success_url"][:8] != "https://": + if "success_url" in data and data.success_url[:8] != "https://": return ( - jsonify({"message": "Success URL must be secure https://..."}), + {"message": "Success URL must be secure https://..."}, HTTPStatus.BAD_REQUEST, ) @@ -99,44 +91,44 @@ async def api_link_create_or_update(link_id=None): if not link: return ( - jsonify({"message": "Pay link does not exist."}), + {"message": "Pay link does not exist."}, HTTPStatus.NOT_FOUND, ) if link.wallet != g.wallet.id: - return jsonify({"message": "Not your pay link."}), HTTPStatus.FORBIDDEN + return {"message": "Not your pay link."}, HTTPStatus.FORBIDDEN - link = await update_pay_link(link_id, **g.data) + link = await update_pay_link(link_id, **data) else: - link = await create_pay_link(wallet_id=g.wallet.id, **g.data) + link = await create_pay_link(wallet_id=g.wallet.id, **data) return ( - jsonify({**link._asdict(), **{"lnurl": link.lnurl}}), + {**link._asdict(), **{"lnurl": link.lnurl}}, HTTPStatus.OK if link_id else HTTPStatus.CREATED, ) -@lnurlp_ext.route("/api/v1/links/", methods=["DELETE"]) +@lnurlp_ext.delete("/api/v1/links/") @api_check_wallet_key("invoice") async def api_link_delete(link_id): link = await get_pay_link(link_id) if not link: - return jsonify({"message": "Pay link does not exist."}), HTTPStatus.NOT_FOUND + return {"message": "Pay link does not exist."}, HTTPStatus.NOT_FOUND if link.wallet != g.wallet.id: - return jsonify({"message": "Not your pay link."}), HTTPStatus.FORBIDDEN + return {"message": "Not your pay link."}, HTTPStatus.FORBIDDEN await delete_pay_link(link_id) return "", HTTPStatus.NO_CONTENT -@lnurlp_ext.route("/api/v1/rate/", methods=["GET"]) +@lnurlp_ext.get("/api/v1/rate/") async def api_check_fiat_rate(currency): try: rate = await get_fiat_rate_satoshis(currency) except AssertionError: rate = None - return jsonify({"rate": rate}), HTTPStatus.OK + return {"rate": rate}, HTTPStatus.OK diff --git a/lnbits/extensions/ngrok/views.py b/lnbits/extensions/ngrok/views.py index 732ad7ed1..7afc4f894 100644 --- a/lnbits/extensions/ngrok/views.py +++ b/lnbits/extensions/ngrok/views.py @@ -23,7 +23,7 @@ port = getenv("PORT") ngrok_tunnel = ngrok.connect(port) -@ngrok_ext.route("/") +@ngrok_ext.get("/") @validate_uuids(["usr"], required=True) @check_user_exists() async def index(): diff --git a/lnbits/extensions/offlineshop/lnurl.py b/lnbits/extensions/offlineshop/lnurl.py index d99e4ceaa..ff8b71dcc 100644 --- a/lnbits/extensions/offlineshop/lnurl.py +++ b/lnbits/extensions/offlineshop/lnurl.py @@ -9,14 +9,14 @@ from . import offlineshop_ext from .crud import get_shop, get_item -@offlineshop_ext.route("/lnurl/", methods=["GET"]) +@offlineshop_ext.get("/lnurl/") async def lnurl_response(item_id): item = await get_item(item_id) if not item: - return jsonify({"status": "ERROR", "reason": "Item not found."}) + return {"status": "ERROR", "reason": "Item not found."} if not item.enabled: - return jsonify({"status": "ERROR", "reason": "Item disabled."}) + return {"status": "ERROR", "reason": "Item disabled."} price_msat = ( await fiat_amount_as_satoshis(item.price, item.unit) @@ -31,14 +31,14 @@ async def lnurl_response(item_id): metadata=await item.lnurlpay_metadata(), ) - return jsonify(resp.dict()) + return resp.dict() -@offlineshop_ext.route("/lnurl/cb/", methods=["GET"]) +@offlineshop_ext.get("/lnurl/cb/") async def lnurl_callback(item_id): item = await get_item(item_id) if not item: - return jsonify({"status": "ERROR", "reason": "Couldn't find item."}) + return {"status": "ERROR", "reason": "Couldn't find item."} if item.unit == "sat": min = item.price * 1000 @@ -51,17 +51,13 @@ async def lnurl_callback(item_id): amount_received = int(request.args.get("amount") or 0) if amount_received < min: - return jsonify( - LnurlErrorResponse( + return LnurlErrorResponse( reason=f"Amount {amount_received} is smaller than minimum {min}." ).dict() - ) elif amount_received > max: - return jsonify( - LnurlErrorResponse( + return LnurlErrorResponse( reason=f"Amount {amount_received} is greater than maximum {max}." ).dict() - ) shop = await get_shop(item.shop) @@ -76,7 +72,7 @@ async def lnurl_callback(item_id): extra={"tag": "offlineshop", "item": item.id}, ) except Exception as exc: - return jsonify(LnurlErrorResponse(reason=exc.message).dict()) + return LnurlErrorResponse(reason=exc.message).dict() resp = LnurlPayActionResponse( pr=payment_request, @@ -84,4 +80,4 @@ async def lnurl_callback(item_id): routes=[], ) - return jsonify(resp.dict()) + return resp.dict() diff --git a/lnbits/extensions/offlineshop/views.py b/lnbits/extensions/offlineshop/views.py index 33702f6ba..4fcb90ce0 100644 --- a/lnbits/extensions/offlineshop/views.py +++ b/lnbits/extensions/offlineshop/views.py @@ -11,14 +11,14 @@ from . import offlineshop_ext from .crud import get_item, get_shop -@offlineshop_ext.route("/") +@offlineshop_ext.get("/") @validate_uuids(["usr"], required=True) @check_user_exists() async def index(): return await render_template("offlineshop/index.html", user=g.user) -@offlineshop_ext.route("/print") +@offlineshop_ext.get("/print") async def print_qr_codes(): items = [] for item_id in request.args.get("items").split(","): @@ -35,7 +35,7 @@ async def print_qr_codes(): return await render_template("offlineshop/print.html", items=items) -@offlineshop_ext.route("/confirmation") +@offlineshop_ext.get("/confirmation") async def confirmation_code(): style = "" diff --git a/lnbits/extensions/offlineshop/views_api.py b/lnbits/extensions/offlineshop/views_api.py index ee3631a77..5f8f4b424 100644 --- a/lnbits/extensions/offlineshop/views_api.py +++ b/lnbits/extensions/offlineshop/views_api.py @@ -17,12 +17,12 @@ from .crud import ( from .models import ShopCounter -@offlineshop_ext.route("/api/v1/currencies", methods=["GET"]) +@offlineshop_ext.get("/api/v1/currencies") async def api_list_currencies_available(): return jsonify(list(currencies.keys())) -@offlineshop_ext.route("/api/v1/offlineshop", methods=["GET"]) +@offlineshop_ext.get("/api/v1/offlineshop") @api_check_wallet_key("invoice") async def api_shop_from_wallet(): shop = await get_or_create_shop_by_wallet(g.wallet.id) @@ -30,66 +30,59 @@ async def api_shop_from_wallet(): try: return ( - jsonify( { **shop._asdict(), **{ "otp_key": shop.otp_key, "items": [item.values() for item in items], }, - } - ), + }, HTTPStatus.OK, ) except LnurlInvalidUrl: return ( - jsonify( { "message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor." - } - ), + }, HTTPStatus.UPGRADE_REQUIRED, ) +class CreateItemsData(BaseModel): + name: str + description: str + image: Optional[str] + price: int + unit: str -@offlineshop_ext.route("/api/v1/offlineshop/items", methods=["POST"]) -@offlineshop_ext.route("/api/v1/offlineshop/items/", methods=["PUT"]) +@offlineshop_ext.post("/api/v1/offlineshop/items") +@offlineshop_ext.put("/api/v1/offlineshop/items/") @api_check_wallet_key("invoice") -@api_validate_post_request( - schema={ - "name": {"type": "string", "empty": False, "required": True}, - "description": {"type": "string", "empty": False, "required": True}, - "image": {"type": "string", "required": False, "nullable": True}, - "price": {"type": "number", "required": True}, - "unit": {"type": "string", "required": True}, - } -) -async def api_add_or_update_item(item_id=None): +async def api_add_or_update_item(data: CreateItemsData, item_id=None): shop = await get_or_create_shop_by_wallet(g.wallet.id) if item_id == None: await add_item( shop.id, - g.data["name"], - g.data["description"], - g.data.get("image"), - g.data["price"], - g.data["unit"], + data.name, + data.description, + data.image, + data.price, + data.unit, ) return "", HTTPStatus.CREATED else: await update_item( shop.id, item_id, - g.data["name"], - g.data["description"], - g.data.get("image"), - g.data["price"], - g.data["unit"], + data.name, + data.description, + data.image, + data.price, + data.unit, ) return "", HTTPStatus.OK -@offlineshop_ext.route("/api/v1/offlineshop/items/", methods=["DELETE"]) +@offlineshop_ext.delete("/api/v1/offlineshop/items/") @api_check_wallet_key("invoice") async def api_delete_item(item_id): shop = await get_or_create_shop_by_wallet(g.wallet.id) @@ -97,23 +90,16 @@ async def api_delete_item(item_id): return "", HTTPStatus.NO_CONTENT -@offlineshop_ext.route("/api/v1/offlineshop/method", methods=["PUT"]) -@api_check_wallet_key("invoice") -@api_validate_post_request( - schema={ - "method": {"type": "string", "required": True, "nullable": False}, - "wordlist": { - "type": "string", - "empty": True, - "nullable": True, - "required": False, - }, - } -) -async def api_set_method(): - method = g.data["method"] +class CreateMethodData(BaseModel): + method: str + wordlist: Optional[str] - wordlist = g.data["wordlist"].split("\n") if g.data["wordlist"] else None +@offlineshop_ext.put("/api/v1/offlineshop/method") +@api_check_wallet_key("invoice") +async def api_set_method(data: CreateMethodData): + method = data.method + + wordlist = data.wordlist.split("\n") if data.wordlist else None wordlist = [word.strip() for word in wordlist if word.strip()] shop = await get_or_create_shop_by_wallet(g.wallet.id) diff --git a/lnbits/extensions/paywall/views_api.py b/lnbits/extensions/paywall/views_api.py index f036c2079..941038995 100644 --- a/lnbits/extensions/paywall/views_api.py +++ b/lnbits/extensions/paywall/views_api.py @@ -25,13 +25,12 @@ async def api_paywalls(): HTTPStatus.OK, ) - class CreateData(BaseModel): url: Optional[str] = Query(...) memo: Optional[str] = Query(...) - description: str = Query(None) - amount: int = Query(None) - remembers: bool = Query(None) + description: str + amount: int + remembers: bool @paywall_ext.post("/api/v1/paywalls") @api_check_wallet_key("invoice") diff --git a/lnbits/extensions/splitpayments/views.py b/lnbits/extensions/splitpayments/views.py index acded737e..63e96c9e0 100644 --- a/lnbits/extensions/splitpayments/views.py +++ b/lnbits/extensions/splitpayments/views.py @@ -5,7 +5,7 @@ from lnbits.decorators import check_user_exists, validate_uuids from . import splitpayments_ext -@splitpayments_ext.route("/") +@splitpayments_ext.get("/") @validate_uuids(["usr"], required=True) @check_user_exists() async def index(): diff --git a/lnbits/extensions/splitpayments/views_api.py b/lnbits/extensions/splitpayments/views_api.py index e0fe475ed..ac7c1a398 100644 --- a/lnbits/extensions/splitpayments/views_api.py +++ b/lnbits/extensions/splitpayments/views_api.py @@ -9,52 +9,41 @@ from .crud import get_targets, set_targets from .models import Target -@splitpayments_ext.route("/api/v1/targets", methods=["GET"]) +@splitpayments_ext.get("/api/v1/targets") @api_check_wallet_key("admin") async def api_targets_get(): targets = await get_targets(g.wallet.id) - return jsonify([target._asdict() for target in targets] or []) + return [target._asdict() for target in targets] or [] +class SchemaData(BaseModel): + wallet: str + alias: str + percent: int -@splitpayments_ext.route("/api/v1/targets", methods=["PUT"]) +@splitpayments_ext.put("/api/v1/targets") @api_check_wallet_key("admin") -@api_validate_post_request( - schema={ - "targets": { - "type": "list", - "schema": { - "type": "dict", - "schema": { - "wallet": {"type": "string"}, - "alias": {"type": "string"}, - "percent": {"type": "integer"}, - }, - }, - } - } -) -async def api_targets_set(): +async def api_targets_set(targets: Optional(list[SchemaData]) = None): targets = [] - for entry in g.data["targets"]: + for entry in targets: wallet = await get_wallet(entry["wallet"]) if not wallet: wallet = await get_wallet_for_key(entry["wallet"], "invoice") if not wallet: return ( - jsonify({"message": f"Invalid wallet '{entry['wallet']}'."}), + {"message": f"Invalid wallet '{entry['wallet']}'."}, HTTPStatus.BAD_REQUEST, ) if wallet.id == g.wallet.id: return ( - jsonify({"message": "Can't split to itself."}), + {"message": "Can't split to itself."}, HTTPStatus.BAD_REQUEST, ) if entry["percent"] < 0: return ( - jsonify({"message": f"Invalid percent '{entry['percent']}'."}), + {"message": f"Invalid percent '{entry['percent']}'."}, HTTPStatus.BAD_REQUEST, ) @@ -64,7 +53,7 @@ async def api_targets_set(): percent_sum = sum([target.percent for target in targets]) if percent_sum > 100: - return jsonify({"message": "Splitting over 100%."}), HTTPStatus.BAD_REQUEST + return {"message": "Splitting over 100%."}, HTTPStatus.BAD_REQUEST await set_targets(g.wallet.id, targets) return "", HTTPStatus.OK diff --git a/lnbits/extensions/streamalerts/views.py b/lnbits/extensions/streamalerts/views.py index 3e9e771d4..fa16d85e4 100644 --- a/lnbits/extensions/streamalerts/views.py +++ b/lnbits/extensions/streamalerts/views.py @@ -7,7 +7,7 @@ from . import streamalerts_ext from .crud import get_service -@streamalerts_ext.route("/") +@streamalerts_ext.get("/") @validate_uuids(["usr"], required=True) @check_user_exists() async def index(): @@ -15,7 +15,7 @@ async def index(): return await render_template("streamalerts/index.html", user=g.user) -@streamalerts_ext.route("/") +@streamalerts_ext.get("/") async def donation(state): """Return the donation form for the Service corresponding to state""" service = await get_service(0, by_state=state) diff --git a/lnbits/extensions/streamalerts/views_api.py b/lnbits/extensions/streamalerts/views_api.py index b914753c0..7da56e3a4 100644 --- a/lnbits/extensions/streamalerts/views_api.py +++ b/lnbits/extensions/streamalerts/views_api.py @@ -24,30 +24,27 @@ from .crud import ( ) from ..satspay.crud import create_charge, get_charge +class CreateServicesData(BaseModel): + twitchuser: str + client_id: str + client_secret: str + wallet: str + servicename: str + onchain: Optional[str] -@streamalerts_ext.route("/api/v1/services", methods=["POST"]) +@streamalerts_ext.post("/api/v1/services") @api_check_wallet_key("invoice") -@api_validate_post_request( - schema={ - "twitchuser": {"type": "string", "required": True}, - "client_id": {"type": "string", "required": True}, - "client_secret": {"type": "string", "required": True}, - "wallet": {"type": "string", "required": True}, - "servicename": {"type": "string", "required": True}, - "onchain": {"type": "string"}, - } -) -async def api_create_service(): +async def api_create_service(data: CreateData): """Create a service, which holds data about how/where to post donations""" try: - service = await create_service(**g.data) + service = await create_service(**data) except Exception as e: - return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR + return {"message": str(e)}, HTTPStatus.INTERNAL_SERVER_ERROR - return jsonify(service._asdict()), HTTPStatus.CREATED + return service._asdict(), HTTPStatus.CREATED -@streamalerts_ext.route("/api/v1/getaccess/", methods=["GET"]) +@streamalerts_ext.get("/api/v1/getaccess/") async def api_get_access(service_id): """Redirect to Streamlabs' Approve/Decline page for API access for Service with service_id @@ -67,56 +64,52 @@ async def api_get_access(service_id): redirect_url = endpoint_url + querystring return redirect(redirect_url) else: - return (jsonify({"message": "Service does not exist!"}), HTTPStatus.BAD_REQUEST) + return ({"message": "Service does not exist!"}, HTTPStatus.BAD_REQUEST) -@streamalerts_ext.route("/api/v1/authenticate/", methods=["GET"]) -async def api_authenticate_service(service_id): +@streamalerts_ext.get("/api/v1/authenticate/") +async def api_authenticate_service(Code: str, State: str, service_id): """Endpoint visited via redirect during third party API authentication If successful, an API access token will be added to the service, and the user will be redirected to index.html. """ - code = request.args.get("code") - state = request.args.get("state") + code = Code + state = State service = await get_service(service_id) if service.state != state: - return (jsonify({"message": "State doesn't match!"}), HTTPStatus.BAD_Request) + return ({"message": "State doesn't match!"}, HTTPStatus.BAD_Request) redirect_uri = request.scheme + "://" + request.headers["Host"] redirect_uri += f"/streamalerts/api/v1/authenticate/{service_id}" url, success = await authenticate_service(service_id, code, redirect_uri) if success: return redirect(url) else: - return ( - jsonify({"message": "Service already authenticated!"}), + return ({"message": "Service already authenticated!"}, HTTPStatus.BAD_REQUEST, ) +class CreateDonationsData(BaseModel): + name: str + sats: int + service: int + message: str -@streamalerts_ext.route("/api/v1/donations", methods=["POST"]) -@api_validate_post_request( - schema={ - "name": {"type": "string"}, - "sats": {"type": "integer", "required": True}, - "service": {"type": "integer", "required": True}, - "message": {"type": "string"}, - } -) -async def api_create_donation(): +@streamalerts_ext.post("/api/v1/donations") +async def api_create_donation(data:CreateDonationsData): """Take data from donation form and return satspay charge""" # Currency is hardcoded while frotnend is limited cur_code = "USD" - sats = g.data["sats"] - message = g.data.get("message", "") + sats = data.sats + message = data.message # Fiat amount is calculated here while frontend is limited price = await btc_price(cur_code) amount = sats * (10 ** (-8)) * price webhook_base = request.scheme + "://" + request.headers["Host"] - service_id = g.data["service"] + service_id = data.service service = await get_service(service_id) charge_details = await get_charge_details(service.id) - name = g.data.get("name", "") + name = data.name if not name: name = "Anonymous" description = f"{sats} sats donation from {name} to {service.twitchuser}" @@ -134,33 +127,29 @@ async def api_create_donation(): message=message, name=name, cur_code=cur_code, - sats=g.data["sats"], + sats=data.sats, amount=amount, - service=g.data["service"], + service=data.service, ) - return (jsonify({"redirect_url": f"/satspay/{charge.id}"}), HTTPStatus.OK) + return ({"redirect_url": f"/satspay/{charge.id}"}), HTTPStatus.OK -@streamalerts_ext.route("/api/v1/postdonation", methods=["POST"]) -@api_validate_post_request( - schema={ - "id": {"type": "string", "required": True}, - } -) -async def api_post_donation(): +@streamalerts_ext.post("/api/v1/postdonation") + +async def api_post_donation(id: str): """Post a paid donation to Stremalabs/StreamElements. This endpoint acts as a webhook for the SatsPayServer extension.""" data = await request.get_json(force=True) - donation_id = data.get("id", "No ID") + donation_id = id charge = await get_charge(donation_id) if charge and charge.paid: return await post_donation(donation_id) else: - return (jsonify({"message": "Not a paid charge!"}), HTTPStatus.BAD_REQUEST) + return ({"message": "Not a paid charge!"}, HTTPStatus.BAD_REQUEST) -@streamalerts_ext.route("/api/v1/services", methods=["GET"]) +@streamalerts_ext.get("/api/v1/services") @api_check_wallet_key("invoice") async def api_get_services(): """Return list of all services assigned to wallet with given invoice key""" @@ -170,12 +159,12 @@ async def api_get_services(): new_services = await get_services(wallet_id) services += new_services if new_services else [] return ( - jsonify([service._asdict() for service in services] if services else []), + [service._asdict() for service in services] if services else [], HTTPStatus.OK, ) -@streamalerts_ext.route("/api/v1/donations", methods=["GET"]) +@streamalerts_ext.get("/api/v1/donations") @api_check_wallet_key("invoice") async def api_get_donations(): """Return list of all donations assigned to wallet with given invoice @@ -187,12 +176,12 @@ async def api_get_donations(): new_donations = await get_donations(wallet_id) donations += new_donations if new_donations else [] return ( - jsonify([donation._asdict() for donation in donations] if donations else []), + [donation._asdict() for donation in donations] if donations else [], HTTPStatus.OK, ) -@streamalerts_ext.route("/api/v1/donations/", methods=["PUT"]) +@streamalerts_ext.put("/api/v1/donations/") @api_check_wallet_key("invoice") async def api_update_donation(donation_id=None): """Update a donation with the data given in the request""" @@ -201,23 +190,23 @@ async def api_update_donation(donation_id=None): if not donation: return ( - jsonify({"message": "Donation does not exist."}), + {"message": "Donation does not exist."}, HTTPStatus.NOT_FOUND, ) if donation.wallet != g.wallet.id: - return (jsonify({"message": "Not your donation."}), HTTPStatus.FORBIDDEN) + return ({"message": "Not your donation."}, HTTPStatus.FORBIDDEN) donation = await update_donation(donation_id, **g.data) else: return ( - jsonify({"message": "No donation ID specified"}), + {"message": "No donation ID specified"}, HTTPStatus.BAD_REQUEST, ) - return jsonify(donation._asdict()), HTTPStatus.CREATED + return donation._asdict(), HTTPStatus.CREATED -@streamalerts_ext.route("/api/v1/services/", methods=["PUT"]) +@streamalerts_ext.put("/api/v1/services/") @api_check_wallet_key("invoice") async def api_update_service(service_id=None): """Update a service with the data given in the request""" @@ -225,30 +214,28 @@ async def api_update_service(service_id=None): service = await get_service(service_id) if not service: - return ( - jsonify({"message": "Service does not exist."}), + return ({"message": "Service does not exist."}, HTTPStatus.NOT_FOUND, ) if service.wallet != g.wallet.id: - return (jsonify({"message": "Not your service."}), HTTPStatus.FORBIDDEN) + return ({"message": "Not your service."}), HTTPStatus.FORBIDDEN service = await update_service(service_id, **g.data) else: - return (jsonify({"message": "No service ID specified"}), HTTPStatus.BAD_REQUEST) - return jsonify(service._asdict()), HTTPStatus.CREATED + return ({"message": "No service ID specified"}), HTTPStatus.BAD_REQUEST + return service._asdict(), HTTPStatus.CREATED -@streamalerts_ext.route("/api/v1/donations/", methods=["DELETE"]) +@streamalerts_ext.delete("/api/v1/donations/") @api_check_wallet_key("invoice") async def api_delete_donation(donation_id): """Delete the donation with the given donation_id""" donation = await get_donation(donation_id) if not donation: - return (jsonify({"message": "No donation with this ID!"}), HTTPStatus.NOT_FOUND) + return ({"message": "No donation with this ID!"}, HTTPStatus.NOT_FOUND) if donation.wallet != g.wallet.id: - return ( - jsonify({"message": "Not authorized to delete this donation!"}), + return ({"message": "Not authorized to delete this donation!"}, HTTPStatus.FORBIDDEN, ) await delete_donation(donation_id) @@ -256,16 +243,16 @@ async def api_delete_donation(donation_id): return "", HTTPStatus.NO_CONTENT -@streamalerts_ext.route("/api/v1/services/", methods=["DELETE"]) +@streamalerts_ext.delete("/api/v1/services/") @api_check_wallet_key("invoice") async def api_delete_service(service_id): """Delete the service with the given service_id""" service = await get_service(service_id) if not service: - return (jsonify({"message": "No service with this ID!"}), HTTPStatus.NOT_FOUND) + return ({"message": "No service with this ID!"}, HTTPStatus.NOT_FOUND) if service.wallet != g.wallet.id: return ( - jsonify({"message": "Not authorized to delete this service!"}), + {"message": "Not authorized to delete this service!"}, HTTPStatus.FORBIDDEN, ) await delete_service(service_id) diff --git a/lnbits/extensions/subdomains/views.py b/lnbits/extensions/subdomains/views.py index 14e4853fe..a9c3cc612 100644 --- a/lnbits/extensions/subdomains/views.py +++ b/lnbits/extensions/subdomains/views.py @@ -7,14 +7,14 @@ from . import subdomains_ext from .crud import get_domain -@subdomains_ext.route("/") +@subdomains_ext.get("/") @validate_uuids(["usr"], required=True) @check_user_exists() async def index(): return await render_template("subdomains/index.html", user=g.user) -@subdomains_ext.route("/") +@subdomains_ext.get("/") async def display(domain_id): domain = await get_domain(domain_id) if not domain: diff --git a/lnbits/extensions/subdomains/views_api.py b/lnbits/extensions/subdomains/views_api.py index c11cd4bea..f70f27e50 100644 --- a/lnbits/extensions/subdomains/views_api.py +++ b/lnbits/extensions/subdomains/views_api.py @@ -24,7 +24,7 @@ from .cloudflare import cloudflare_create_subdomain, cloudflare_deletesubdomain # domainS -@subdomains_ext.route("/api/v1/domains", methods=["GET"]) +@subdomains_ext.get("/api/v1/domains") @api_check_wallet_key("invoice") async def api_domains(): wallet_ids = [g.wallet.id] @@ -33,52 +33,49 @@ async def api_domains(): wallet_ids = (await get_user(g.wallet.user)).wallet_ids return ( - jsonify([domain._asdict() for domain in await get_domains(wallet_ids)]), + [domain._asdict() for domain in await get_domains(wallet_ids)], HTTPStatus.OK, ) +class CreateDomainsData(BaseModel): + wallet: str + domain: str + cf_token: str + cf_zone_id: str + webhook: optional[str] + description: str + cost: int + allowed_record_types: str -@subdomains_ext.route("/api/v1/domains", methods=["POST"]) -@subdomains_ext.route("/api/v1/domains/", methods=["PUT"]) +@subdomains_ext.post("/api/v1/domains") +@subdomains_ext.put("/api/v1/domains/") @api_check_wallet_key("invoice") -@api_validate_post_request( - schema={ - "wallet": {"type": "string", "empty": False, "required": True}, - "domain": {"type": "string", "empty": False, "required": True}, - "cf_token": {"type": "string", "empty": False, "required": True}, - "cf_zone_id": {"type": "string", "empty": False, "required": True}, - "webhook": {"type": "string", "empty": False, "required": False}, - "description": {"type": "string", "min": 0, "required": True}, - "cost": {"type": "integer", "min": 0, "required": True}, - "allowed_record_types": {"type": "string", "required": True}, - } -) -async def api_domain_create(domain_id=None): +async def api_domain_create(data: CreateDomainsData, domain_id=None): if domain_id: domain = await get_domain(domain_id) if not domain: - return jsonify({"message": "domain does not exist."}), HTTPStatus.NOT_FOUND + return {"message": "domain does not exist."}, HTTPStatus.NOT_FOUND if domain.wallet != g.wallet.id: - return jsonify({"message": "Not your domain."}), HTTPStatus.FORBIDDEN + return {"message": "Not your domain."}, HTTPStatus.FORBIDDEN - domain = await update_domain(domain_id, **g.data) + domain = await update_domain(domain_id, **data) else: - domain = await create_domain(**g.data) + domain = await create_domain(**data) return jsonify(domain._asdict()), HTTPStatus.CREATED -@subdomains_ext.route("/api/v1/domains/", methods=["DELETE"]) +@subdomains_ext.delete("/api/v1/domains/") @api_check_wallet_key("invoice") async def api_domain_delete(domain_id): domain = await get_domain(domain_id) if not domain: - return jsonify({"message": "domain does not exist."}), HTTPStatus.NOT_FOUND + return {"message": "domain does not exist."}, HTTPStatus.NOT_FOUND if domain.wallet != g.wallet.id: - return jsonify({"message": "Not your domain."}), HTTPStatus.FORBIDDEN + return {"message": "Not your domain."}, HTTPStatus.FORBIDDEN await delete_domain(domain_id) @@ -88,7 +85,7 @@ async def api_domain_delete(domain_id): #########subdomains########## -@subdomains_ext.route("/api/v1/subdomains", methods=["GET"]) +@subdomains_ext.get("/api/v1/subdomains") @api_check_wallet_key("invoice") async def api_subdomains(): wallet_ids = [g.wallet.id] @@ -97,24 +94,22 @@ async def api_subdomains(): wallet_ids = (await get_user(g.wallet.user)).wallet_ids return ( - jsonify([domain._asdict() for domain in await get_subdomains(wallet_ids)]), + [domain._asdict() for domain in await get_subdomains(wallet_ids)], HTTPStatus.OK, ) +class CreateDomainsData(BaseModel): + domain: str + subdomain: str + email: str + ip: str + sats: int = Query(ge=0) + duration: int + record_type: str -@subdomains_ext.route("/api/v1/subdomains/", methods=["POST"]) -@api_validate_post_request( - schema={ - "domain": {"type": "string", "empty": False, "required": True}, - "subdomain": {"type": "string", "empty": False, "required": True}, - "email": {"type": "string", "empty": True, "required": True}, - "ip": {"type": "string", "empty": False, "required": True}, - "sats": {"type": "integer", "min": 0, "required": True}, - "duration": {"type": "integer", "empty": False, "required": True}, - "record_type": {"type": "string", "empty": False, "required": True}, - } -) -async def api_subdomain_make_subdomain(domain_id): +@subdomains_ext.post("/api/v1/subdomains/") + +async def api_subdomain_make_subdomain(data: CreateDomainsData, domain_id): domain = await get_domain(domain_id) # If the request is coming for the non-existant domain @@ -122,100 +117,95 @@ async def api_subdomain_make_subdomain(domain_id): return jsonify({"message": "LNsubdomain does not exist."}), HTTPStatus.NOT_FOUND ## If record_type is not one of the allowed ones reject the request - if g.data["record_type"] not in domain.allowed_record_types: - return ( - jsonify({"message": g.data["record_type"] + "Not a valid record"}), + if data.record_type not in domain.allowed_record_types: + return ({"message": data.record_type + "Not a valid record"}, HTTPStatus.BAD_REQUEST, ) ## If domain already exist in our database reject it - if await get_subdomainBySubdomain(g.data["subdomain"]) is not None: + if await get_subdomainBySubdomain(data.subdomain) is not None: return ( - jsonify( { - "message": g.data["subdomain"] + "message": data.subdomain + "." + domain.domain + " domain already taken" - } - ), + }, HTTPStatus.BAD_REQUEST, ) ## Dry run cloudflare... (create and if create is sucessful delete it) cf_response = await cloudflare_create_subdomain( domain=domain, - subdomain=g.data["subdomain"], - record_type=g.data["record_type"], - ip=g.data["ip"], + subdomain=data.subdomain, + record_type=data.record_type, + ip=data.ip, ) if cf_response["success"] == True: cloudflare_deletesubdomain(domain=domain, domain_id=cf_response["result"]["id"]) else: return ( - jsonify( { "message": "Problem with cloudflare: " + cf_response["errors"][0]["message"] - } - ), + }, HTTPStatus.BAD_REQUEST, ) ## ALL OK - create an invoice and return it to the user - sats = g.data["sats"] + sats = data.sats try: payment_hash, payment_request = await create_invoice( wallet_id=domain.wallet, amount=sats, - memo=f"subdomain {g.data['subdomain']}.{domain.domain} for {sats} sats for {g.data['duration']} days", + memo=f"subdomain {data.subdomain}.{domain.domain} for {sats} sats for {data.duration} days", extra={"tag": "lnsubdomain"}, ) except Exception as e: - return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR + return {"message": str(e)}, HTTPStatus.INTERNAL_SERVER_ERROR subdomain = await create_subdomain( - payment_hash=payment_hash, wallet=domain.wallet, **g.data + payment_hash=payment_hash, wallet=domain.wallet, **data ) if not subdomain: return ( - jsonify({"message": "LNsubdomain could not be fetched."}), + {"message": "LNsubdomain could not be fetched."}, HTTPStatus.NOT_FOUND, ) return ( - jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), + {"payment_hash": payment_hash, "payment_request": payment_request}, HTTPStatus.OK, ) -@subdomains_ext.route("/api/v1/subdomains/", methods=["GET"]) +@subdomains_ext.get("/api/v1/subdomains/") async def api_subdomain_send_subdomain(payment_hash): subdomain = await get_subdomain(payment_hash) try: status = await check_invoice_status(subdomain.wallet, payment_hash) is_paid = not status.pending except Exception: - return jsonify({"paid": False}), HTTPStatus.OK + return {"paid": False}, HTTPStatus.OK if is_paid: - return jsonify({"paid": True}), HTTPStatus.OK + return {"paid": True}, HTTPStatus.OK - return jsonify({"paid": False}), HTTPStatus.OK + return {"paid": False}, HTTPStatus.OK -@subdomains_ext.route("/api/v1/subdomains/", methods=["DELETE"]) +@subdomains_ext.delete("/api/v1/subdomains/") @api_check_wallet_key("invoice") async def api_subdomain_delete(subdomain_id): subdomain = await get_subdomain(subdomain_id) if not subdomain: - return jsonify({"message": "Paywall does not exist."}), HTTPStatus.NOT_FOUND + return {"message": "Paywall does not exist."}, HTTPStatus.NOT_FOUND if subdomain.wallet != g.wallet.id: - return jsonify({"message": "Not your subdomain."}), HTTPStatus.FORBIDDEN + return {"message": "Not your subdomain."}, HTTPStatus.FORBIDDEN await delete_subdomain(subdomain_id) diff --git a/lnbits/extensions/usermanager/views.py b/lnbits/extensions/usermanager/views.py index df6949c64..c5c62ab34 100644 --- a/lnbits/extensions/usermanager/views.py +++ b/lnbits/extensions/usermanager/views.py @@ -5,7 +5,7 @@ from lnbits.decorators import check_user_exists, validate_uuids from . import usermanager_ext -@usermanager_ext.route("/") +@usermanager_ext.get("/") @validate_uuids(["usr"], required=True) @check_user_exists() async def index(): diff --git a/lnbits/extensions/usermanager/views_api.py b/lnbits/extensions/usermanager/views_api.py index d3bba6ad3..ff10a48aa 100644 --- a/lnbits/extensions/usermanager/views_api.py +++ b/lnbits/extensions/usermanager/views_api.py @@ -23,45 +23,40 @@ from lnbits.core import update_user_extension ### Users -@usermanager_ext.route("/api/v1/users", methods=["GET"]) +@usermanager_ext.get("/api/v1/users") @api_check_wallet_key(key_type="invoice") async def api_usermanager_users(): user_id = g.wallet.user - return ( - jsonify([user._asdict() for user in await get_usermanager_users(user_id)]), + return ([user._asdict() for user in await get_usermanager_users(user_id)], HTTPStatus.OK, ) -@usermanager_ext.route("/api/v1/users/", methods=["GET"]) +@usermanager_ext.get("/api/v1/users/") @api_check_wallet_key(key_type="invoice") async def api_usermanager_user(user_id): user = await get_usermanager_user(user_id) - return ( - jsonify(user._asdict()), + return (user._asdict(), HTTPStatus.OK, ) +class CreateUsersData(BaseModel): + domain: str + subdomain: str + email: str + ip: Optional[str] + sats: Optional[str] -@usermanager_ext.route("/api/v1/users", methods=["POST"]) +@usermanager_ext.post("/api/v1/users") @api_check_wallet_key(key_type="invoice") -@api_validate_post_request( - schema={ - "user_name": {"type": "string", "empty": False, "required": True}, - "wallet_name": {"type": "string", "empty": False, "required": True}, - "admin_id": {"type": "string", "empty": False, "required": True}, - "email": {"type": "string", "required": False}, - "password": {"type": "string", "required": False}, - } -) -async def api_usermanager_users_create(): - user = await create_usermanager_user(**g.data) +async def api_usermanager_users_create(data: CreateUsersData): + user = await create_usermanager_user(**data) full = user._asdict() full["wallets"] = [wallet._asdict() for wallet in await get_usermanager_users_wallets(user.id)] return jsonify(full), HTTPStatus.CREATED -@usermanager_ext.route("/api/v1/users/", methods=["DELETE"]) +@usermanager_ext.delete("/api/v1/users/") @api_check_wallet_key(key_type="invoice") async def api_usermanager_users_delete(user_id): user = await get_usermanager_user(user_id) @@ -73,84 +68,75 @@ async def api_usermanager_users_delete(user_id): ###Activate Extension +class CreateUsersData(BaseModel): + extension: str + userid: str + active: bool -@usermanager_ext.route("/api/v1/extensions", methods=["POST"]) +@usermanager_ext.post("/api/v1/extensions") @api_check_wallet_key(key_type="invoice") -@api_validate_post_request( - schema={ - "extension": {"type": "string", "empty": False, "required": True}, - "userid": {"type": "string", "empty": False, "required": True}, - "active": {"type": "boolean", "required": True}, - } -) -async def api_usermanager_activate_extension(): - user = await get_user(g.data["userid"]) +async def api_usermanager_activate_extension(data: CreateUsersData): + user = await get_user(data.userid) if not user: - return jsonify({"message": "no such user"}), HTTPStatus.NOT_FOUND + return {"message": "no such user"}, HTTPStatus.NOT_FOUND update_user_extension( - user_id=g.data["userid"], extension=g.data["extension"], active=g.data["active"] + user_id=data.userid, extension=data.extension, active=data.active ) - return jsonify({"extension": "updated"}), HTTPStatus.CREATED + return {"extension": "updated"}, HTTPStatus.CREATED ###Wallets +class CreateWalletsData(BaseModel): + user_id: str + wallet_name: str + admin_id: str -@usermanager_ext.route("/api/v1/wallets", methods=["POST"]) +@usermanager_ext.post("/api/v1/wallets") @api_check_wallet_key(key_type="invoice") -@api_validate_post_request( - schema={ - "user_id": {"type": "string", "empty": False, "required": True}, - "wallet_name": {"type": "string", "empty": False, "required": True}, - "admin_id": {"type": "string", "empty": False, "required": True}, - } -) -async def api_usermanager_wallets_create(): + +async def api_usermanager_wallets_create(data: CreateWalletsData): user = await create_usermanager_wallet( - g.data["user_id"], g.data["wallet_name"], g.data["admin_id"] + data.user_id, data.wallet_name, data.admin_id ) - return jsonify(user._asdict()), HTTPStatus.CREATED + return user._asdict(), HTTPStatus.CREATED -@usermanager_ext.route("/api/v1/wallets", methods=["GET"]) +@usermanager_ext.get("/api/v1/wallets") @api_check_wallet_key(key_type="invoice") async def api_usermanager_wallets(): admin_id = g.wallet.user return ( - jsonify( - [wallet._asdict() for wallet in await get_usermanager_wallets(admin_id)] - ), + [wallet._asdict() for wallet in await get_usermanager_wallets(admin_id)], HTTPStatus.OK, ) -@usermanager_ext.route("/api/v1/wallets", methods=["GET"]) +@usermanager_ext.get("/api/v1/wallets") @api_check_wallet_key(key_type="invoice") async def api_usermanager_wallet_transactions(wallet_id): - return jsonify(await get_usermanager_wallet_transactions(wallet_id)), HTTPStatus.OK + return await get_usermanager_wallet_transactions(wallet_id), HTTPStatus.OK -@usermanager_ext.route("/api/v1/wallets/", methods=["GET"]) +@usermanager_ext.get("/api/v1/wallets/") @api_check_wallet_key(key_type="invoice") async def api_usermanager_users_wallets(user_id): wallet = await get_usermanager_users_wallets(user_id) return ( - jsonify( [ wallet._asdict() for wallet in await get_usermanager_users_wallets(user_id) - ] - ), + ], HTTPStatus.OK, ) -@usermanager_ext.route("/api/v1/wallets/", methods=["DELETE"]) +@usermanager_ext.delete("/api/v1/wallets/") @api_check_wallet_key(key_type="invoice") async def api_usermanager_wallets_delete(wallet_id): wallet = await get_usermanager_wallet(wallet_id) if not wallet: - return jsonify({"message": "Wallet does not exist."}), HTTPStatus.NOT_FOUND + return {"message": "Wallet does not exist."}, HTTPStatus.NOT_FOUND await delete_usermanager_wallet(wallet_id, wallet.user) return "", HTTPStatus.NO_CONTENT diff --git a/lnbits/extensions/withdraw/views_api.py b/lnbits/extensions/withdraw/views_api.py index 7564c5cbd..97d7a9de1 100644 --- a/lnbits/extensions/withdraw/views_api.py +++ b/lnbits/extensions/withdraw/views_api.py @@ -6,7 +6,6 @@ from lnbits.core.crud import get_user from lnbits.decorators import api_check_wallet_key, api_validate_post_request from pydantic import BaseModel from fastapi import FastAPI, Query -from fastapi.encoders import jsonable_encoder from . import withdraw_ext from .crud import (