From 88895f80be27d9eed208ebb8c03122772dca0072 Mon Sep 17 00:00:00 2001 From: supertestnet <58400631+supertestnet@users.noreply.github.com> Date: Tue, 27 Apr 2021 10:22:14 -0400 Subject: [PATCH 01/13] note on virtualenv installation (#185) --- docs/guide/installation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 4f1bb853..3c979a3a 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -13,6 +13,7 @@ Download this repo and install the dependencies: ```sh git clone https://github.com/lnbits/lnbits.git cd lnbits/ +# on debian based installations like ubuntu, you may have to intall global python packages. Try sudo apt install python3-venv -y python3 -m venv venv ./venv/bin/pip install -r requirements.txt cp .env.example .env From b6f015b561f40ba281b7f594b4d207376722a295 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Tue, 27 Apr 2021 15:52:44 -0300 Subject: [PATCH 02/13] lnurlp: account for invalid amount in querystring. --- lnbits/extensions/lnurlp/lnurl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/lnurlp/lnurl.py b/lnbits/extensions/lnurlp/lnurl.py index 79076564..5d8bcf08 100644 --- a/lnbits/extensions/lnurlp/lnurl.py +++ b/lnbits/extensions/lnurlp/lnurl.py @@ -54,7 +54,7 @@ async def api_lnurl_callback(link_id): min = link.min * 1000 max = link.max * 1000 - amount_received = int(request.args.get("amount")) + amount_received = int(request.args.get("amount") or 0) if amount_received < min: return ( jsonify( From 9b793c9f3abebe260b26ea4d14ecc18226b2cb8f Mon Sep 17 00:00:00 2001 From: Dave Scotese Date: Sun, 2 May 2021 19:21:27 -0700 Subject: [PATCH 03/13] Nearly undetectable typo fix. (#188) Regularily => regularly --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61fa7965..bc700bfd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ LNbits is a very simple Python server that sits on top of any funding source, an * Fallback wallet for the LNURL scheme * Instant wallet for LN demonstrations -LNbits can run on top of any lightning-network funding source, currently there is support for LND, c-lightning, Spark, LNpay, OpenNode, lntxbot, with more being added regularily. +LNbits can run on top of any lightning-network funding source, currently there is support for LND, c-lightning, Spark, LNpay, OpenNode, lntxbot, with more being added regularly. See [lnbits.org](https://lnbits.org) for more detailed documentation. From d5f5c9473f6d44d906b581bcd951eeafb276db6f Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Wed, 5 May 2021 09:43:52 -0300 Subject: [PATCH 04/13] guard against amount=NaN in two other lnurlw contexts. --- lnbits/extensions/livestream/lnurl.py | 2 +- lnbits/extensions/offlineshop/lnurl.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/livestream/lnurl.py b/lnbits/extensions/livestream/lnurl.py index 1e021f85..3b9e7e31 100644 --- a/lnbits/extensions/livestream/lnurl.py +++ b/lnbits/extensions/livestream/lnurl.py @@ -61,7 +61,7 @@ async def lnurl_callback(track_id): if not track: return jsonify({"status": "ERROR", "reason": "Couldn't find track."}) - amount_received = int(request.args.get("amount")) + amount_received = int(request.args.get("amount") or 0) if amount_received < track.min_sendable: return ( diff --git a/lnbits/extensions/offlineshop/lnurl.py b/lnbits/extensions/offlineshop/lnurl.py index 13944a29..d99e4cea 100644 --- a/lnbits/extensions/offlineshop/lnurl.py +++ b/lnbits/extensions/offlineshop/lnurl.py @@ -49,7 +49,7 @@ async def lnurl_callback(item_id): min = price * 995 max = price * 1010 - amount_received = int(request.args.get("amount")) + amount_received = int(request.args.get("amount") or 0) if amount_received < min: return jsonify( LnurlErrorResponse( From 2b21a4f47900597c6162e7b1d89d04af1a59f358 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Thu, 6 May 2021 12:41:44 -0300 Subject: [PATCH 05/13] g.nursery -> current_app.nursery --- lnbits/app.py | 4 ---- lnbits/core/views/api.py | 6 +++--- lnbits/core/views/generic.py | 5 +++-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index cd700f5c..c5654f8e 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -114,10 +114,6 @@ def register_filters(app: QuartTrio): def register_request_hooks(app: QuartTrio): """Open the core db for each request so everything happens in a big transaction""" - @app.before_request - async def before_request(): - g.nursery = app.nursery - @app.after_request async def set_secure_headers(response): secure_headers.quart(response) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 89330ab3..2547435e 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -3,7 +3,7 @@ import json import lnurl # type: ignore import httpx from urllib.parse import urlparse, urlunparse, urlencode, parse_qs, ParseResult -from quart import g, jsonify, make_response, url_for +from quart import g, current_app, jsonify, make_response, url_for from http import HTTPStatus from binascii import unhexlify from typing import Dict, Union @@ -310,8 +310,8 @@ async def api_payments_sse(): await send_event.send(("keepalive", "")) await trio.sleep(25) - g.nursery.start_soon(payment_received) - g.nursery.start_soon(repeat_keepalive) + current_app.nursery.start_soon(payment_received) + current_app.nursery.start_soon(repeat_keepalive) async def send_events(): try: diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index e17d71b3..f48b054f 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -2,6 +2,7 @@ from os import path from http import HTTPStatus from quart import ( g, + current_app, abort, jsonify, request, @@ -154,7 +155,7 @@ async def lnurl_full_withdraw_callback(): async def pay(): await pay_invoice(wallet_id=wallet.id, payment_request=pr) - g.nursery.start_soon(pay) + current_app.nursery.start_soon(pay) balance_notify = request.args.get("balanceNotify") if balance_notify: @@ -197,7 +198,7 @@ async def lnurlwallet(): user = await get_user(account.id, conn=conn) wallet = await create_wallet(user_id=user.id, conn=conn) - g.nursery.start_soon( + current_app.nursery.start_soon( redeem_lnurl_withdraw, wallet.id, request.args.get("lightning"), From 8cff11bf721be3139be00d1cc1323609c9d21995 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Thu, 6 May 2021 23:22:02 -0300 Subject: [PATCH 06/13] global quart errorhandler. --- lnbits/app.py | 24 +++++++++++++++++++++--- lnbits/tasks.py | 7 ------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index c5654f8e..6137fdaf 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -1,6 +1,7 @@ import sys -import importlib import warnings +import importlib +import traceback from quart import g from quart_trio import QuartTrio @@ -23,7 +24,6 @@ from .tasks import ( invoice_listener, internal_invoice_listener, webhook_handler, - grab_app_for_later, ) from .settings import WALLET @@ -48,7 +48,7 @@ def create_app(config_object="lnbits.settings") -> QuartTrio: register_commands(app) register_request_hooks(app) register_async_tasks(app) - grab_app_for_later(app) + register_exception_handlers(app) return app @@ -135,3 +135,21 @@ def register_async_tasks(app): @app.after_serving async def stop_listeners(): pass + + +def register_exception_handlers(app): + @app.errorhandler(Exception) + async def basic_error(err): + etype, value, tb = sys.exc_info() + traceback.print_exception(etype, err, tb) + exc = traceback.format_exc() + return ( + "\n\n".join( + [ + "LNbits internal error!", + exc, + "If you believe this shouldn't be an error please bring it up on https://t.me/lnbits", + ] + ), + 500, + ) diff --git a/lnbits/tasks.py b/lnbits/tasks.py index 0e2ff98d..756b1142 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -13,13 +13,6 @@ from lnbits.core.crud import ( ) from lnbits.core.services import redeem_lnurl_withdraw -main_app: Optional[QuartTrio] = None - - -def grab_app_for_later(app: QuartTrio): - global main_app - main_app = app - deferred_async: List[Callable] = [] From 5dbbca0ffbe1d48b3d4d1ff08170d1b2f06f5270 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Fri, 14 May 2021 13:12:05 -0300 Subject: [PATCH 07/13] improve invoice deleting code. --- lnbits/core/crud.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index b9f02070..47623cc2 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -267,12 +267,23 @@ async def get_payments( async def delete_expired_invoices( conn: Optional[Connection] = None, ) -> None: + # first we delete all invoices older than one month + await (conn or db).execute( + """ + DELETE FROM apipayments + WHERE pending = 1 AND amount > 0 AND time < strftime('%s', 'now') - 2592000 + """ + ) + + # then we delete all expired invoices, checking one by one rows = await (conn or db).fetchall( """ SELECT bolt11 FROM apipayments - WHERE pending = 1 AND amount > 0 AND time < strftime('%s', 'now') - 86400 - """ + WHERE pending = 1 + AND bolt11 IS NOT NULL + AND amount > 0 AND time < strftime('%s', 'now') - 86400 + """ ) for (payment_request,) in rows: try: From 9ba5342f3cf2a0a40cbc7249fc2bb969336d9fae Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Sat, 15 May 2021 11:06:35 -0300 Subject: [PATCH 08/13] subdomain: guard allowed_record_types type. --- lnbits/extensions/subdomains/templates/subdomains/index.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/subdomains/templates/subdomains/index.html b/lnbits/extensions/subdomains/templates/subdomains/index.html index d62f8f38..74fd4ade 100644 --- a/lnbits/extensions/subdomains/templates/subdomains/index.html +++ b/lnbits/extensions/subdomains/templates/subdomains/index.html @@ -452,7 +452,10 @@ id: this.domainDialog.data.wallet }) var data = this.domainDialog.data - data.allowed_record_types = data.allowed_record_types.join(', ') + data.allowed_record_types = + typeof data.allowed_record_types === 'string' + ? data.allowed_record_types + : data.allowed_record_types.join(', ') console.log(this.domainDialog) if (data.id) { this.updateDomain(wallet, data) From b9db504f3461c2fdae26d26ebce9bb00e503e4eb Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Fri, 21 May 2021 22:57:22 -0300 Subject: [PATCH 09/13] fix links to extensions in payments tags. --- lnbits/core/templates/core/wallet.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index c6851793..b3f7b1c1 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -129,7 +129,7 @@ #{{ props.row.tag }} From f6a8e960875b9fdc2ebb5b179362c712f3d747d6 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Sat, 22 May 2021 20:35:37 -0300 Subject: [PATCH 10/13] fix note about global virtualenv. --- docs/guide/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 3c979a3a..eabef16e 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -13,7 +13,7 @@ Download this repo and install the dependencies: ```sh git clone https://github.com/lnbits/lnbits.git cd lnbits/ -# on debian based installations like ubuntu, you may have to intall global python packages. Try sudo apt install python3-venv -y +# ensure you have virtualenv installed, on debian/ubuntu 'apt install python3-venv' should work python3 -m venv venv ./venv/bin/pip install -r requirements.txt cp .env.example .env From 036e45d77c9a1e633615830bbb10ac0c57d34a72 Mon Sep 17 00:00:00 2001 From: Pac Date: Fri, 28 May 2021 03:11:42 -0300 Subject: [PATCH 11/13] Enforce https on Success URL for lnurlp --- lnbits/extensions/lnurlp/views_api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lnbits/extensions/lnurlp/views_api.py b/lnbits/extensions/lnurlp/views_api.py index de36345a..cbe1bc4e 100644 --- a/lnbits/extensions/lnurlp/views_api.py +++ b/lnbits/extensions/lnurlp/views_api.py @@ -87,6 +87,9 @@ async def api_link_create_or_update(link_id=None): round(g.data["min"]) != g.data["min"] or round(g.data["max"]) != g.data["max"] ): return jsonify({"message": "Must use full satoshis."}), HTTPStatus.BAD_REQUEST + + if g.data["success_url"][:8] != "https://": + return jsonify({"message": "Success URL must be secure https://..."}), HTTPStatus.BAD_REQUEST if link_id: link = await get_pay_link(link_id) From 8af88022cd91df9b87921b64e36e026f11b6b7dc Mon Sep 17 00:00:00 2001 From: Ben Arc Date: Sat, 29 May 2021 13:42:14 +0100 Subject: [PATCH 12/13] Updated usermanager Made use of the email and password fields in db --- lnbits/extensions/usermanager/crud.py | 12 ++++++--- .../templates/usermanager/_api_docs.html | 6 +++-- .../templates/usermanager/index.html | 25 +++++++++++++++++-- lnbits/extensions/usermanager/views_api.py | 18 ++++++++++--- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/lnbits/extensions/usermanager/crud.py b/lnbits/extensions/usermanager/crud.py index 6470c413..dbee287c 100644 --- a/lnbits/extensions/usermanager/crud.py +++ b/lnbits/extensions/usermanager/crud.py @@ -17,7 +17,11 @@ from .models import Users, Wallets async def create_usermanager_user( - user_name: str, wallet_name: str, admin_id: str + user_name: str, + wallet_name: str, + admin_id: str, + email: Optional[str] = None, + password: Optional[str] = None, ) -> Users: account = await create_account() user = await get_user(account.id) @@ -27,10 +31,10 @@ async def create_usermanager_user( await db.execute( """ - INSERT INTO users (id, name, admin) - VALUES (?, ?, ?) + INSERT INTO users (id, name, admin, email, password) + VALUES (?, ?, ?, ?, ?) """, - (user.id, user_name, admin_id), + (user.id, user_name, admin_id, email, password), ) await db.execute( diff --git a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html index fbd13e72..2bb7e92c 100644 --- a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html +++ b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html @@ -114,7 +114,8 @@ {"admin_id": <string>, "user_name": <string>, - "wallet_name": <string>}
Returns 201 CREATED (application/json) @@ -128,7 +129,8 @@ curl -X POST {{ request.url_root }}api/v1/users -d '{"admin_id": "{{ g.user.id }}", "wallet_name": <string>, "user_name": - <string>}' -H "X-Api-Key: {{ g.user.wallets[0].inkey }}" -H + <string>, "email": <Optional string>, "password": < + Optional string>}' -H "X-Api-Key: {{ g.user.wallets[0].inkey }}" -H "Content-type: application/json" diff --git a/lnbits/extensions/usermanager/templates/usermanager/index.html b/lnbits/extensions/usermanager/templates/usermanager/index.html index 44837a54..a7514089 100644 --- a/lnbits/extensions/usermanager/templates/usermanager/index.html +++ b/lnbits/extensions/usermanager/templates/usermanager/index.html @@ -157,6 +157,18 @@ v-model.trim="userDialog.data.walname" label="Initial wallet name" > + + ", methods=["GET"]) +@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()), + HTTPStatus.OK, + ) + + @usermanager_ext.route("/api/v1/users", methods=["POST"]) @api_check_wallet_key(key_type="invoice") @api_validate_post_request( schema={ - "admin_id": {"type": "string", "empty": False, "required": True}, "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["user_name"], g.data["wallet_name"], g.data["admin_id"] - ) + user = await create_usermanager_user(**g.data) return jsonify(user._asdict()), HTTPStatus.CREATED From b6e6d942e48802a8ce16cef80e37461a76d4c5de Mon Sep 17 00:00:00 2001 From: Ben Arc Date: Sat, 29 May 2021 14:03:03 +0100 Subject: [PATCH 13/13] Added api info --- .../templates/usermanager/_api_docs.html | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html index 2bb7e92c..2f516b28 100644 --- a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html +++ b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html @@ -48,6 +48,26 @@ + + + + GET + /usermanager/api/v1/users/<user_id> +
Body (application/json)
+
+ Returns 201 CREATED (application/json) +
+ JSON list of users +
Curl example
+ curl -X GET {{ request.url_root }}api/v1/users/<user_id> -H + "X-Api-Key: {{ g.user.wallets[0].inkey }}" + +
+
+