From 0331861cc6eb515e3d9811afcb558911ecb97dd8 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Fri, 12 Nov 2021 13:05:10 -0300 Subject: [PATCH 1/8] prevent pay_invoice from locking sqlite for the entire app. --- lnbits/core/services.py | 144 ++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 08ee6e37..57722dfd 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -85,81 +85,81 @@ async def pay_invoice( description: str = "", conn: Optional[Connection] = None, ) -> str: - async with (db.reuse_conn(conn) if conn else db.connect()) as conn: - temp_id = f"temp_{urlsafe_short_hash()}" - internal_id = f"internal_{urlsafe_short_hash()}" + temp_id = f"temp_{urlsafe_short_hash()}" + internal_id = f"internal_{urlsafe_short_hash()}" - invoice = bolt11.decode(payment_request) - if invoice.amount_msat == 0: - raise ValueError("Amountless invoices not supported.") - if max_sat and invoice.amount_msat > max_sat * 1000: - raise ValueError("Amount in invoice is too high.") + invoice = bolt11.decode(payment_request) + if invoice.amount_msat == 0: + raise ValueError("Amountless invoices not supported.") + if max_sat and invoice.amount_msat > max_sat * 1000: + raise ValueError("Amount in invoice is too high.") - # put all parameters that don't change here - PaymentKwargs = TypedDict( - "PaymentKwargs", - { - "wallet_id": str, - "payment_request": str, - "payment_hash": str, - "amount": int, - "memo": str, - "extra": Optional[Dict], - }, + # put all parameters that don't change here + PaymentKwargs = TypedDict( + "PaymentKwargs", + { + "wallet_id": str, + "payment_request": str, + "payment_hash": str, + "amount": int, + "memo": str, + "extra": Optional[Dict], + }, + ) + payment_kwargs: PaymentKwargs = dict( + wallet_id=wallet_id, + payment_request=payment_request, + payment_hash=invoice.payment_hash, + amount=-invoice.amount_msat, + memo=description or invoice.description or "", + extra=extra, + ) + + # check_internal() returns the checking_id of the invoice we're waiting for + internal_checking_id = await check_internal(invoice.payment_hash, conn=conn) + if internal_checking_id: + # create a new payment from this wallet + await create_payment( + checking_id=internal_id, + fee=0, + pending=False, + conn=conn, + **payment_kwargs, ) - payment_kwargs: PaymentKwargs = dict( - wallet_id=wallet_id, - payment_request=payment_request, - payment_hash=invoice.payment_hash, - amount=-invoice.amount_msat, - memo=description or invoice.description or "", - extra=extra, + else: + # create a temporary payment here so we can check if + # the balance is enough in the next step + await create_payment( + checking_id=temp_id, + fee=-fee_reserve(invoice.amount_msat), + conn=conn, + **payment_kwargs, ) - # check_internal() returns the checking_id of the invoice we're waiting for - internal_checking_id = await check_internal(invoice.payment_hash, conn=conn) - if internal_checking_id: - # create a new payment from this wallet - await create_payment( - checking_id=internal_id, - fee=0, - pending=False, - conn=conn, - **payment_kwargs, - ) - else: - # create a temporary payment here so we can check if - # the balance is enough in the next step - await create_payment( - checking_id=temp_id, - fee=-fee_reserve(invoice.amount_msat), - conn=conn, - **payment_kwargs, - ) + # do the balance check + wallet = await get_wallet(wallet_id, conn=conn) + assert wallet + if wallet.balance_msat < 0: + raise PermissionError("Insufficient balance.") - # do the balance check - wallet = await get_wallet(wallet_id, conn=conn) - assert wallet - if wallet.balance_msat < 0: - raise PermissionError("Insufficient balance.") + if internal_checking_id: + # mark the invoice from the other side as not pending anymore + # so the other side only has access to his new money when we are sure + # the payer has enough to deduct from + await update_payment_status( + checking_id=internal_checking_id, pending=False, conn=conn + ) - if internal_checking_id: - # mark the invoice from the other side as not pending anymore - # so the other side only has access to his new money when we are sure - # the payer has enough to deduct from - await update_payment_status( - checking_id=internal_checking_id, pending=False, conn=conn - ) + # notify receiver asynchronously - # notify receiver asynchronously + from lnbits.tasks import internal_invoice_queue - from lnbits.tasks import internal_invoice_queue - - await internal_invoice_queue.put(internal_checking_id) - else: - # actually pay the external invoice - payment: PaymentResponse = await WALLET.pay_invoice(payment_request) - if payment.checking_id: + await internal_invoice_queue.put(internal_checking_id) + else: + # actually pay the external invoice + payment: PaymentResponse = await WALLET.pay_invoice(payment_request) + if payment.checking_id: + async with (db.reuse_conn(conn) if conn else db.connect()) as conn: await create_payment( checking_id=payment.checking_id, fee=payment.fee_msat, @@ -169,13 +169,13 @@ async def pay_invoice( **payment_kwargs, ) await delete_payment(temp_id, conn=conn) - else: - raise PaymentFailure( - payment.error_message - or "Payment failed, but backend didn't give us an error message." - ) + else: + raise PaymentFailure( + payment.error_message + or "Payment failed, but backend didn't give us an error message." + ) - return invoice.payment_hash + return invoice.payment_hash async def redeem_lnurl_withdraw( From bec0b1d1febc9f682513d9c7856abfc3418ba281 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Sat, 13 Nov 2021 11:42:11 +0100 Subject: [PATCH 2/8] fix db connect --- lnbits/core/services.py | 115 +++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 57722dfd..64db8d72 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -85,70 +85,72 @@ async def pay_invoice( description: str = "", conn: Optional[Connection] = None, ) -> str: - temp_id = f"temp_{urlsafe_short_hash()}" - internal_id = f"internal_{urlsafe_short_hash()}" + async with (db.reuse_conn(conn) if conn else db.connect()) as conn: + temp_id = f"temp_{urlsafe_short_hash()}" + internal_id = f"internal_{urlsafe_short_hash()}" - invoice = bolt11.decode(payment_request) - if invoice.amount_msat == 0: - raise ValueError("Amountless invoices not supported.") - if max_sat and invoice.amount_msat > max_sat * 1000: - raise ValueError("Amount in invoice is too high.") + invoice = bolt11.decode(payment_request) + if invoice.amount_msat == 0: + raise ValueError("Amountless invoices not supported.") + if max_sat and invoice.amount_msat > max_sat * 1000: + raise ValueError("Amount in invoice is too high.") - # put all parameters that don't change here - PaymentKwargs = TypedDict( - "PaymentKwargs", - { - "wallet_id": str, - "payment_request": str, - "payment_hash": str, - "amount": int, - "memo": str, - "extra": Optional[Dict], - }, - ) - payment_kwargs: PaymentKwargs = dict( - wallet_id=wallet_id, - payment_request=payment_request, - payment_hash=invoice.payment_hash, - amount=-invoice.amount_msat, - memo=description or invoice.description or "", - extra=extra, - ) - - # check_internal() returns the checking_id of the invoice we're waiting for - internal_checking_id = await check_internal(invoice.payment_hash, conn=conn) - if internal_checking_id: - # create a new payment from this wallet - await create_payment( - checking_id=internal_id, - fee=0, - pending=False, - conn=conn, - **payment_kwargs, + # put all parameters that don't change here + PaymentKwargs = TypedDict( + "PaymentKwargs", + { + "wallet_id": str, + "payment_request": str, + "payment_hash": str, + "amount": int, + "memo": str, + "extra": Optional[Dict], + }, ) - else: - # create a temporary payment here so we can check if - # the balance is enough in the next step - await create_payment( - checking_id=temp_id, - fee=-fee_reserve(invoice.amount_msat), - conn=conn, - **payment_kwargs, + payment_kwargs: PaymentKwargs = dict( + wallet_id=wallet_id, + payment_request=payment_request, + payment_hash=invoice.payment_hash, + amount=-invoice.amount_msat, + memo=description or invoice.description or "", + extra=extra, ) - # do the balance check - wallet = await get_wallet(wallet_id, conn=conn) - assert wallet - if wallet.balance_msat < 0: - raise PermissionError("Insufficient balance.") + # check_internal() returns the checking_id of the invoice we're waiting for + internal_checking_id = await check_internal(invoice.payment_hash, conn=conn) + if internal_checking_id: + # create a new payment from this wallet + await create_payment( + checking_id=internal_id, + fee=0, + pending=False, + conn=conn, + **payment_kwargs, + ) + else: + # create a temporary payment here so we can check if + # the balance is enough in the next step + await create_payment( + checking_id=temp_id, + fee=-fee_reserve(invoice.amount_msat), + conn=conn, + **payment_kwargs, + ) + + # do the balance check + wallet = await get_wallet(wallet_id, conn=conn) + assert wallet + if wallet.balance_msat < 0: + raise PermissionError("Insufficient balance.") if internal_checking_id: # mark the invoice from the other side as not pending anymore # so the other side only has access to his new money when we are sure # the payer has enough to deduct from - await update_payment_status( - checking_id=internal_checking_id, pending=False, conn=conn - ) + async with db.connect() as conn: + await update_payment_status( + checking_id=internal_checking_id, pending=False, conn=conn + ) # notify receiver asynchronously @@ -159,7 +161,7 @@ async def pay_invoice( # actually pay the external invoice payment: PaymentResponse = await WALLET.pay_invoice(payment_request) if payment.checking_id: - async with (db.reuse_conn(conn) if conn else db.connect()) as conn: + async with db.connect() as conn: await create_payment( checking_id=payment.checking_id, fee=payment.fee_msat, @@ -314,7 +316,8 @@ async def check_invoice_status( if not payment.pending: return status if payment.is_out and status.failed: - print(f" - deleting outgoing failed payment {payment.checking_id}: {status}") + print( + f" - deleting outgoing failed payment {payment.checking_id}: {status}") await payment.delete() elif not status.pending: print( From feb5a9e6c8cc0297ee729a2d83b120633d77d1c9 Mon Sep 17 00:00:00 2001 From: Arc <33088785+arcbtc@users.noreply.github.com> Date: Tue, 16 Nov 2021 03:11:01 +0000 Subject: [PATCH 3/8] Update views.py --- lnbits/extensions/satsdice/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/satsdice/views.py b/lnbits/extensions/satsdice/views.py index dd71643d..1f553476 100644 --- a/lnbits/extensions/satsdice/views.py +++ b/lnbits/extensions/satsdice/views.py @@ -69,7 +69,7 @@ async def displaywin( request: Request, link_id: str = Query(None), payment_hash: str = Query(None) ): satsdicelink = await get_satsdice_pay(link_id) - if not satsdiceLink: + if not satsdicelink: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="satsdice link does not exist." ) From 4c77d6f11e4d3399858bb50bb478a7d215a5ad74 Mon Sep 17 00:00:00 2001 From: Charles Hill Date: Fri, 12 Nov 2021 02:57:31 -0500 Subject: [PATCH 4/8] Fix Bleskomat extension for FastAPI branch --- lnbits/extensions/bleskomat/helpers.py | 4 ++-- lnbits/extensions/bleskomat/lnurl_api.py | 6 +++--- lnbits/extensions/bleskomat/models.py | 10 +++++----- lnbits/extensions/bleskomat/views.py | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lnbits/extensions/bleskomat/helpers.py b/lnbits/extensions/bleskomat/helpers.py index 1062ca27..6e55b3df 100644 --- a/lnbits/extensions/bleskomat/helpers.py +++ b/lnbits/extensions/bleskomat/helpers.py @@ -35,8 +35,8 @@ def generate_bleskomat_lnurl_secret(api_key_id: str, signature: str): return m.hexdigest() -def get_callback_url(request: Request): - return request.url_for("bleskomat.api_bleskomat_lnurl") +def get_callback_url(req: Request): + return req.url_for("bleskomat.api_bleskomat_lnurl") 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 fa3e6133..4faa0ee9 100644 --- a/lnbits/extensions/bleskomat/lnurl_api.py +++ b/lnbits/extensions/bleskomat/lnurl_api.py @@ -25,9 +25,9 @@ from .helpers import ( # Handles signed URL from Bleskomat ATMs and "action" callback of auto-generated LNURLs. @bleskomat_ext.get("/u", name="bleskomat.api_bleskomat_lnurl") -async def api_bleskomat_lnurl(request: Request): +async def api_bleskomat_lnurl(req: Request): try: - query = request.query_params + query = req.query_params # Unshorten query if "s" is used instead of "signature". if "s" in query: @@ -96,7 +96,7 @@ async def api_bleskomat_lnurl(request: Request): ) # Reply with LNURL response object. - return lnurl.get_info_response_object(secret) + return lnurl.get_info_response_object(secret, req) # No signature provided. # Treat as "action" callback. diff --git a/lnbits/extensions/bleskomat/models.py b/lnbits/extensions/bleskomat/models.py index e96ddb80..89aefe1f 100644 --- a/lnbits/extensions/bleskomat/models.py +++ b/lnbits/extensions/bleskomat/models.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, validator from starlette.requests import Request from lnbits import bolt11 -from lnbits.core.services import pay_invoice +from lnbits.core.services import pay_invoice, PaymentFailure from . import db from .exchange_rates import exchange_rate_providers, fiat_currencies @@ -119,13 +119,13 @@ class BleskomatLnurl(BaseModel): tag = self.tag if tag == "withdrawRequest": try: - payment_hash = await pay_invoice( + await pay_invoice( wallet_id=self.wallet, payment_request=query["pr"] ) + except (ValueError, PermissionError, PaymentFailure) as e: + raise LnurlValidationError("Failed to pay invoice: " + str(e)) except Exception: - raise LnurlValidationError("Failed to pay invoice") - if not payment_hash: - raise LnurlValidationError("Failed to pay invoice") + raise LnurlValidationError("Unexpected error") async def use(self, conn) -> bool: now = int(time.time()) diff --git a/lnbits/extensions/bleskomat/views.py b/lnbits/extensions/bleskomat/views.py index c3e775c8..92d47513 100644 --- a/lnbits/extensions/bleskomat/views.py +++ b/lnbits/extensions/bleskomat/views.py @@ -14,13 +14,13 @@ templates = Jinja2Templates(directory="templates") @bleskomat_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index(req: Request, user: User = Depends(check_user_exists)): bleskomat_vars = { - "callback_url": get_callback_url(request=request), + "callback_url": get_callback_url(req), "exchange_rate_providers": exchange_rate_providers_serializable, "fiat_currencies": fiat_currencies, } return bleskomat_renderer().TemplateResponse( "bleskomat/index.html", - {"request": request, "user": user.dict(), "bleskomat_vars": bleskomat_vars}, + {"request": req, "user": user.dict(), "bleskomat_vars": bleskomat_vars}, ) From d8e42379612d2dcf95233ddecea00b5d0c0b4f6e Mon Sep 17 00:00:00 2001 From: Stefan Stammberger Date: Wed, 17 Nov 2021 14:33:23 +0100 Subject: [PATCH 5/8] fix: TipJar wallet access bug --- lnbits/extensions/tipjar/views_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/tipjar/views_api.py b/lnbits/extensions/tipjar/views_api.py index c25e08ea..1033dbe8 100644 --- a/lnbits/extensions/tipjar/views_api.py +++ b/lnbits/extensions/tipjar/views_api.py @@ -172,7 +172,7 @@ async def api_delete_tip( raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="No tip with this ID!" ) - if tip.wallet != g.wallet.id: + if tip.wallet != wallet.wallet.id: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not authorized to delete this tip!", @@ -192,7 +192,7 @@ async def api_delete_tipjar( raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="No tipjar with this ID!" ) - if tipjar.wallet != g.wallet.id: + if tipjar.wallet != wallet.wallet.id: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, From 83b7779972ddff36a11784342b9926db571268b8 Mon Sep 17 00:00:00 2001 From: Stefan Stammberger Date: Wed, 24 Nov 2021 14:31:55 +0100 Subject: [PATCH 6/8] chore: apply black to all .py files --- conv.py | 2 +- lnbits/core/crud.py | 4 +- lnbits/core/services.py | 3 +- lnbits/core/views/public_api.py | 1 + lnbits/extensions/livestream/models.py | 2 +- lnbits/extensions/lnaddress/__init__.py | 6 +- lnbits/extensions/lnaddress/cloudflare.py | 4 +- lnbits/extensions/lnaddress/crud.py | 53 ++++++++++------ lnbits/extensions/lnaddress/lnurl.py | 26 +++++--- lnbits/extensions/lnaddress/migrations.py | 2 + lnbits/extensions/lnaddress/models.py | 5 +- lnbits/extensions/lnaddress/tasks.py | 4 +- lnbits/extensions/lnaddress/views.py | 14 +++-- lnbits/extensions/lnaddress/views_api.py | 75 ++++++++++++++--------- lnbits/extensions/lndhub/views_api.py | 4 +- lnbits/extensions/lnurlpos/crud.py | 5 +- lnbits/extensions/withdraw/lnurl.py | 13 +--- lnbits/wallets/lnbits.py | 6 +- 18 files changed, 139 insertions(+), 90 deletions(-) diff --git a/conv.py b/conv.py index bf408cfb..14233f2d 100644 --- a/conv.py +++ b/conv.py @@ -1,5 +1,5 @@ ## Python script to migrate an LNbits SQLite DB to Postgres -## All credits to @Fritz446 for the awesome work +## All credits to @Fritz446 for the awesome work ## pip install psycopg2 OR psycopg2-binary diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index f69ca95b..a9f1b3a0 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -278,7 +278,9 @@ async def get_payments( return [Payment.from_row(row) for row in rows] -async def delete_expired_invoices(conn: Optional[Connection] = None,) -> None: +async def delete_expired_invoices( + conn: Optional[Connection] = None, +) -> None: # first we delete all invoices older than one month await (conn or db).execute( f""" diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 64db8d72..2549b296 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -316,8 +316,7 @@ async def check_invoice_status( if not payment.pending: return status if payment.is_out and status.failed: - print( - f" - deleting outgoing failed payment {payment.checking_id}: {status}") + print(f" - deleting outgoing failed payment {payment.checking_id}: {status}") await payment.delete() elif not status.pending: print( diff --git a/lnbits/core/views/public_api.py b/lnbits/core/views/public_api.py index 32abacb6..6019a139 100644 --- a/lnbits/core/views/public_api.py +++ b/lnbits/core/views/public_api.py @@ -15,6 +15,7 @@ from ..tasks import api_invoice_listeners @core_app.get("/.well-known/lnurlp/{username}") async def lnaddress(username: str, request: Request): from lnbits.extensions.lnaddress.lnurl import lnurl_response + domain = request.client.host return await lnurl_response(username, domain) diff --git a/lnbits/extensions/livestream/models.py b/lnbits/extensions/livestream/models.py index 1b5d4f7a..521909a3 100644 --- a/lnbits/extensions/livestream/models.py +++ b/lnbits/extensions/livestream/models.py @@ -76,7 +76,7 @@ class Track(BaseModel): return None url = request.url_for("livestream.track_redirect_download", track_id=self.id) - url_with_query = f"{url}?p={payment_hash}" + url_with_query = f"{url}?p={payment_hash}" return UrlAction( url=url_with_query, diff --git a/lnbits/extensions/lnaddress/__init__.py b/lnbits/extensions/lnaddress/__init__.py index 102c48ec..6e8afa37 100644 --- a/lnbits/extensions/lnaddress/__init__.py +++ b/lnbits/extensions/lnaddress/__init__.py @@ -8,10 +8,8 @@ from lnbits.tasks import catch_everything_and_restart db = Database("ext_lnaddress") -lnaddress_ext: APIRouter = APIRouter( - prefix="/lnaddress", - tags=["lnaddress"] -) +lnaddress_ext: APIRouter = APIRouter(prefix="/lnaddress", tags=["lnaddress"]) + def lnaddress_renderer(): return template_renderer(["lnbits/extensions/lnaddress/templates"]) diff --git a/lnbits/extensions/lnaddress/cloudflare.py b/lnbits/extensions/lnaddress/cloudflare.py index 3f3ed171..e22daf95 100644 --- a/lnbits/extensions/lnaddress/cloudflare.py +++ b/lnbits/extensions/lnaddress/cloudflare.py @@ -2,9 +2,7 @@ from lnbits.extensions.lnaddress.models import Domains import httpx, json -async def cloudflare_create_record( - domain: Domains, ip: str -): +async def cloudflare_create_record(domain: Domains, ip: str): url = ( "https://api.cloudflare.com/client/v4/zones/" + domain.cf_zone_id diff --git a/lnbits/extensions/lnaddress/crud.py b/lnbits/extensions/lnaddress/crud.py index 36c0d54e..7ad3598d 100644 --- a/lnbits/extensions/lnaddress/crud.py +++ b/lnbits/extensions/lnaddress/crud.py @@ -7,9 +7,7 @@ from . import db from .models import Addresses, CreateAddress, CreateDomain, Domains -async def create_domain( - data: CreateDomain -) -> Domains: +async def create_domain(data: CreateDomain) -> Domains: domain_id = urlsafe_short_hash() await db.execute( """ @@ -37,21 +35,20 @@ async def update_domain(domain_id: str, **kwargs) -> Domains: await db.execute( f"UPDATE lnaddress.domain SET {q} WHERE id = ?", (*kwargs.values(), domain_id) ) - row = await db.fetchone( - "SELECT * FROM lnaddress.domain WHERE id = ?", (domain_id,) - ) + row = await db.fetchone("SELECT * FROM lnaddress.domain WHERE id = ?", (domain_id,)) assert row, "Newly updated domain couldn't be retrieved" return Domains(**row) + async def delete_domain(domain_id: str) -> None: await db.execute("DELETE FROM lnaddress.domain WHERE id = ?", (domain_id,)) + async def get_domain(domain_id: str) -> Optional[Domains]: - row = await db.fetchone( - "SELECT * FROM lnaddress.domain WHERE id = ?", (domain_id,) - ) + row = await db.fetchone("SELECT * FROM lnaddress.domain WHERE id = ?", (domain_id,)) return Domains(**row) if row else None + async def get_domains(wallet_ids: Union[str, List[str]]) -> List[Domains]: if isinstance(wallet_ids, str): wallet_ids = [wallet_ids] @@ -63,12 +60,12 @@ async def get_domains(wallet_ids: Union[str, List[str]]) -> List[Domains]: return [Domains(**row) for row in rows] + ## ADRESSES + async def create_address( - payment_hash: str, - wallet: str, - data: CreateAddress + payment_hash: str, wallet: str, data: CreateAddress ) -> Addresses: await db.execute( """ @@ -93,6 +90,7 @@ async def create_address( assert new_address, "Newly created address couldn't be retrieved" return new_address + async def get_address(address_id: str) -> Optional[Addresses]: row = await db.fetchone( "SELECT a.* FROM lnaddress.address AS a INNER JOIN lnaddress.domain AS d ON a.id = ? AND a.domain = d.id", @@ -100,18 +98,24 @@ async def get_address(address_id: str) -> Optional[Addresses]: ) return Addresses(**row) if row else None + async def get_address_by_username(username: str, domain: str) -> Optional[Addresses]: row = await db.fetchone( "SELECT a.* FROM lnaddress.address AS a INNER JOIN lnaddress.domain AS d ON a.username = ? AND d.domain = ?", # "SELECT * FROM lnaddress.address WHERE username = ? AND domain = ?", - (username, domain,), + ( + username, + domain, + ), ) print("ADD", row) return Addresses(**row) if row else None + async def delete_address(address_id: str) -> None: await db.execute("DELETE FROM lnaddress.address WHERE id = ?", (address_id,)) + async def get_addresses(wallet_ids: Union[str, List[str]]) -> List[Addresses]: if isinstance(wallet_ids, str): wallet_ids = [wallet_ids] @@ -124,6 +128,7 @@ async def get_addresses(wallet_ids: Union[str, List[str]]) -> List[Addresses]: print([Addresses(**row) for row in rows]) return [Addresses(**row) for row in rows] + async def set_address_paid(payment_hash: str) -> Addresses: _address = await get_address(payment_hash) address = _address._asdict() @@ -142,6 +147,7 @@ async def set_address_paid(payment_hash: str) -> Addresses: assert new_address, "Newly paid address couldn't be retrieved" return new_address + async def set_address_renewed(address_id: str, duration: int): _address = await get_address(address_id) address = _address._asdict() @@ -153,7 +159,10 @@ async def set_address_renewed(address_id: str, duration: int): SET duration = ? WHERE id = ? """, - (extend_duration, address_id,), + ( + extend_duration, + address_id, + ), ) updated_address = await get_address(address_id) assert updated_address, "Renewed address couldn't be retrieved" @@ -161,17 +170,21 @@ async def set_address_renewed(address_id: str, duration: int): async def check_address_available(username: str, domain: str): - row, = await db.fetchone( + (row,) = await db.fetchone( "SELECT COUNT(username) FROM lnaddress.address WHERE username = ? AND domain = ?", - (username, domain,), + ( + username, + domain, + ), ) return row + async def purge_addresses(domain_id: str): rows = await db.fetchall( "SELECT * FROM lnaddress.address WHERE domain = ?", - (domain_id, ), + (domain_id,), ) now = datetime.now().timestamp() @@ -181,8 +194,10 @@ async def purge_addresses(domain_id: str): start = datetime.fromtimestamp(r["time"]) paid = r["paid"] - pay_expire = now > start.timestamp() + 86400 #if payment wasn't made in 1 day - expired = now > (start + timedelta(days = r["duration"] + 1)).timestamp() #give user 1 day to topup is address + pay_expire = now > start.timestamp() + 86400 # if payment wasn't made in 1 day + expired = ( + now > (start + timedelta(days=r["duration"] + 1)).timestamp() + ) # give user 1 day to topup is address if not paid and pay_expire: print("DELETE UNP_PAY_EXP", r["username"]) diff --git a/lnbits/extensions/lnaddress/lnurl.py b/lnbits/extensions/lnaddress/lnurl.py index ab043a11..cf7da8d4 100644 --- a/lnbits/extensions/lnaddress/lnurl.py +++ b/lnbits/extensions/lnaddress/lnurl.py @@ -23,13 +23,15 @@ async def lnurl_response(username: str, domain: str, request: Request): ## CHECK IF USER IS STILL VALID/PAYING now = datetime.now().timestamp() start = datetime.fromtimestamp(address.time) - expiration = (start + timedelta(days = address.duration)).timestamp() + expiration = (start + timedelta(days=address.duration)).timestamp() if now > expiration: return LnurlErrorResponse(reason="Address has expired.").dict() resp = LnurlPayResponse( - callback=request.url_for("lnaddress.lnurl_callback", address_id=address.id, _external=True), + callback=request.url_for( + "lnaddress.lnurl_callback", address_id=address.id, _external=True + ), min_sendable=1000, max_sendable=1000000000, metadata=await address.lnurlpay_metadata(), @@ -37,14 +39,13 @@ async def lnurl_response(username: str, domain: str, request: Request): return resp.dict() + @lnaddress_ext.get("/lnurl/cb/{address_id}", name="lnaddress.lnurl_callback") async def lnurl_callback(address_id, amount: int = Query(...)): address = await get_address(address_id) if not address: - return LnurlErrorResponse( - reason=f"Address not found" - ).dict() + return LnurlErrorResponse(reason=f"Address not found").dict() amount_received = amount # min = 1000 @@ -64,17 +65,26 @@ async def lnurl_callback(address_id, amount: int = Query(...)): domain = await get_domain(address.domain) - base_url = address.wallet_endpoint[:-1] if address.wallet_endpoint.endswith('/') else address.wallet_endpoint + base_url = ( + address.wallet_endpoint[:-1] + if address.wallet_endpoint.endswith("/") + else address.wallet_endpoint + ) async with httpx.AsyncClient() as client: try: call = await client.post( base_url + "/api/v1/payments", - headers={"X-Api-Key": address.wallet_key, "Content-Type": "application/json"}, + headers={ + "X-Api-Key": address.wallet_key, + "Content-Type": "application/json", + }, json={ "out": False, "amount": int(amount_received / 1000), - "description_hash": hashlib.sha256((await address.lnurlpay_metadata()).encode("utf-8")).hexdigest(), + "description_hash": hashlib.sha256( + (await address.lnurlpay_metadata()).encode("utf-8") + ).hexdigest(), "extra": {"tag": f"Payment to {address.username}@{domain.domain}"}, }, timeout=40, diff --git a/lnbits/extensions/lnaddress/migrations.py b/lnbits/extensions/lnaddress/migrations.py index 4c8b8be1..a9b1137b 100644 --- a/lnbits/extensions/lnaddress/migrations.py +++ b/lnbits/extensions/lnaddress/migrations.py @@ -16,6 +16,7 @@ async def m001_initial(db): """ ) + async def m002_addresses(db): await db.execute( """ @@ -37,5 +38,6 @@ async def m002_addresses(db): """ ) + # async def m003_create_unique_indexes(db): # await db.execute("CREATE UNIQUE INDEX IF NOT EXISTS address_at_domain ON lnaddress.address (domain, username);") diff --git a/lnbits/extensions/lnaddress/models.py b/lnbits/extensions/lnaddress/models.py index e0ff9fa1..67ab07bb 100644 --- a/lnbits/extensions/lnaddress/models.py +++ b/lnbits/extensions/lnaddress/models.py @@ -7,13 +7,14 @@ from pydantic.main import BaseModel # type: ignore class CreateDomain(BaseModel): - wallet: str = Query(...) + wallet: str = Query(...) domain: str = Query(...) cf_token: str = Query(...) cf_zone_id: str = Query(...) webhook: str = Query(None) cost: int = Query(..., ge=0) + class Domains(BaseModel): id: str wallet: str @@ -24,6 +25,7 @@ class Domains(BaseModel): cost: int time: int + class CreateAddress(BaseModel): domain: str = Query(...) username: str = Query(...) @@ -33,6 +35,7 @@ class CreateAddress(BaseModel): sats: int = Query(..., ge=0) duration: int = Query(..., ge=1) + class Addresses(BaseModel): id: str wallet: str diff --git a/lnbits/extensions/lnaddress/tasks.py b/lnbits/extensions/lnaddress/tasks.py index 710b84bd..f962f4ab 100644 --- a/lnbits/extensions/lnaddress/tasks.py +++ b/lnbits/extensions/lnaddress/tasks.py @@ -52,7 +52,9 @@ async def on_invoice_paid(payment: Payment) -> None: elif "renew lnaddress" == payment.extra.get("tag"): await payment.set_pending(False) - await set_address_renewed(address_id=payment.extra["id"], duration=payment.extra["duration"]) + await set_address_renewed( + address_id=payment.extra["id"], duration=payment.extra["duration"] + ) await call_webhook_on_paid(payment.payment_hash) else: diff --git a/lnbits/extensions/lnaddress/views.py b/lnbits/extensions/lnaddress/views.py index 9f6542df..ac24dbad 100644 --- a/lnbits/extensions/lnaddress/views.py +++ b/lnbits/extensions/lnaddress/views.py @@ -18,7 +18,10 @@ templates = Jinja2Templates(directory="templates") @lnaddress_ext.get("/", response_class=HTMLResponse) async def index(request: Request, user: User = Depends(check_user_exists)): - return lnaddress_renderer().TemplateResponse("lnaddress/index.html", {"request": request, "user": user.dict()}) + return lnaddress_renderer().TemplateResponse( + "lnaddress/index.html", {"request": request, "user": user.dict()} + ) + @lnaddress_ext.get("/{domain_id}") async def display(domain_id, request: Request): @@ -33,11 +36,12 @@ async def display(domain_id, request: Request): wallet = await get_wallet(domain.wallet) return lnaddress_renderer().TemplateResponse( - "lnaddress/display.html",{ + "lnaddress/display.html", + { "request": request, - "domain_id":domain.id, + "domain_id": domain.id, "domain_domain": domain.domain, "domain_cost": domain.cost, - "domain_wallet_inkey": wallet.inkey - } + "domain_wallet_inkey": wallet.inkey, + }, ) diff --git a/lnbits/extensions/lnaddress/views_api.py b/lnbits/extensions/lnaddress/views_api.py index 38cd723e..a32cf499 100644 --- a/lnbits/extensions/lnaddress/views_api.py +++ b/lnbits/extensions/lnaddress/views_api.py @@ -43,7 +43,12 @@ async def api_domains( @lnaddress_ext.post("/api/v1/domains") @lnaddress_ext.put("/api/v1/domains/{domain_id}") -async def api_domain_create(request: Request,data: CreateDomain, domain_id=None, g: WalletTypeInfo = Depends(get_key_type)): +async def api_domain_create( + request: Request, + data: CreateDomain, + domain_id=None, + g: WalletTypeInfo = Depends(get_key_type), +): if domain_id: domain = await get_domain(domain_id) @@ -64,7 +69,7 @@ async def api_domain_create(request: Request,data: CreateDomain, domain_id=None, domain = await create_domain(data=data) root_url = urlparse(request.url.path).netloc - #root_url = request.url_root + # root_url = request.url_root cf_response = await cloudflare_create_record( domain=domain, @@ -75,32 +80,36 @@ async def api_domain_create(request: Request,data: CreateDomain, domain_id=None, await delete_domain(domain.id) raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, - detail="Problem with cloudflare: " + cf_response["errors"][0]["message"], + detail="Problem with cloudflare: " + + cf_response["errors"][0]["message"], ) return domain.dict() + @lnaddress_ext.delete("/api/v1/domains/{domain_id}") async def api_domain_delete(domain_id, g: WalletTypeInfo = Depends(get_key_type)): domain = await get_domain(domain_id) if not domain: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, - detail="Domain does not exist.", - ) + status_code=HTTPStatus.NOT_FOUND, + detail="Domain does not exist.", + ) if domain.wallet != g.wallet.id: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="Not your domain", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="Not your domain", + ) await delete_domain(domain_id) raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + # ADDRESSES + @lnaddress_ext.get("/api/v1/addresses") async def api_addresses( g: WalletTypeInfo = Depends(get_key_type), @@ -113,6 +122,7 @@ async def api_addresses( return [address.dict() for address in await get_addresses(wallet_ids)] + @lnaddress_ext.get("/api/v1/address/{domain}/{username}/{wallet_key}") async def api_get_user_info(username, wallet_key, domain): address = await get_address_by_username(username, domain) @@ -125,39 +135,43 @@ async def api_get_user_info(username, wallet_key, domain): if address.wallet_key != wallet_key: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="Incorrect user/wallet information.", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="Incorrect user/wallet information.", + ) return address.dict() + @lnaddress_ext.get("/api/v1/address/availabity/{domain_id}/{username}") async def api_check_available_username(domain_id, username): used_username = await check_address_available(username, domain_id) return used_username + @lnaddress_ext.post("/api/v1/address/{domain_id}") @lnaddress_ext.put("/api/v1/address/{domain_id}/{user}/{wallet_key}") -async def api_lnaddress_make_address(domain_id, data: CreateAddress, user=None, wallet_key=None): +async def api_lnaddress_make_address( + domain_id, data: CreateAddress, user=None, wallet_key=None +): domain = await get_domain(domain_id) # If the request is coming for the non-existant domain if not domain: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="The domain does not exist.", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="The domain does not exist.", + ) domain_cost = domain[6] sats = data.sats ## FAILSAFE FOR CREATING ADDRESSES BY API - if(domain_cost * data.duration != data.sats): + if domain_cost * data.duration != data.sats: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="The amount is not correct. Either 'duration', or 'sats' are wrong.", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="The amount is not correct. Either 'duration', or 'sats' are wrong.", + ) if user: print("USER", user, domain.domain) @@ -183,7 +197,7 @@ async def api_lnaddress_make_address(domain_id, data: CreateAddress, user=None, extra={ "tag": "renew lnaddress", "id": address.id, - "duration": data.duration + "duration": data.duration, }, ) @@ -215,7 +229,7 @@ async def api_lnaddress_make_address(domain_id, data: CreateAddress, user=None, status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e), ) - + address = await create_address( payment_hash=payment_hash, wallet=domain.wallet, data=data ) @@ -228,6 +242,7 @@ async def api_lnaddress_make_address(domain_id, data: CreateAddress, user=None, return {"payment_hash": payment_hash, "payment_request": payment_request} + @lnaddress_ext.get("/api/v1/addresses/{payment_hash}") async def api_address_send_address(payment_hash): address = await get_address(payment_hash) @@ -236,27 +251,27 @@ async def api_address_send_address(payment_hash): status = await check_invoice_status(domain.wallet, payment_hash) is_paid = not status.pending except Exception as e: - return {"paid": False, 'error': str(e)} + return {"paid": False, "error": str(e)} if is_paid: return {"paid": True} return {"paid": False} + @lnaddress_ext.delete("/api/v1/addresses/{address_id}") async def api_address_delete(address_id, g: WalletTypeInfo = Depends(get_key_type)): address = await get_address(address_id) if not address: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, - detail="Address does not exist.", - ) + status_code=HTTPStatus.NOT_FOUND, + detail="Address does not exist.", + ) if address.wallet != g.wallet.id: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="Not your address.", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="Not your address.", + ) await delete_address(address_id) raise HTTPException(status_code=HTTPStatus.NO_CONTENT) - \ No newline at end of file diff --git a/lnbits/extensions/lndhub/views_api.py b/lnbits/extensions/lndhub/views_api.py index d28bbd9e..fdda1356 100644 --- a/lnbits/extensions/lndhub/views_api.py +++ b/lnbits/extensions/lndhub/views_api.py @@ -108,7 +108,9 @@ async def lndhub_payinvoice( @lndhub_ext.get("/ext/balance") -async def lndhub_balance(wallet: WalletTypeInfo = Depends(check_wallet),): +async def lndhub_balance( + wallet: WalletTypeInfo = Depends(check_wallet), +): return {"BTC": {"AvailableBalance": wallet.wallet.balance}} diff --git a/lnbits/extensions/lnurlpos/crud.py b/lnbits/extensions/lnurlpos/crud.py index a2ca88c5..55f8bec5 100644 --- a/lnbits/extensions/lnurlpos/crud.py +++ b/lnbits/extensions/lnurlpos/crud.py @@ -8,7 +8,9 @@ from .models import lnurlposs, lnurlpospayment, createLnurlpos ###############lnurlposS########################## -async def create_lnurlpos(data: createLnurlpos,) -> lnurlposs: +async def create_lnurlpos( + data: createLnurlpos, +) -> lnurlposs: print(data) lnurlpos_id = urlsafe_short_hash() lnurlpos_key = urlsafe_short_hash() @@ -60,6 +62,7 @@ async def get_lnurlposs(wallet_ids: Union[str, List[str]]) -> List[lnurlposs]: return [lnurlposs(**row) if row else None for row in rows] + async def delete_lnurlpos(lnurlpos_id: str) -> None: await db.execute("DELETE FROM lnurlpos.lnurlposs WHERE id = ?", (lnurlpos_id,)) diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index df500dc1..f05386f0 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -51,19 +51,12 @@ async def api_lnurl_response(request: Request, unique_hash): # CALLBACK - -#https://5650-2001-8a0-fa12-2900-4c13-748a-fbb9-a47f.ngrok.io/withdraw/api/v1/lnurl/cb/eJHybS8hqcBWajZM63H3FP?k1=MUaYBGrUPuAs8SLpfizmCk&pr=lnbc100n1pse2tsypp5ju0yn3w9j0n8rr3squg0knddawu2ude2cgrm6zje5f34e9jzpmlsdq8w3jhxaqxqyjw5qcqpjsp5tyhu78pamqg5zfy96kup329zt40ramc8gs2ev6jxgp66zca2348qrzjqwac3nxyg3f5mfa4ke9577c4u8kvkx8pqtdsusqdfww0aymk823x6znwa5qqzyqqqyqqqqlgqqqqppgq9q9qy9qsq66zp6pctnlmk59xwtqjga5lvqrkyccmafmn43enhhc6ugew80sanxymepshpv44m9yyhfgh8r2upvxhgk00d36rpqzfy3fxemeu4jhqp96l8hx - - @withdraw_ext.get( "/api/v1/lnurl/cb/{unique_hash}", name="withdraw.api_lnurl_callback", ) async def api_lnurl_callback( - unique_hash, - request: Request, - k1: str = Query(...), - pr: str = Query(...) + unique_hash, request: Request, k1: str = Query(...), pr: str = Query(...) ): link = await get_withdraw_link_by_hash(unique_hash) now = int(datetime.now().timestamp()) @@ -101,8 +94,8 @@ async def api_lnurl_callback( "usescsv": usescsv, } await update_withdraw_link(link.id, **changes) - - payment_request=pr + + payment_request = pr await pay_invoice( wallet_id=link.wallet, diff --git a/lnbits/wallets/lnbits.py b/lnbits/wallets/lnbits.py index e2453179..64931818 100644 --- a/lnbits/wallets/lnbits.py +++ b/lnbits/wallets/lnbits.py @@ -63,7 +63,9 @@ class LNbitsWallet(Wallet): async with httpx.AsyncClient() as client: r = await client.post( - url=f"{self.endpoint}/api/v1/payments", headers=self.key, json=data, + url=f"{self.endpoint}/api/v1/payments", + headers=self.key, + json=data, ) ok, checking_id, payment_request, error_message = ( not r.is_error, @@ -85,7 +87,7 @@ class LNbitsWallet(Wallet): r = await client.post( url=f"{self.endpoint}/api/v1/payments", headers=self.key, - json={"out": True, "bolt11": bolt11}, + json={"out": True, "bolt11": bolt11}, timeout=100, ) ok, checking_id, fee_msat, error_message = not r.is_error, None, 0, None From efec7cb8f04c73880263100defa5fbdf356c9461 Mon Sep 17 00:00:00 2001 From: Stefan Stammberger Date: Wed, 24 Nov 2021 14:31:55 +0100 Subject: [PATCH 7/8] Revert "chore: apply black to all .py files" This reverts commit 83b7779972ddff36a11784342b9926db571268b8. --- conv.py | 2 +- lnbits/core/crud.py | 4 +- lnbits/core/services.py | 3 +- lnbits/core/views/public_api.py | 1 - lnbits/extensions/livestream/models.py | 2 +- lnbits/extensions/lnaddress/__init__.py | 6 +- lnbits/extensions/lnaddress/cloudflare.py | 4 +- lnbits/extensions/lnaddress/crud.py | 53 ++++++---------- lnbits/extensions/lnaddress/lnurl.py | 26 +++----- lnbits/extensions/lnaddress/migrations.py | 2 - lnbits/extensions/lnaddress/models.py | 5 +- lnbits/extensions/lnaddress/tasks.py | 4 +- lnbits/extensions/lnaddress/views.py | 14 ++--- lnbits/extensions/lnaddress/views_api.py | 75 +++++++++-------------- lnbits/extensions/lndhub/views_api.py | 4 +- lnbits/extensions/lnurlpos/crud.py | 5 +- lnbits/extensions/withdraw/lnurl.py | 13 +++- lnbits/wallets/lnbits.py | 6 +- 18 files changed, 90 insertions(+), 139 deletions(-) diff --git a/conv.py b/conv.py index 14233f2d..bf408cfb 100644 --- a/conv.py +++ b/conv.py @@ -1,5 +1,5 @@ ## Python script to migrate an LNbits SQLite DB to Postgres -## All credits to @Fritz446 for the awesome work +## All credits to @Fritz446 for the awesome work ## pip install psycopg2 OR psycopg2-binary diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index a9f1b3a0..f69ca95b 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -278,9 +278,7 @@ async def get_payments( return [Payment.from_row(row) for row in rows] -async def delete_expired_invoices( - conn: Optional[Connection] = None, -) -> None: +async def delete_expired_invoices(conn: Optional[Connection] = None,) -> None: # first we delete all invoices older than one month await (conn or db).execute( f""" diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 2549b296..64db8d72 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -316,7 +316,8 @@ async def check_invoice_status( if not payment.pending: return status if payment.is_out and status.failed: - print(f" - deleting outgoing failed payment {payment.checking_id}: {status}") + print( + f" - deleting outgoing failed payment {payment.checking_id}: {status}") await payment.delete() elif not status.pending: print( diff --git a/lnbits/core/views/public_api.py b/lnbits/core/views/public_api.py index 6019a139..32abacb6 100644 --- a/lnbits/core/views/public_api.py +++ b/lnbits/core/views/public_api.py @@ -15,7 +15,6 @@ from ..tasks import api_invoice_listeners @core_app.get("/.well-known/lnurlp/{username}") async def lnaddress(username: str, request: Request): from lnbits.extensions.lnaddress.lnurl import lnurl_response - domain = request.client.host return await lnurl_response(username, domain) diff --git a/lnbits/extensions/livestream/models.py b/lnbits/extensions/livestream/models.py index 521909a3..1b5d4f7a 100644 --- a/lnbits/extensions/livestream/models.py +++ b/lnbits/extensions/livestream/models.py @@ -76,7 +76,7 @@ class Track(BaseModel): return None url = request.url_for("livestream.track_redirect_download", track_id=self.id) - url_with_query = f"{url}?p={payment_hash}" + url_with_query = f"{url}?p={payment_hash}" return UrlAction( url=url_with_query, diff --git a/lnbits/extensions/lnaddress/__init__.py b/lnbits/extensions/lnaddress/__init__.py index 6e8afa37..102c48ec 100644 --- a/lnbits/extensions/lnaddress/__init__.py +++ b/lnbits/extensions/lnaddress/__init__.py @@ -8,8 +8,10 @@ from lnbits.tasks import catch_everything_and_restart db = Database("ext_lnaddress") -lnaddress_ext: APIRouter = APIRouter(prefix="/lnaddress", tags=["lnaddress"]) - +lnaddress_ext: APIRouter = APIRouter( + prefix="/lnaddress", + tags=["lnaddress"] +) def lnaddress_renderer(): return template_renderer(["lnbits/extensions/lnaddress/templates"]) diff --git a/lnbits/extensions/lnaddress/cloudflare.py b/lnbits/extensions/lnaddress/cloudflare.py index e22daf95..3f3ed171 100644 --- a/lnbits/extensions/lnaddress/cloudflare.py +++ b/lnbits/extensions/lnaddress/cloudflare.py @@ -2,7 +2,9 @@ from lnbits.extensions.lnaddress.models import Domains import httpx, json -async def cloudflare_create_record(domain: Domains, ip: str): +async def cloudflare_create_record( + domain: Domains, ip: str +): url = ( "https://api.cloudflare.com/client/v4/zones/" + domain.cf_zone_id diff --git a/lnbits/extensions/lnaddress/crud.py b/lnbits/extensions/lnaddress/crud.py index 7ad3598d..36c0d54e 100644 --- a/lnbits/extensions/lnaddress/crud.py +++ b/lnbits/extensions/lnaddress/crud.py @@ -7,7 +7,9 @@ from . import db from .models import Addresses, CreateAddress, CreateDomain, Domains -async def create_domain(data: CreateDomain) -> Domains: +async def create_domain( + data: CreateDomain +) -> Domains: domain_id = urlsafe_short_hash() await db.execute( """ @@ -35,20 +37,21 @@ async def update_domain(domain_id: str, **kwargs) -> Domains: await db.execute( f"UPDATE lnaddress.domain SET {q} WHERE id = ?", (*kwargs.values(), domain_id) ) - row = await db.fetchone("SELECT * FROM lnaddress.domain WHERE id = ?", (domain_id,)) + row = await db.fetchone( + "SELECT * FROM lnaddress.domain WHERE id = ?", (domain_id,) + ) assert row, "Newly updated domain couldn't be retrieved" return Domains(**row) - async def delete_domain(domain_id: str) -> None: await db.execute("DELETE FROM lnaddress.domain WHERE id = ?", (domain_id,)) - async def get_domain(domain_id: str) -> Optional[Domains]: - row = await db.fetchone("SELECT * FROM lnaddress.domain WHERE id = ?", (domain_id,)) + row = await db.fetchone( + "SELECT * FROM lnaddress.domain WHERE id = ?", (domain_id,) + ) return Domains(**row) if row else None - async def get_domains(wallet_ids: Union[str, List[str]]) -> List[Domains]: if isinstance(wallet_ids, str): wallet_ids = [wallet_ids] @@ -60,12 +63,12 @@ async def get_domains(wallet_ids: Union[str, List[str]]) -> List[Domains]: return [Domains(**row) for row in rows] - ## ADRESSES - async def create_address( - payment_hash: str, wallet: str, data: CreateAddress + payment_hash: str, + wallet: str, + data: CreateAddress ) -> Addresses: await db.execute( """ @@ -90,7 +93,6 @@ async def create_address( assert new_address, "Newly created address couldn't be retrieved" return new_address - async def get_address(address_id: str) -> Optional[Addresses]: row = await db.fetchone( "SELECT a.* FROM lnaddress.address AS a INNER JOIN lnaddress.domain AS d ON a.id = ? AND a.domain = d.id", @@ -98,24 +100,18 @@ async def get_address(address_id: str) -> Optional[Addresses]: ) return Addresses(**row) if row else None - async def get_address_by_username(username: str, domain: str) -> Optional[Addresses]: row = await db.fetchone( "SELECT a.* FROM lnaddress.address AS a INNER JOIN lnaddress.domain AS d ON a.username = ? AND d.domain = ?", # "SELECT * FROM lnaddress.address WHERE username = ? AND domain = ?", - ( - username, - domain, - ), + (username, domain,), ) print("ADD", row) return Addresses(**row) if row else None - async def delete_address(address_id: str) -> None: await db.execute("DELETE FROM lnaddress.address WHERE id = ?", (address_id,)) - async def get_addresses(wallet_ids: Union[str, List[str]]) -> List[Addresses]: if isinstance(wallet_ids, str): wallet_ids = [wallet_ids] @@ -128,7 +124,6 @@ async def get_addresses(wallet_ids: Union[str, List[str]]) -> List[Addresses]: print([Addresses(**row) for row in rows]) return [Addresses(**row) for row in rows] - async def set_address_paid(payment_hash: str) -> Addresses: _address = await get_address(payment_hash) address = _address._asdict() @@ -147,7 +142,6 @@ async def set_address_paid(payment_hash: str) -> Addresses: assert new_address, "Newly paid address couldn't be retrieved" return new_address - async def set_address_renewed(address_id: str, duration: int): _address = await get_address(address_id) address = _address._asdict() @@ -159,10 +153,7 @@ async def set_address_renewed(address_id: str, duration: int): SET duration = ? WHERE id = ? """, - ( - extend_duration, - address_id, - ), + (extend_duration, address_id,), ) updated_address = await get_address(address_id) assert updated_address, "Renewed address couldn't be retrieved" @@ -170,21 +161,17 @@ async def set_address_renewed(address_id: str, duration: int): async def check_address_available(username: str, domain: str): - (row,) = await db.fetchone( + row, = await db.fetchone( "SELECT COUNT(username) FROM lnaddress.address WHERE username = ? AND domain = ?", - ( - username, - domain, - ), + (username, domain,), ) return row - async def purge_addresses(domain_id: str): rows = await db.fetchall( "SELECT * FROM lnaddress.address WHERE domain = ?", - (domain_id,), + (domain_id, ), ) now = datetime.now().timestamp() @@ -194,10 +181,8 @@ async def purge_addresses(domain_id: str): start = datetime.fromtimestamp(r["time"]) paid = r["paid"] - pay_expire = now > start.timestamp() + 86400 # if payment wasn't made in 1 day - expired = ( - now > (start + timedelta(days=r["duration"] + 1)).timestamp() - ) # give user 1 day to topup is address + pay_expire = now > start.timestamp() + 86400 #if payment wasn't made in 1 day + expired = now > (start + timedelta(days = r["duration"] + 1)).timestamp() #give user 1 day to topup is address if not paid and pay_expire: print("DELETE UNP_PAY_EXP", r["username"]) diff --git a/lnbits/extensions/lnaddress/lnurl.py b/lnbits/extensions/lnaddress/lnurl.py index cf7da8d4..ab043a11 100644 --- a/lnbits/extensions/lnaddress/lnurl.py +++ b/lnbits/extensions/lnaddress/lnurl.py @@ -23,15 +23,13 @@ async def lnurl_response(username: str, domain: str, request: Request): ## CHECK IF USER IS STILL VALID/PAYING now = datetime.now().timestamp() start = datetime.fromtimestamp(address.time) - expiration = (start + timedelta(days=address.duration)).timestamp() + expiration = (start + timedelta(days = address.duration)).timestamp() if now > expiration: return LnurlErrorResponse(reason="Address has expired.").dict() resp = LnurlPayResponse( - callback=request.url_for( - "lnaddress.lnurl_callback", address_id=address.id, _external=True - ), + callback=request.url_for("lnaddress.lnurl_callback", address_id=address.id, _external=True), min_sendable=1000, max_sendable=1000000000, metadata=await address.lnurlpay_metadata(), @@ -39,13 +37,14 @@ async def lnurl_response(username: str, domain: str, request: Request): return resp.dict() - @lnaddress_ext.get("/lnurl/cb/{address_id}", name="lnaddress.lnurl_callback") async def lnurl_callback(address_id, amount: int = Query(...)): address = await get_address(address_id) if not address: - return LnurlErrorResponse(reason=f"Address not found").dict() + return LnurlErrorResponse( + reason=f"Address not found" + ).dict() amount_received = amount # min = 1000 @@ -65,26 +64,17 @@ async def lnurl_callback(address_id, amount: int = Query(...)): domain = await get_domain(address.domain) - base_url = ( - address.wallet_endpoint[:-1] - if address.wallet_endpoint.endswith("/") - else address.wallet_endpoint - ) + base_url = address.wallet_endpoint[:-1] if address.wallet_endpoint.endswith('/') else address.wallet_endpoint async with httpx.AsyncClient() as client: try: call = await client.post( base_url + "/api/v1/payments", - headers={ - "X-Api-Key": address.wallet_key, - "Content-Type": "application/json", - }, + headers={"X-Api-Key": address.wallet_key, "Content-Type": "application/json"}, json={ "out": False, "amount": int(amount_received / 1000), - "description_hash": hashlib.sha256( - (await address.lnurlpay_metadata()).encode("utf-8") - ).hexdigest(), + "description_hash": hashlib.sha256((await address.lnurlpay_metadata()).encode("utf-8")).hexdigest(), "extra": {"tag": f"Payment to {address.username}@{domain.domain}"}, }, timeout=40, diff --git a/lnbits/extensions/lnaddress/migrations.py b/lnbits/extensions/lnaddress/migrations.py index a9b1137b..4c8b8be1 100644 --- a/lnbits/extensions/lnaddress/migrations.py +++ b/lnbits/extensions/lnaddress/migrations.py @@ -16,7 +16,6 @@ async def m001_initial(db): """ ) - async def m002_addresses(db): await db.execute( """ @@ -38,6 +37,5 @@ async def m002_addresses(db): """ ) - # async def m003_create_unique_indexes(db): # await db.execute("CREATE UNIQUE INDEX IF NOT EXISTS address_at_domain ON lnaddress.address (domain, username);") diff --git a/lnbits/extensions/lnaddress/models.py b/lnbits/extensions/lnaddress/models.py index 67ab07bb..e0ff9fa1 100644 --- a/lnbits/extensions/lnaddress/models.py +++ b/lnbits/extensions/lnaddress/models.py @@ -7,14 +7,13 @@ from pydantic.main import BaseModel # type: ignore class CreateDomain(BaseModel): - wallet: str = Query(...) + wallet: str = Query(...) domain: str = Query(...) cf_token: str = Query(...) cf_zone_id: str = Query(...) webhook: str = Query(None) cost: int = Query(..., ge=0) - class Domains(BaseModel): id: str wallet: str @@ -25,7 +24,6 @@ class Domains(BaseModel): cost: int time: int - class CreateAddress(BaseModel): domain: str = Query(...) username: str = Query(...) @@ -35,7 +33,6 @@ class CreateAddress(BaseModel): sats: int = Query(..., ge=0) duration: int = Query(..., ge=1) - class Addresses(BaseModel): id: str wallet: str diff --git a/lnbits/extensions/lnaddress/tasks.py b/lnbits/extensions/lnaddress/tasks.py index f962f4ab..710b84bd 100644 --- a/lnbits/extensions/lnaddress/tasks.py +++ b/lnbits/extensions/lnaddress/tasks.py @@ -52,9 +52,7 @@ async def on_invoice_paid(payment: Payment) -> None: elif "renew lnaddress" == payment.extra.get("tag"): await payment.set_pending(False) - await set_address_renewed( - address_id=payment.extra["id"], duration=payment.extra["duration"] - ) + await set_address_renewed(address_id=payment.extra["id"], duration=payment.extra["duration"]) await call_webhook_on_paid(payment.payment_hash) else: diff --git a/lnbits/extensions/lnaddress/views.py b/lnbits/extensions/lnaddress/views.py index ac24dbad..9f6542df 100644 --- a/lnbits/extensions/lnaddress/views.py +++ b/lnbits/extensions/lnaddress/views.py @@ -18,10 +18,7 @@ templates = Jinja2Templates(directory="templates") @lnaddress_ext.get("/", response_class=HTMLResponse) async def index(request: Request, user: User = Depends(check_user_exists)): - return lnaddress_renderer().TemplateResponse( - "lnaddress/index.html", {"request": request, "user": user.dict()} - ) - + return lnaddress_renderer().TemplateResponse("lnaddress/index.html", {"request": request, "user": user.dict()}) @lnaddress_ext.get("/{domain_id}") async def display(domain_id, request: Request): @@ -36,12 +33,11 @@ async def display(domain_id, request: Request): wallet = await get_wallet(domain.wallet) return lnaddress_renderer().TemplateResponse( - "lnaddress/display.html", - { + "lnaddress/display.html",{ "request": request, - "domain_id": domain.id, + "domain_id":domain.id, "domain_domain": domain.domain, "domain_cost": domain.cost, - "domain_wallet_inkey": wallet.inkey, - }, + "domain_wallet_inkey": wallet.inkey + } ) diff --git a/lnbits/extensions/lnaddress/views_api.py b/lnbits/extensions/lnaddress/views_api.py index a32cf499..38cd723e 100644 --- a/lnbits/extensions/lnaddress/views_api.py +++ b/lnbits/extensions/lnaddress/views_api.py @@ -43,12 +43,7 @@ async def api_domains( @lnaddress_ext.post("/api/v1/domains") @lnaddress_ext.put("/api/v1/domains/{domain_id}") -async def api_domain_create( - request: Request, - data: CreateDomain, - domain_id=None, - g: WalletTypeInfo = Depends(get_key_type), -): +async def api_domain_create(request: Request,data: CreateDomain, domain_id=None, g: WalletTypeInfo = Depends(get_key_type)): if domain_id: domain = await get_domain(domain_id) @@ -69,7 +64,7 @@ async def api_domain_create( domain = await create_domain(data=data) root_url = urlparse(request.url.path).netloc - # root_url = request.url_root + #root_url = request.url_root cf_response = await cloudflare_create_record( domain=domain, @@ -80,36 +75,32 @@ async def api_domain_create( await delete_domain(domain.id) raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, - detail="Problem with cloudflare: " - + cf_response["errors"][0]["message"], + detail="Problem with cloudflare: " + cf_response["errors"][0]["message"], ) return domain.dict() - @lnaddress_ext.delete("/api/v1/domains/{domain_id}") async def api_domain_delete(domain_id, g: WalletTypeInfo = Depends(get_key_type)): domain = await get_domain(domain_id) if not domain: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, - detail="Domain does not exist.", - ) + status_code=HTTPStatus.NOT_FOUND, + detail="Domain does not exist.", + ) if domain.wallet != g.wallet.id: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="Not your domain", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="Not your domain", + ) await delete_domain(domain_id) raise HTTPException(status_code=HTTPStatus.NO_CONTENT) - # ADDRESSES - @lnaddress_ext.get("/api/v1/addresses") async def api_addresses( g: WalletTypeInfo = Depends(get_key_type), @@ -122,7 +113,6 @@ async def api_addresses( return [address.dict() for address in await get_addresses(wallet_ids)] - @lnaddress_ext.get("/api/v1/address/{domain}/{username}/{wallet_key}") async def api_get_user_info(username, wallet_key, domain): address = await get_address_by_username(username, domain) @@ -135,43 +125,39 @@ async def api_get_user_info(username, wallet_key, domain): if address.wallet_key != wallet_key: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="Incorrect user/wallet information.", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="Incorrect user/wallet information.", + ) return address.dict() - @lnaddress_ext.get("/api/v1/address/availabity/{domain_id}/{username}") async def api_check_available_username(domain_id, username): used_username = await check_address_available(username, domain_id) return used_username - @lnaddress_ext.post("/api/v1/address/{domain_id}") @lnaddress_ext.put("/api/v1/address/{domain_id}/{user}/{wallet_key}") -async def api_lnaddress_make_address( - domain_id, data: CreateAddress, user=None, wallet_key=None -): +async def api_lnaddress_make_address(domain_id, data: CreateAddress, user=None, wallet_key=None): domain = await get_domain(domain_id) # If the request is coming for the non-existant domain if not domain: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="The domain does not exist.", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="The domain does not exist.", + ) domain_cost = domain[6] sats = data.sats ## FAILSAFE FOR CREATING ADDRESSES BY API - if domain_cost * data.duration != data.sats: + if(domain_cost * data.duration != data.sats): raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="The amount is not correct. Either 'duration', or 'sats' are wrong.", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="The amount is not correct. Either 'duration', or 'sats' are wrong.", + ) if user: print("USER", user, domain.domain) @@ -197,7 +183,7 @@ async def api_lnaddress_make_address( extra={ "tag": "renew lnaddress", "id": address.id, - "duration": data.duration, + "duration": data.duration }, ) @@ -229,7 +215,7 @@ async def api_lnaddress_make_address( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e), ) - + address = await create_address( payment_hash=payment_hash, wallet=domain.wallet, data=data ) @@ -242,7 +228,6 @@ async def api_lnaddress_make_address( return {"payment_hash": payment_hash, "payment_request": payment_request} - @lnaddress_ext.get("/api/v1/addresses/{payment_hash}") async def api_address_send_address(payment_hash): address = await get_address(payment_hash) @@ -251,27 +236,27 @@ async def api_address_send_address(payment_hash): status = await check_invoice_status(domain.wallet, payment_hash) is_paid = not status.pending except Exception as e: - return {"paid": False, "error": str(e)} + return {"paid": False, 'error': str(e)} if is_paid: return {"paid": True} return {"paid": False} - @lnaddress_ext.delete("/api/v1/addresses/{address_id}") async def api_address_delete(address_id, g: WalletTypeInfo = Depends(get_key_type)): address = await get_address(address_id) if not address: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, - detail="Address does not exist.", - ) + status_code=HTTPStatus.NOT_FOUND, + detail="Address does not exist.", + ) if address.wallet != g.wallet.id: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, - detail="Not your address.", - ) + status_code=HTTPStatus.FORBIDDEN, + detail="Not your address.", + ) await delete_address(address_id) raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + \ No newline at end of file diff --git a/lnbits/extensions/lndhub/views_api.py b/lnbits/extensions/lndhub/views_api.py index fdda1356..d28bbd9e 100644 --- a/lnbits/extensions/lndhub/views_api.py +++ b/lnbits/extensions/lndhub/views_api.py @@ -108,9 +108,7 @@ async def lndhub_payinvoice( @lndhub_ext.get("/ext/balance") -async def lndhub_balance( - wallet: WalletTypeInfo = Depends(check_wallet), -): +async def lndhub_balance(wallet: WalletTypeInfo = Depends(check_wallet),): return {"BTC": {"AvailableBalance": wallet.wallet.balance}} diff --git a/lnbits/extensions/lnurlpos/crud.py b/lnbits/extensions/lnurlpos/crud.py index 55f8bec5..a2ca88c5 100644 --- a/lnbits/extensions/lnurlpos/crud.py +++ b/lnbits/extensions/lnurlpos/crud.py @@ -8,9 +8,7 @@ from .models import lnurlposs, lnurlpospayment, createLnurlpos ###############lnurlposS########################## -async def create_lnurlpos( - data: createLnurlpos, -) -> lnurlposs: +async def create_lnurlpos(data: createLnurlpos,) -> lnurlposs: print(data) lnurlpos_id = urlsafe_short_hash() lnurlpos_key = urlsafe_short_hash() @@ -62,7 +60,6 @@ async def get_lnurlposs(wallet_ids: Union[str, List[str]]) -> List[lnurlposs]: return [lnurlposs(**row) if row else None for row in rows] - async def delete_lnurlpos(lnurlpos_id: str) -> None: await db.execute("DELETE FROM lnurlpos.lnurlposs WHERE id = ?", (lnurlpos_id,)) diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index f05386f0..df500dc1 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -51,12 +51,19 @@ async def api_lnurl_response(request: Request, unique_hash): # CALLBACK + +#https://5650-2001-8a0-fa12-2900-4c13-748a-fbb9-a47f.ngrok.io/withdraw/api/v1/lnurl/cb/eJHybS8hqcBWajZM63H3FP?k1=MUaYBGrUPuAs8SLpfizmCk&pr=lnbc100n1pse2tsypp5ju0yn3w9j0n8rr3squg0knddawu2ude2cgrm6zje5f34e9jzpmlsdq8w3jhxaqxqyjw5qcqpjsp5tyhu78pamqg5zfy96kup329zt40ramc8gs2ev6jxgp66zca2348qrzjqwac3nxyg3f5mfa4ke9577c4u8kvkx8pqtdsusqdfww0aymk823x6znwa5qqzyqqqyqqqqlgqqqqppgq9q9qy9qsq66zp6pctnlmk59xwtqjga5lvqrkyccmafmn43enhhc6ugew80sanxymepshpv44m9yyhfgh8r2upvxhgk00d36rpqzfy3fxemeu4jhqp96l8hx + + @withdraw_ext.get( "/api/v1/lnurl/cb/{unique_hash}", name="withdraw.api_lnurl_callback", ) async def api_lnurl_callback( - unique_hash, request: Request, k1: str = Query(...), pr: str = Query(...) + unique_hash, + request: Request, + k1: str = Query(...), + pr: str = Query(...) ): link = await get_withdraw_link_by_hash(unique_hash) now = int(datetime.now().timestamp()) @@ -94,8 +101,8 @@ async def api_lnurl_callback( "usescsv": usescsv, } await update_withdraw_link(link.id, **changes) - - payment_request = pr + + payment_request=pr await pay_invoice( wallet_id=link.wallet, diff --git a/lnbits/wallets/lnbits.py b/lnbits/wallets/lnbits.py index 64931818..e2453179 100644 --- a/lnbits/wallets/lnbits.py +++ b/lnbits/wallets/lnbits.py @@ -63,9 +63,7 @@ class LNbitsWallet(Wallet): async with httpx.AsyncClient() as client: r = await client.post( - url=f"{self.endpoint}/api/v1/payments", - headers=self.key, - json=data, + url=f"{self.endpoint}/api/v1/payments", headers=self.key, json=data, ) ok, checking_id, payment_request, error_message = ( not r.is_error, @@ -87,7 +85,7 @@ class LNbitsWallet(Wallet): r = await client.post( url=f"{self.endpoint}/api/v1/payments", headers=self.key, - json={"out": True, "bolt11": bolt11}, + json={"out": True, "bolt11": bolt11}, timeout=100, ) ok, checking_id, fee_msat, error_message = not r.is_error, None, 0, None From 3831bca9e2dbb739f91d8f4ec276e777733380a0 Mon Sep 17 00:00:00 2001 From: Stefan Stammberger Date: Sat, 27 Nov 2021 10:13:32 +0100 Subject: [PATCH 8/8] fix: cleanup core/views/generic.py * Fixed withdraw endpoint query params access was: request.query_params.get("usr") corrected: request.query_params.get("usr") * Added missing response classes * Removed old comments and commeted out code * Deleted the unused auth_bearer.py file --- lnbits/auth_bearer.py | 51 ------------------------------ lnbits/core/views/generic.py | 61 +++++++++++++++++++----------------- 2 files changed, 32 insertions(+), 80 deletions(-) delete mode 100644 lnbits/auth_bearer.py diff --git a/lnbits/auth_bearer.py b/lnbits/auth_bearer.py deleted file mode 100644 index 163785dd..00000000 --- a/lnbits/auth_bearer.py +++ /dev/null @@ -1,51 +0,0 @@ -from fastapi import Request, HTTPException -from fastapi.security.api_key import APIKeyQuery, APIKeyCookie, APIKeyHeader, APIKey - -# https://medium.com/data-rebels/fastapi-authentication-revisited-enabling-api-key-authentication-122dc5975680 - -from fastapi import Security, Depends, FastAPI, HTTPException -from fastapi.security.api_key import APIKeyQuery, APIKeyCookie, APIKeyHeader, APIKey -from fastapi.security.base import SecurityBase - - -API_KEY = "usr" -API_KEY_NAME = "X-API-key" - -api_key_query = APIKeyQuery(name=API_KEY_NAME, auto_error=False) -api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False) - - -class AuthBearer(SecurityBase): - def __init__(self, scheme_name: str = None, auto_error: bool = True): - self.scheme_name = scheme_name or self.__class__.__name__ - self.auto_error = auto_error - - async def __call__(self, request: Request): - key = await self.get_api_key() - print(key) - # credentials: HTTPAuthorizationCredentials = await super(AuthBearer, self).__call__(request) - # if credentials: - # if not credentials.scheme == "Bearer": - # raise HTTPException( - # status_code=403, detail="Invalid authentication scheme.") - # if not self.verify_jwt(credentials.credentials): - # raise HTTPException( - # status_code=403, detail="Invalid token or expired token.") - # return credentials.credentials - # else: - # raise HTTPException( - # status_code=403, detail="Invalid authorization code.") - - async def get_api_key( - self, - api_key_query: str = Security(api_key_query), - api_key_header: str = Security(api_key_header), - ): - if api_key_query == API_KEY: - return api_key_query - elif api_key_header == API_KEY: - return api_key_header - else: - raise HTTPException( - status_code=403, detail="Could not validate credentials" - ) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 5e0ededf..5dbe92a7 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -4,18 +4,16 @@ from typing import Optional from fastapi import Request, status from fastapi.exceptions import HTTPException -from fastapi.param_functions import Body from fastapi.params import Depends, Query from fastapi.responses import FileResponse, RedirectResponse from fastapi.routing import APIRouter from pydantic.types import UUID4 -from starlette.responses import HTMLResponse +from starlette.responses import HTMLResponse, JSONResponse from lnbits.core import db from lnbits.core.models import User from lnbits.decorators import check_user_exists from lnbits.helpers import template_renderer, url_for -from lnbits.requestvars import g from lnbits.settings import LNBITS_ALLOWED_USERS, LNBITS_SITE_TITLE, SERVICE_FEE from ..crud import ( @@ -32,7 +30,7 @@ from ..services import pay_invoice, redeem_lnurl_withdraw core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"]) -@core_html_routes.get("/favicon.ico") +@core_html_routes.get("/favicon.ico", response_class=FileResponse) async def favicon(): return FileResponse("lnbits/core/static/favicon.ico") @@ -44,7 +42,11 @@ async def home(request: Request, lightning: str = None): ) -@core_html_routes.get("/extensions", name="core.extensions") +@core_html_routes.get( + "/extensions", + name="core.extensions", + response_class=HTMLResponse, +) async def extensions( request: Request, user: User = Depends(check_user_exists), @@ -77,9 +79,19 @@ async def extensions( ) -@core_html_routes.get("/wallet", response_class=HTMLResponse) -# Not sure how to validate -# @validate_uuids(["usr", "nme"]) +@core_html_routes.get( + "/wallet", + response_class=HTMLResponse, + description=""" +Args: + +just **wallet_name**: create a new user, then create a new wallet for user with wallet_name
+just **user_id**: return the first user wallet or create one if none found (with default wallet_name)
+**user_id** and **wallet_name**: create a new wallet for user with wallet_name
+**user_id** and **wallet_id**: return that wallet if user is the owner
+nothing: create everything
+""", +) async def wallet( request: Request = Query(None), nme: Optional[str] = Query(None), @@ -91,12 +103,6 @@ async def wallet( wallet_name = nme service_fee = int(SERVICE_FEE) if int(SERVICE_FEE) == SERVICE_FEE else SERVICE_FEE - # just wallet_name: create a new user, then create a new wallet for user with wallet_name - # just user_id: return the first user wallet or create one if none found (with default wallet_name) - # user_id and wallet_name: create a new wallet for user with wallet_name - # user_id and wallet_id: return that wallet if user is the owner - # nothing: create everything - if not user_id: user = await get_user((await create_account()).id) else: @@ -137,14 +143,13 @@ async def wallet( ) -@core_html_routes.get("/withdraw") -# @validate_uuids(["usr", "wal"], required=True) +@core_html_routes.get("/withdraw", response_class=JSONResponse) async def lnurl_full_withdraw(request: Request): - user = await get_user(request.args.get("usr")) + user = await get_user(request.query_params.get("usr")) if not user: return {"status": "ERROR", "reason": "User does not exist."} - wallet = user.get_wallet(request.args.get("wal")) + wallet = user.get_wallet(request.query_params.get("wal")) if not wallet: return {"status": "ERROR", "reason": "Wallet does not exist."} @@ -159,18 +164,17 @@ async def lnurl_full_withdraw(request: Request): } -@core_html_routes.get("/withdraw/cb") -# @validate_uuids(["usr", "wal"], required=True) +@core_html_routes.get("/withdraw/cb", response_class=JSONResponse) async def lnurl_full_withdraw_callback(request: Request): - user = await get_user(request.args.get("usr")) + user = await get_user(request.query_params.get("usr")) if not user: return {"status": "ERROR", "reason": "User does not exist."} - wallet = user.get_wallet(request.args.get("wal")) + wallet = user.get_wallet(request.query_params.get("wal")) if not wallet: return {"status": "ERROR", "reason": "Wallet does not exist."} - pr = request.args.get("pr") + pr = request.query_params.get("pr") async def pay(): try: @@ -180,14 +184,14 @@ async def lnurl_full_withdraw_callback(request: Request): asyncio.create_task(pay()) - balance_notify = request.args.get("balanceNotify") + balance_notify = request.query_params.get("balanceNotify") if balance_notify: await save_balance_notify(wallet.id, balance_notify) return {"status": "OK"} -@core_html_routes.get("/deletewallet") +@core_html_routes.get("/deletewallet", response_class=RedirectResponse) async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): user = await get_user(usr) user_wallet_ids = [u.id for u in user.wallets] @@ -211,14 +215,13 @@ async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query @core_html_routes.get("/withdraw/notify/{service}") -# @validate_uuids(["wal"], required=True) async def lnurl_balance_notify(request: Request, service: str): - bc = await get_balance_check(request.args.get("wal"), service) + bc = await get_balance_check(request.query_params.get("wal"), service) if bc: redeem_lnurl_withdraw(bc.wallet, bc.url) -@core_html_routes.get("/lnurlwallet") +@core_html_routes.get("/lnurlwallet", response_class=RedirectResponse) async def lnurlwallet(request: Request): async with db.connect() as conn: account = await create_account(conn=conn) @@ -228,7 +231,7 @@ async def lnurlwallet(request: Request): asyncio.create_task( redeem_lnurl_withdraw( wallet.id, - request.args.get("lightning"), + request.query_params.get("lightning"), "LNbits initial funding: voucher redeem.", {"tag": "lnurlwallet"}, 5, # wait 5 seconds before sending the invoice to the service