From 83a000d7c9795b81fbe88f9d444c27bc0d20ad40 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 13:06:19 +0200 Subject: [PATCH 01/23] fix up mypy.ini, and properly ignore grpc and extension --- mypy.ini | 7 ++++--- requirements.txt | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mypy.ini b/mypy.ini index 735f89e0..e5a974b5 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,7 +1,8 @@ [mypy] ignore_missing_imports = True -exclude = lnbits/wallets/lnd_grpc_files/ -exclude = lnbits/extensions/ - +exclude = (?x)( + ^lnbits/extensions. + | ^lnbits/wallets/lnd_grpc_files. + ) [mypy-lnbits.wallets.lnd_grpc_files.*] follow_imports = skip diff --git a/requirements.txt b/requirements.txt index 8359456f..322712f5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,9 +43,10 @@ sqlalchemy==1.3.23 sqlalchemy-aio==0.16.0 sse-starlette==0.6.2 starlette==0.14.2 +types-protobuf==3.19.22 typing-extensions==3.10.0.2 uvicorn==0.15.0 uvloop==0.16.0 watchgod==0.7 websockets==10.0 -zipp==3.5.0 \ No newline at end of file +zipp==3.5.0 From 45a13fd807433debcc8c83915b2100aef555bb0a Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 13:36:44 +0200 Subject: [PATCH 02/23] remove commented out and unused code in app object, it is now used in lnbits/__main__.py directly, fixes mypy error --- lnbits/app.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index a7c8fdaf..eaf8a9db 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -17,7 +17,6 @@ from loguru import logger import lnbits.settings from lnbits.core.tasks import register_task_listeners -from .commands import db_migrate, handle_assets from .core import core_app from .core.views.generic import core_html_routes from .helpers import ( @@ -84,7 +83,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: check_funding_source(app) register_assets(app) register_routes(app) - # register_commands(app) register_async_tasks(app) register_exception_handlers(app) @@ -137,12 +135,6 @@ def register_routes(app: FastAPI) -> None: ) -def register_commands(app: FastAPI): - """Register Click commands.""" - app.cli.add_command(db_migrate) - app.cli.add_command(handle_assets) - - def register_assets(app: FastAPI): """Serve each vendored asset separately or a bundle.""" From 8901ff8084ae3638f95c5616086ccbdcf8b175d8 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 18:51:35 +0200 Subject: [PATCH 03/23] some more mypy fixes --- lnbits/core/crud.py | 2 +- lnbits/core/models.py | 2 ++ lnbits/core/views/api.py | 15 +++++++++------ lnbits/core/views/generic.py | 6 +++++- lnbits/helpers.py | 2 +- lnbits/tasks.py | 2 +- lnbits/utils/exchange_rates.py | 8 ++++---- lnbits/wallets/lnpay.py | 2 +- lnbits/wallets/opennode.py | 2 +- 9 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 770e2906..db802d7b 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -113,7 +113,7 @@ async def create_wallet( async def update_wallet( wallet_id: str, new_name: str, conn: Optional[Connection] = None ) -> Optional[Wallet]: - await (conn or db).execute( + return await (conn or db).execute( """ UPDATE wallets SET name = ? diff --git a/lnbits/core/models.py b/lnbits/core/models.py index ab73b702..0f7eba73 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -106,6 +106,8 @@ class Payment(BaseModel): @property def tag(self) -> Optional[str]: + if self.extra is None: + return "" return self.extra.get("tag") @property diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 9fee6063..bd15ee8d 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -3,7 +3,7 @@ import hashlib import json from binascii import unhexlify from http import HTTPStatus -from typing import Dict, List, Optional, Union +from typing import Dict, List, Optional, Union, Tuple from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx @@ -185,7 +185,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet): assert ( data.lnurl_balance_check is not None ), "lnurl_balance_check is required" - save_balance_check(wallet.id, data.lnurl_balance_check) + await save_balance_check(wallet.id, data.lnurl_balance_check) async with httpx.AsyncClient() as client: try: @@ -291,7 +291,7 @@ async def api_payments_pay_lnurl( timeout=40, ) if r.is_error: - raise httpx.ConnectError + raise httpx.ConnectError("LNURL Callback Connection Error") except (httpx.ConnectError, httpx.RequestError): raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, @@ -348,7 +348,7 @@ async def subscribe(request: Request, wallet: Wallet): logger.debug("adding sse listener", payment_queue) api_invoice_listeners.append(payment_queue) - send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0) + send_queue: asyncio.Queue[Tuple[str, Payment]] = asyncio.Queue(0) async def payment_received() -> None: while True: @@ -389,10 +389,13 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)): # If a valid key is given, we also return the field "details", otherwise not wallet = None try: + assert X_Api_Key is not None + # TODO: type above is Optional[str] how can that have .extra? if X_Api_Key.extra: logger.warning("No key") except: - wallet = await get_wallet_for_key(X_Api_Key) + if X_Api_Key is not None: + wallet = await get_wallet_for_key(X_Api_Key) payment = await get_standalone_payment( payment_hash, wallet_id=wallet.id if wallet else None ) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order @@ -606,7 +609,7 @@ class ConversionData(BaseModel): async def api_fiat_as_sats(data: ConversionData): output = {} if data.from_ == "sat": - output["sats"] = int(data.amount) + output["sats"] = data.amount output["BTC"] = data.amount / 100000000 for currency in data.to.split(","): output[currency.strip().upper()] = await satoshis_amount_as_fiat( diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 4366028d..d75aa77e 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -112,9 +112,11 @@ async def wallet( if not user_id: user = await get_user((await create_account()).id) + assert user is not None logger.info(f"Created new account for user {user.id}") else: user = await get_user(user_id) + assert user is not None if not user: return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "User does not exist."} @@ -209,6 +211,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): user = await get_user(usr) + assert user is not None user_wallet_ids = [u.id for u in user.wallets] if wal not in user_wallet_ids: @@ -233,7 +236,7 @@ async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query async def lnurl_balance_notify(request: Request, service: str): bc = await get_balance_check(request.query_params.get("wal"), service) if bc: - redeem_lnurl_withdraw(bc.wallet, bc.url) + await redeem_lnurl_withdraw(bc.wallet, bc.url) @core_html_routes.get( @@ -243,6 +246,7 @@ async def lnurlwallet(request: Request): async with db.connect() as conn: account = await create_account(conn=conn) user = await get_user(account.id, conn=conn) + assert user is not None wallet = await create_wallet(user_id=user.id, conn=conn) asyncio.create_task( diff --git a/lnbits/helpers.py b/lnbits/helpers.py index 71b3dd69..e97fc7bb 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -34,7 +34,7 @@ class ExtensionManager: @property def extensions(self) -> List[Extension]: - output = [] + output: List[Extension] = [] if "all" in self._disabled: return output diff --git a/lnbits/tasks.py b/lnbits/tasks.py index 86863f98..f4d0a928 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -66,7 +66,7 @@ async def webhook_handler(): raise HTTPException(status_code=HTTPStatus.NO_CONTENT) -internal_invoice_queue = asyncio.Queue(0) +internal_invoice_queue: asyncio.Queue = asyncio.Queue(0) async def internal_invoice_listener(): diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py index 0432b364..fbb4add2 100644 --- a/lnbits/utils/exchange_rates.py +++ b/lnbits/utils/exchange_rates.py @@ -1,5 +1,5 @@ import asyncio -from typing import Callable, NamedTuple +from typing import Callable, NamedTuple, List import httpx from loguru import logger @@ -227,10 +227,10 @@ async def btc_price(currency: str) -> float: "TO": currency.upper(), "to": currency.lower(), } - rates = [] - tasks = [] + rates: List[float] = [] + tasks: List[asyncio.Task] = [] - send_channel = asyncio.Queue() + send_channel: asyncio.Queue = asyncio.Queue() async def controller(): failures = 0 diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py index 807d7253..2ff1afa9 100644 --- a/lnbits/wallets/lnpay.py +++ b/lnbits/wallets/lnpay.py @@ -119,7 +119,7 @@ class LNPayWallet(Wallet): return PaymentStatus(statuses[r.json()["settled"]]) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: - self.queue = asyncio.Queue(0) + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value = await self.queue.get() yield value diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py index 6d3fb02c..9cd05ebd 100644 --- a/lnbits/wallets/opennode.py +++ b/lnbits/wallets/opennode.py @@ -127,7 +127,7 @@ class OpenNodeWallet(Wallet): return PaymentStatus(statuses[r.json()["data"]["status"]]) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: - self.queue = asyncio.Queue(0) + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value = await self.queue.get() yield value From 91c5a68cccb254665318adbf128f36e39dda1292 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 19:20:20 +0200 Subject: [PATCH 04/23] mypy fakewallet, macaroon, bolt11 invoice --- lnbits/bolt11.py | 1 + lnbits/wallets/fake.py | 10 +++++++--- lnbits/wallets/macaroon/macaroon.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lnbits/bolt11.py b/lnbits/bolt11.py index cc841585..cc8c6040 100644 --- a/lnbits/bolt11.py +++ b/lnbits/bolt11.py @@ -23,6 +23,7 @@ class Route(NamedTuple): class Invoice(object): payment_hash: str + checking_id: Optional[str] = None amount_msat: int = 0 description: Optional[str] = None description_hash: Optional[str] = None diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index 3859d33c..c2b2307a 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -28,7 +28,7 @@ class FakeWallet(Wallet): logger.info( "FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr." ) - return StatusResponse(None, float("inf")) + return StatusResponse(None, 1000000000) async def create_invoice( self, @@ -80,8 +80,12 @@ class FakeWallet(Wallet): async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: invoice = decode(bolt11) + # TODO: no data here? + data: Dict = { + "privkey": "missing" + } if ( - hasattr(invoice, "checking_id") + invoice.checking_id is not None and invoice.checking_id[6:] == data["privkey"][:6] ): return PaymentResponse(True, invoice.payment_hash, 0) @@ -97,7 +101,7 @@ class FakeWallet(Wallet): return PaymentStatus(None) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: - self.queue = asyncio.Queue(0) + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value = await self.queue.get() yield value diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py index 2183dacb..aa00cd3e 100644 --- a/lnbits/wallets/macaroon/macaroon.py +++ b/lnbits/wallets/macaroon/macaroon.py @@ -73,7 +73,7 @@ class AESCipher(object): final_key += key return final_key[:output] - def decrypt(self, encrypted: str) -> str: + def decrypt(self, encrypted) -> str: """Decrypts a string using AES-256-CBC.""" passphrase = self.passphrase encrypted = base64.b64decode(encrypted) From 886c74f205ebb725ec98729a956f5a5bab65c331 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 08:43:10 +0200 Subject: [PATCH 05/23] assert Optional[str] --- lnbits/core/tasks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index 5fea769d..8234b5fc 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -55,6 +55,7 @@ async def dispatch_webhook(payment: Payment): data = payment.dict() try: logger.debug("sending webhook", payment.webhook) + assert payment.webhook is not None r = await client.post(payment.webhook, json=data, timeout=40) await mark_webhook_sent(payment, r.status_code) except (httpx.ConnectError, httpx.RequestError): From 045212a0701157c252f6425d8b50c48f2191b5d9 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 09:36:13 +0200 Subject: [PATCH 06/23] mypy fixes for generic.py, decurators.py, eclair.py --- lnbits/core/views/generic.py | 16 +++++++-------- lnbits/decorators.py | 40 ++++++++++++++++++++---------------- lnbits/wallets/eclair.py | 4 +++- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index d75aa77e..7ecedcf8 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -54,9 +54,9 @@ async def home(request: Request, lightning: str = None): ) async def extensions( request: Request, - user: User = Depends(check_user_exists), - enable: str = Query(None), - disable: str = Query(None), + user = Depends(check_user_exists), + enable = Query(None), + disable = Query(None), ): extension_to_enable = enable extension_to_disable = disable @@ -100,10 +100,10 @@ nothing: create everything
""", ) async def wallet( - request: Request = Query(None), - nme: Optional[str] = Query(None), - usr: Optional[UUID4] = Query(None), - wal: Optional[UUID4] = Query(None), + request = Query(None), + nme = Query(None), + usr = Query(None), + wal = Query(None), ): user_id = usr.hex if usr else None wallet_id = wal.hex if wal else None @@ -209,7 +209,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) -async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): +async def deletewallet(request: Request, wal = Query(...), usr = Query(...)): user = await get_user(usr) assert user is not None user_wallet_ids = [u.id for u in user.wallets] diff --git a/lnbits/decorators.py b/lnbits/decorators.py index e65b9041..77fe3227 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,5 +1,7 @@ from http import HTTPStatus +from typing import Optional + from cerberus import Validator # type: ignore from fastapi import status from fastapi.exceptions import HTTPException @@ -29,20 +31,20 @@ class KeyChecker(SecurityBase): self._key_type = "invoice" self._api_key = api_key if api_key: - self.model: APIKey = APIKey( + key = APIKey( **{"in": APIKeyIn.query}, name="X-API-KEY", description="Wallet API Key - QUERY", ) else: - self.model: APIKey = APIKey( + key = APIKey( **{"in": APIKeyIn.header}, name="X-API-KEY", description="Wallet API Key - HEADER", ) - self.wallet = None + self.model: APIKey = key - async def __call__(self, request: Request) -> Wallet: + async def __call__(self, request: Request): try: key_value = ( self._api_key @@ -52,12 +54,13 @@ class KeyChecker(SecurityBase): # FIXME: Find another way to validate the key. A fetch from DB should be avoided here. # Also, we should not return the wallet here - thats silly. # Possibly store it in a Redis DB - self.wallet = await get_wallet_for_key(key_value, self._key_type) - if not self.wallet: + wallet = await get_wallet_for_key(key_value, self._key_type) + if not wallet: raise HTTPException( status_code=HTTPStatus.UNAUTHORIZED, detail="Invalid key or expired key.", ) + self.wallet = wallet except KeyError: raise HTTPException( @@ -120,8 +123,8 @@ api_key_query = APIKeyQuery( async def get_key_type( r: Request, - api_key_header: str = Security(api_key_header), - api_key_query: str = Security(api_key_query), + api_key_header = Security(api_key_header), + api_key_query = Security(api_key_query), ) -> WalletTypeInfo: # 0: admin # 1: invoice @@ -134,9 +137,10 @@ async def get_key_type( token = api_key_header if api_key_header else api_key_query try: - checker = WalletAdminKeyChecker(api_key=token) - await checker.__call__(r) - wallet = WalletTypeInfo(0, checker.wallet) + admin_checker = WalletAdminKeyChecker(api_key=token) + await admin_checker.__call__(r) + wallet = WalletTypeInfo(0, admin_checker.wallet) + assert wallet.wallet is not None if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): @@ -153,9 +157,9 @@ async def get_key_type( raise try: - checker = WalletInvoiceKeyChecker(api_key=token) - await checker.__call__(r) - wallet = WalletTypeInfo(1, checker.wallet) + invoice_checker = WalletInvoiceKeyChecker(api_key=token) + await invoice_checker.__call__(r) + wallet = WalletTypeInfo(1, invoice_checker.wallet) if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): @@ -174,8 +178,8 @@ async def get_key_type( async def require_admin_key( r: Request, - api_key_header: str = Security(api_key_header), - api_key_query: str = Security(api_key_query), + api_key_header = Security(api_key_header), + api_key_query = Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query @@ -193,8 +197,8 @@ async def require_admin_key( async def require_invoice_key( r: Request, - api_key_header: str = Security(api_key_header), - api_key_query: str = Security(api_key_query), + api_key_header = Security(api_key_header), + api_key_query = Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index 0ac3fd2a..bad707ff 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -7,7 +7,9 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger -from websockets import connect + +# mypy https://github.com/aaugustin/websockets/issues/940 +from websockets.client import connect from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From e1d1ce7edca9a26a4c956f82031f81f54f98ccbe Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 09:45:08 +0200 Subject: [PATCH 07/23] fix PaymentKwargs class for mypy --- lnbits/core/services.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 0b565ebb..f4f96250 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -109,18 +109,15 @@ async def pay_invoice( 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( + class PaymentKwargs(TypedDict): + wallet_id: str + payment_request: str + payment_hash: str + amount: int + memo: str + extra: Optional[Dict] + + payment_kwargs: PaymentKwargs = PaymentKwargs( wallet_id=wallet_id, payment_request=payment_request, payment_hash=invoice.payment_hash, From 68a195952ada90895c11b5f146e643605f2366bf Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 10:05:30 +0200 Subject: [PATCH 08/23] mypy fixes, api, eclair --- lnbits/core/views/api.py | 4 ++-- lnbits/wallets/eclair.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index bd15ee8d..09eb4f90 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -181,7 +181,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet): lnurl_response: Union[None, bool, str] = None if data.lnurl_callback: - if "lnurl_balance_check" in data: + if hasattr(data, "lnurl_balance_check"): assert ( data.lnurl_balance_check is not None ), "lnurl_balance_check is required" @@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData: CreateInvoiceData = Body(...), + invoiceData = Body(...), ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index bad707ff..b669f8b7 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -9,7 +9,8 @@ import httpx from loguru import logger # mypy https://github.com/aaugustin/websockets/issues/940 -from websockets.client import connect +# from websockets.client import connect +from websockets import connect from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From e96944a4fae70dbb8213cb36a63d6eebe1257b3f Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 10:24:40 +0200 Subject: [PATCH 09/23] fully fix decorators, thanks calle --- lnbits/decorators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 77fe3227..b74230ef 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -171,9 +171,10 @@ async def get_key_type( if e.status_code == HTTPStatus.BAD_REQUEST: raise if e.status_code == HTTPStatus.UNAUTHORIZED: - return WalletTypeInfo(2, None) + return WalletTypeInfo(2, Wallet()) except: raise + return wallet async def require_admin_key( From 8dbf64a0d074657eed7517ca085181a3bf867657 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 10:34:01 +0200 Subject: [PATCH 10/23] ignore eclair websockets type --- lnbits/wallets/eclair.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index b669f8b7..b43d90c1 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -8,9 +8,10 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger +from websockets import connect # type: ignore +# TODO: https://github.com/lnbits/lnbits-legend/issues/764 # mypy https://github.com/aaugustin/websockets/issues/940 -# from websockets.client import connect -from websockets import connect + from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From 56b4b0cf435ae796c641b867162328b83f55b65b Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 11:21:38 +0200 Subject: [PATCH 11/23] fix last views api bug, thanks calle --- lnbits/core/views/api.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 09eb4f90..6766ad8d 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -387,19 +387,13 @@ async def api_payments_sse( async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)): # We use X_Api_Key here because we want this call to work with and without keys # If a valid key is given, we also return the field "details", otherwise not - wallet = None - try: - assert X_Api_Key is not None - # TODO: type above is Optional[str] how can that have .extra? - if X_Api_Key.extra: - logger.warning("No key") - except: - if X_Api_Key is not None: - wallet = await get_wallet_for_key(X_Api_Key) + wallet = await get_wallet_for_key(X_Api_Key) if type(X_Api_Key) == str else None + + # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order + # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results payment = await get_standalone_payment( payment_hash, wallet_id=wallet.id if wallet else None - ) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order - # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results + ) if payment is None: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist." From 64502c355eff38dd6ba43da4c7a09b055397bba0 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 11:33:37 +0200 Subject: [PATCH 12/23] black formating --- lnbits/core/views/api.py | 2 +- lnbits/core/views/generic.py | 16 ++++++++-------- lnbits/decorators.py | 12 ++++++------ lnbits/wallets/eclair.py | 3 ++- lnbits/wallets/fake.py | 4 +--- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 6766ad8d..95e9dde3 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData = Body(...), + invoiceData=Body(...), ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 7ecedcf8..00d321af 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -54,9 +54,9 @@ async def home(request: Request, lightning: str = None): ) async def extensions( request: Request, - user = Depends(check_user_exists), - enable = Query(None), - disable = Query(None), + user=Depends(check_user_exists), + enable=Query(None), + disable=Query(None), ): extension_to_enable = enable extension_to_disable = disable @@ -100,10 +100,10 @@ nothing: create everything
""", ) async def wallet( - request = Query(None), - nme = Query(None), - usr = Query(None), - wal = Query(None), + request=Query(None), + nme=Query(None), + usr=Query(None), + wal=Query(None), ): user_id = usr.hex if usr else None wallet_id = wal.hex if wal else None @@ -209,7 +209,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) -async def deletewallet(request: Request, wal = Query(...), usr = Query(...)): +async def deletewallet(request: Request, wal=Query(...), usr=Query(...)): user = await get_user(usr) assert user is not None user_wallet_ids = [u.id for u in user.wallets] diff --git a/lnbits/decorators.py b/lnbits/decorators.py index b74230ef..ef689f8d 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -123,8 +123,8 @@ api_key_query = APIKeyQuery( async def get_key_type( r: Request, - api_key_header = Security(api_key_header), - api_key_query = Security(api_key_query), + api_key_header=Security(api_key_header), + api_key_query=Security(api_key_query), ) -> WalletTypeInfo: # 0: admin # 1: invoice @@ -179,8 +179,8 @@ async def get_key_type( async def require_admin_key( r: Request, - api_key_header = Security(api_key_header), - api_key_query = Security(api_key_query), + api_key_header=Security(api_key_header), + api_key_query=Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query @@ -198,8 +198,8 @@ async def require_admin_key( async def require_invoice_key( r: Request, - api_key_header = Security(api_key_header), - api_key_query = Security(api_key_query), + api_key_header=Security(api_key_header), + api_key_query=Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index b43d90c1..12289517 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -8,7 +8,8 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger -from websockets import connect # type: ignore +from websockets import connect # type: ignore + # TODO: https://github.com/lnbits/lnbits-legend/issues/764 # mypy https://github.com/aaugustin/websockets/issues/940 diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index c2b2307a..ba2956c5 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -81,9 +81,7 @@ class FakeWallet(Wallet): async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: invoice = decode(bolt11) # TODO: no data here? - data: Dict = { - "privkey": "missing" - } + data: Dict = {"privkey": "missing"} if ( invoice.checking_id is not None and invoice.checking_id[6:] == data["privkey"][:6] From 627959e30ba83342834f2f02b25c5398d7cde63f Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 11:52:49 +0200 Subject: [PATCH 13/23] add tests for api_payment --- tests/core/views/test_api.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 10e659aa..1f858870 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -140,3 +140,27 @@ async def test_decode_invoice(client, invoice): ) assert response.status_code < 300 assert response.json()["payment_hash"] == invoice["payment_hash"] + + +# check api_payment() internal function call (NOT API): payment status +@pytest.mark.asyncio +async def test_api_payment_without_key(invoice): + # check the payment status + response = await api_payment(invoice["payment_hash"]) + assert type(response) == dict + assert response["paid"] == True + # not key, that's why no "details" + assert "details" not in response + + +# check api_payment() internal function call (NOT API): payment status +@pytest.mark.asyncio +async def test_api_payment_with_key(invoice, inkey_headers_from): + # check the payment status + response = await api_payment( + invoice["payment_hash"], inkey_headers_from["X-Api-Key"] + ) + assert type(response) == dict + assert response["paid"] == True + # not key, that's why no "details" + assert "details" in response From 5f6dc757dcd08f2b248cbd5296616cc6c90502c0 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:00:53 +0200 Subject: [PATCH 14/23] types-mock==4.0.15 to requirements, needed for mypy tests --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 322712f5..8ee1fc6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,6 +43,7 @@ sqlalchemy==1.3.23 sqlalchemy-aio==0.16.0 sse-starlette==0.6.2 starlette==0.14.2 +types-mock==4.0.15 types-protobuf==3.19.22 typing-extensions==3.10.0.2 uvicorn==0.15.0 From c186e53cef1a5331be2981bce6092f0b5bf5adb9 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:05:35 +0200 Subject: [PATCH 15/23] fixup requirements.txt and ad to Pipfile --- Pipfile | 12 +++++++----- requirements.txt | 2 -- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Pipfile b/Pipfile index 8ef241f1..60c79292 100644 --- a/Pipfile +++ b/Pipfile @@ -35,9 +35,11 @@ pycryptodomex = "*" [dev-packages] black = "==20.8b1" -pytest = "*" -pytest-cov = "*" -mypy = "*" -pytest-asyncio = "*" -requests = "*" mock = "*" +mypy = "*" +pytest = "*" +pytest-asyncio = "*" +pytest-cov = "*" +requests = "*" +types-mock = "*" +types-protobuf = "*" diff --git a/requirements.txt b/requirements.txt index 8ee1fc6c..3603ab03 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,8 +43,6 @@ sqlalchemy==1.3.23 sqlalchemy-aio==0.16.0 sse-starlette==0.6.2 starlette==0.14.2 -types-mock==4.0.15 -types-protobuf==3.19.22 typing-extensions==3.10.0.2 uvicorn==0.15.0 uvloop==0.16.0 From 2e0276bc605a1b704cbfeee39e3d5fd03881376e Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:17:34 +0200 Subject: [PATCH 16/23] make test run again, introduce mypy again --- lnbits/core/views/api.py | 2 +- tests/core/views/test_api.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 95e9dde3..c81f623a 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData=Body(...), + invoiceData: CreateInvoiceData = Body(...), ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 1f858870..01427cba 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -1,6 +1,7 @@ import pytest import pytest_asyncio from lnbits.core.crud import get_wallet +from lnbits.core.views.api import api_payment from ...helpers import get_random_invoice_data From 06b1f71d3954d0dcb9de193a91845041a8de2ca2 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:21:39 +0200 Subject: [PATCH 17/23] fix up decorators for tests --- lnbits/decorators.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index ef689f8d..adfbd664 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,6 +1,6 @@ from http import HTTPStatus -from typing import Optional +from typing import Union from cerberus import Validator # type: ignore from fastapi import status @@ -102,9 +102,9 @@ class WalletAdminKeyChecker(KeyChecker): class WalletTypeInfo: wallet_type: int - wallet: Wallet + wallet: Union[Wallet, None] - def __init__(self, wallet_type: int, wallet: Wallet) -> None: + def __init__(self, wallet_type: int, wallet: Union[Wallet, None]) -> None: self.wallet_type = wallet_type self.wallet = wallet @@ -171,7 +171,7 @@ async def get_key_type( if e.status_code == HTTPStatus.BAD_REQUEST: raise if e.status_code == HTTPStatus.UNAUTHORIZED: - return WalletTypeInfo(2, Wallet()) + return WalletTypeInfo(2, None) except: raise return wallet From f4e3a3980e33ccca01de4502e8882993d106555c Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:12:55 +0200 Subject: [PATCH 18/23] fix isort check --- lnbits/core/views/api.py | 2 +- lnbits/decorators.py | 1 - lnbits/utils/exchange_rates.py | 2 +- lnbits/wallets/eclair.py | 4 +--- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index c81f623a..d7be3e55 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -3,7 +3,7 @@ import hashlib import json from binascii import unhexlify from http import HTTPStatus -from typing import Dict, List, Optional, Union, Tuple +from typing import Dict, List, Optional, Tuple, Union from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx diff --git a/lnbits/decorators.py b/lnbits/decorators.py index adfbd664..b62e456a 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,5 +1,4 @@ from http import HTTPStatus - from typing import Union from cerberus import Validator # type: ignore diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py index fbb4add2..2801146b 100644 --- a/lnbits/utils/exchange_rates.py +++ b/lnbits/utils/exchange_rates.py @@ -1,5 +1,5 @@ import asyncio -from typing import Callable, NamedTuple, List +from typing import Callable, List, NamedTuple import httpx from loguru import logger diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index 12289517..1ba81385 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -8,11 +8,9 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger -from websockets import connect # type: ignore - # TODO: https://github.com/lnbits/lnbits-legend/issues/764 # mypy https://github.com/aaugustin/websockets/issues/940 - +from websockets import connect # type: ignore from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From 0784ae02b1fa940000ecab917869c16bf28055de Mon Sep 17 00:00:00 2001 From: Daniel Krahofer Date: Wed, 20 Jul 2022 13:15:06 +0200 Subject: [PATCH 19/23] Update tests/core/views/test_api.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> --- tests/core/views/test_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 01427cba..d5eeeeae 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -163,5 +163,4 @@ async def test_api_payment_with_key(invoice, inkey_headers_from): ) assert type(response) == dict assert response["paid"] == True - # not key, that's why no "details" assert "details" in response From 613c5b466727ce77b1f81c8102f674dfe6f39536 Mon Sep 17 00:00:00 2001 From: Daniel Krahofer Date: Wed, 20 Jul 2022 13:15:18 +0200 Subject: [PATCH 20/23] Update tests/core/views/test_api.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> --- tests/core/views/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index d5eeeeae..61a71d31 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -150,7 +150,7 @@ async def test_api_payment_without_key(invoice): response = await api_payment(invoice["payment_hash"]) assert type(response) == dict assert response["paid"] == True - # not key, that's why no "details" + # no key, that's why no "details" assert "details" not in response From 04df2129b0fd26ec2bb3e81ce5e6bb82985cee50 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:21:42 +0200 Subject: [PATCH 21/23] add missing Pipfile.lock --- Pipfile.lock | 168 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 132 insertions(+), 36 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 6a89abb3..2e6d3a14 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "81bd288eea338c3bf1b70b8d30c1185b84c13a25a595bcddd77f74f7bc090032" + "sha256": "35eac2d1e375057cb654888c4414ec0bd479a0df1dc6b51bf5b72ba78d52ea36" }, "pipfile-spec": 6, "requires": { @@ -29,6 +29,7 @@ "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b", "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be" ], + "markers": "python_full_version >= '3.6.2'", "version": "==3.6.1" }, "asyncio": { @@ -46,6 +47,7 @@ "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==21.4.0" }, "bech32": { @@ -53,6 +55,7 @@ "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899", "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981" ], + "markers": "python_version >= '3.5'", "version": "==1.2.0" }, "bitstring": { @@ -76,6 +79,7 @@ "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" ], + "markers": "python_version >= '3.6'", "version": "==2022.6.15" }, "cffi": { @@ -152,6 +156,7 @@ "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" ], + "markers": "python_version >= '3.7'", "version": "==8.1.3" }, "ecdsa": { @@ -179,17 +184,18 @@ }, "fastapi": { "hashes": [ - "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65", - "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83" + "sha256:cf0ff6db25b91d321050c4112baab0908c90f19b40bf257f9591d2f9780d1f22", + "sha256:d337563424ceada23857f73d5abe8dae0c28e4cccb53b2af06e78b7bb4a1c7d7" ], "index": "pypi", - "version": "==0.78.0" + "version": "==0.79.0" }, "h11": { "hashes": [ "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" ], + "markers": "python_version >= '3.6'", "version": "==0.12.0" }, "httpcore": { @@ -197,6 +203,7 @@ "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6", "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b" ], + "markers": "python_version >= '3.7'", "version": "==0.15.0" }, "httptools": { @@ -251,8 +258,17 @@ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" ], + "markers": "python_version >= '3.5'", "version": "==3.3" }, + "importlib-metadata": { + "hashes": [ + "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670", + "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23" + ], + "markers": "python_version < '3.8'", + "version": "==4.12.0" + }, "jinja2": { "hashes": [ "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4", @@ -320,6 +336,7 @@ "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" ], + "markers": "python_version >= '3.7'", "version": "==2.1.1" }, "marshmallow": { @@ -327,6 +344,7 @@ "sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb", "sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7" ], + "markers": "python_version >= '3.7'", "version": "==3.17.0" }, "outcome": { @@ -334,6 +352,7 @@ "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672", "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5" ], + "markers": "python_version >= '3.7'", "version": "==1.2.0" }, "packaging": { @@ -341,6 +360,7 @@ "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" ], + "markers": "python_version >= '3.6'", "version": "==21.3" }, "psycopg2-binary": { @@ -486,6 +506,7 @@ "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e", "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6" ], + "markers": "python_full_version >= '3.6.1'", "version": "==1.9.1" }, "pyngrok": { @@ -500,14 +521,16 @@ "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" ], + "markers": "python_full_version >= '3.6.8'", "version": "==3.0.9" }, "pypng": { "hashes": [ - "sha256:76f8a1539ec56451da7ab7121f12a361969fe0f2d48d703d198ce2a99d6c5afd" + "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c", + "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1" ], "index": "pypi", - "version": "==0.0.21" + "version": "==0.20220715.0" }, "pyqrcode": { "hashes": [ @@ -574,9 +597,13 @@ "sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0", "sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.6.0.post0" }, "rfc3986": { + "extras": [ + "idna2008" + ], "hashes": [ "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835", "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97" @@ -612,6 +639,14 @@ "index": "pypi", "version": "==0.14.0" }, + "setuptools": { + "hashes": [ + "sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16", + "sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450" + ], + "markers": "python_version >= '3.7'", + "version": "==63.2.0" + }, "shortuuid": { "hashes": [ "sha256:459f12fa1acc34ff213b1371467c0325169645a31ed989e268872339af7563d5", @@ -625,6 +660,7 @@ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, "sniffio": { @@ -632,6 +668,7 @@ "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663", "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de" ], + "markers": "python_version >= '3.5'", "version": "==1.2.0" }, "sqlalchemy": { @@ -699,6 +736,7 @@ "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf", "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7" ], + "markers": "python_version >= '3.6'", "version": "==0.19.1" }, "typing-extensions": { @@ -707,10 +745,12 @@ "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" ], "index": "pypi", - "markers": "python_version < '3.10'", "version": "==4.3.0" }, "uvicorn": { + "extras": [ + "standard" + ], "hashes": [ "sha256:c19a057deb1c5bb060946e2e5c262fc01590c6529c0af2c3d9ce941e89bc30e0", "sha256:cade07c403c397f9fe275492a48c1b869efd175d5d8a692df649e6e7e2ed8f4e" @@ -808,6 +848,14 @@ "sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4" ], "version": "==10.3" + }, + "zipp": { + "hashes": [ + "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2", + "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009" + ], + "markers": "python_version >= '3.7'", + "version": "==3.8.1" } }, "develop": { @@ -823,6 +871,7 @@ "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==21.4.0" }, "black": { @@ -837,6 +886,7 @@ "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" ], + "markers": "python_version >= '3.6'", "version": "==2022.6.15" }, "charset-normalizer": { @@ -844,6 +894,7 @@ "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5", "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413" ], + "markers": "python_version >= '3.6'", "version": "==2.1.0" }, "click": { @@ -851,9 +902,13 @@ "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" ], + "markers": "python_version >= '3.7'", "version": "==8.1.3" }, "coverage": { + "extras": [ + "toml" + ], "hashes": [ "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32", "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7", @@ -897,6 +952,7 @@ "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39", "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452" ], + "markers": "python_version >= '3.7'", "version": "==6.4.2" }, "idna": { @@ -904,8 +960,17 @@ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" ], + "markers": "python_version >= '3.5'", "version": "==3.3" }, + "importlib-metadata": { + "hashes": [ + "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670", + "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23" + ], + "markers": "python_version < '3.8'", + "version": "==4.12.0" + }, "iniconfig": { "hashes": [ "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", @@ -923,32 +988,32 @@ }, "mypy": { "hashes": [ - "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5", - "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66", - "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e", - "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56", - "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e", - "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d", - "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813", - "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932", - "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569", - "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b", - "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0", - "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648", - "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6", - "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950", - "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15", - "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723", - "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a", - "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3", - "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6", - "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24", - "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b", - "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d", - "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492" + "sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655", + "sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9", + "sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3", + "sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6", + "sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0", + "sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58", + "sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103", + "sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09", + "sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417", + "sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56", + "sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2", + "sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856", + "sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0", + "sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8", + "sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27", + "sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5", + "sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71", + "sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27", + "sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe", + "sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca", + "sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf", + "sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9", + "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c" ], "index": "pypi", - "version": "==0.961" + "version": "==0.971" }, "mypy-extensions": { "hashes": [ @@ -962,6 +1027,7 @@ "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" ], + "markers": "python_version >= '3.6'", "version": "==21.3" }, "pathspec": { @@ -976,6 +1042,7 @@ "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" ], + "markers": "python_version >= '3.6'", "version": "==1.0.0" }, "py": { @@ -983,6 +1050,7 @@ "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==1.11.0" }, "pyparsing": { @@ -990,6 +1058,7 @@ "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" ], + "markers": "python_full_version >= '3.6.8'", "version": "==3.0.9" }, "pytest": { @@ -1002,12 +1071,11 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:16cf40bdf2b4fb7fc8e4b82bd05ce3fbcd454cbf7b92afc445fe299dabb88213", - "sha256:7659bdb0a9eb9c6e3ef992eef11a2b3e69697800ad02fb06374a210d85b29f91", - "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84" + "sha256:7a97e37cfe1ed296e2e84941384bdd37c376453912d397ed39293e0916f521fa", + "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed" ], "index": "pypi", - "version": "==0.18.3" + "version": "==0.19.0" }, "pytest-cov": { "hashes": [ @@ -1094,6 +1162,7 @@ "sha256:f8a2fd2f62a77536e4e3193303bec380df40d99e253b1c8f9b6eafa07eaeff67", "sha256:fbdf4fc6adf38fab1091c579ece3fe9f493bd0f1cfc3d2c76d2e52461ca4f8a9" ], + "markers": "python_version >= '3.6'", "version": "==2022.7.9" }, "requests": { @@ -1109,6 +1178,7 @@ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" }, "tomli": { @@ -1116,6 +1186,7 @@ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], + "markers": "python_version < '3.11'", "version": "==2.0.1" }, "typed-ast": { @@ -1145,15 +1216,31 @@ "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3", "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66" ], + "markers": "python_version >= '3.6'", "version": "==1.5.4" }, + "types-mock": { + "hashes": [ + "sha256:4535fbb3912b88a247d43cdb41db0c8b2e187138986f6f01a989717e56105848", + "sha256:a849bc2d966063f4946013bf404822ee2b96f77a8dccda4174b70ab61c5293fe" + ], + "index": "pypi", + "version": "==4.0.15" + }, + "types-protobuf": { + "hashes": [ + "sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab", + "sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83" + ], + "index": "pypi", + "version": "==3.19.22" + }, "typing-extensions": { "hashes": [ "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02", "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" ], "index": "pypi", - "markers": "python_version < '3.10'", "version": "==4.3.0" }, "urllib3": { @@ -1161,7 +1248,16 @@ "sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec", "sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", "version": "==1.26.10" + }, + "zipp": { + "hashes": [ + "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2", + "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009" + ], + "markers": "python_version >= '3.7'", + "version": "==3.8.1" } } } From 76fdce4328a946fa7aa78be27a870adbf5846f4d Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:23:50 +0200 Subject: [PATCH 22/23] enable mypy checks --- .github/workflows/mypy.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 4d6c6d4d..bf90a8e3 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -5,7 +5,6 @@ on: [push, pull_request] jobs: check: runs-on: ubuntu-latest - if: ${{ 'false' == 'true' }} # skip mypy for now steps: - uses: actions/checkout@v1 - uses: jpetrucciani/mypy-check@master From 5aa30c843c6604a9fb013f99094b8c454d650b1a Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:41:13 +0200 Subject: [PATCH 23/23] added FIXME tags and fix WalletTypeInfo --- lnbits/core/services.py | 4 ++++ lnbits/core/views/api.py | 23 +++++++++++++++++++++++ lnbits/decorators.py | 2 ++ 3 files changed, 29 insertions(+) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index f4f96250..2416ed31 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -269,6 +269,10 @@ async def perform_lnurlauth( cb = urlparse(callback) k1 = unhexlify(parse_qs(cb.query)["k1"][0]) + + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + key = wallet.wallet.lnurlauth_key(cb.netloc) def int_to_bytes_suitable_der(x: int) -> bytes: diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index d7be3e55..a3a59a5f 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -56,12 +56,16 @@ from ..tasks import api_invoice_listeners @core_app.get("/api/v1/wallet") async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)): if wallet.wallet_type == 0: + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return { "id": wallet.wallet.id, "name": wallet.wallet.name, "balance": wallet.wallet.balance_msat, } else: + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return {"name": wallet.wallet.name, "balance": wallet.wallet.balance_msat} @@ -69,6 +73,9 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)): async def api_update_balance( amount: int, wallet: WalletTypeInfo = Depends(get_key_type) ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + if wallet.wallet.user not in LNBITS_ADMIN_USERS: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user" @@ -98,6 +105,9 @@ async def api_update_balance( async def api_update_wallet( new_name: str, wallet: WalletTypeInfo = Depends(require_admin_key) ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + await update_wallet(wallet.wallet.id, new_name) return { "id": wallet.wallet.id, @@ -112,6 +122,9 @@ async def api_payments( offset: Optional[int] = None, wallet: WalletTypeInfo = Depends(get_key_type), ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + pendingPayments = await get_payments( wallet_id=wallet.wallet.id, pending=True, @@ -256,11 +269,15 @@ async def api_payments_create( status_code=HTTPStatus.BAD_REQUEST, detail="BOLT11 string is invalid or not given", ) + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return await api_payments_pay_invoice( invoiceData.bolt11, wallet.wallet ) # admin key elif not invoiceData.out: # invoice key + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return await api_payments_create_invoice(invoiceData, wallet.wallet) else: raise HTTPException( @@ -325,6 +342,8 @@ async def api_payments_pay_lnurl( if data.comment: extra["comment"] = data.comment assert data.description is not None, "description is required" + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None payment_hash = await pay_invoice( wallet_id=wallet.wallet.id, payment_request=params["pr"], @@ -378,6 +397,8 @@ async def subscribe(request: Request, wallet: Wallet): async def api_payments_sse( request: Request, wallet: WalletTypeInfo = Depends(get_key_type) ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return EventSourceResponse( subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream" ) @@ -456,6 +477,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type params.update(kind="auth") params.update(callback=url) # with k1 already in it + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None lnurlauth_key = wallet.wallet.lnurlauth_key(domain) params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex()) else: diff --git a/lnbits/decorators.py b/lnbits/decorators.py index b62e456a..9f51ce64 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -159,6 +159,8 @@ async def get_key_type( invoice_checker = WalletInvoiceKeyChecker(api_key=token) await invoice_checker.__call__(r) wallet = WalletTypeInfo(1, invoice_checker.wallet) + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ):