From 42704eaf1bcf0a0ec6fc4309ea63f692f0721286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 18 Oct 2022 08:49:27 +0200 Subject: [PATCH 01/96] change boltz logs from warn to debug (#1057) --- lnbits/extensions/boltz/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/boltz/tasks.py b/lnbits/extensions/boltz/tasks.py index ace94557..d1ace04b 100644 --- a/lnbits/extensions/boltz/tasks.py +++ b/lnbits/extensions/boltz/tasks.py @@ -57,7 +57,7 @@ async def check_for_pending_swaps(): swap_status = get_swap_status(swap) # should only happen while development when regtest is reset if swap_status.exists is False: - logger.warning(f"Boltz - swap: {swap.boltz_id} does not exist.") + logger.debug(f"Boltz - swap: {swap.boltz_id} does not exist.") await update_swap_status(swap.id, "failed") continue @@ -73,7 +73,7 @@ async def check_for_pending_swaps(): else: if swap_status.hit_timeout: if not swap_status.has_lockup: - logger.warning( + logger.debug( f"Boltz - swap: {swap.id} hit timeout, but no lockup tx..." ) await update_swap_status(swap.id, "timeout") From 9de38f908bfa603c1cfb98cfd078e0136e815320 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Thu, 20 Oct 2022 17:40:23 +0300 Subject: [PATCH 02/96] fix: wallet not found error (#1063) --- lnbits/extensions/satspay/crud.py | 9 ++++++++- lnbits/extensions/satspay/helpers.py | 17 +++++++++++++++++ lnbits/extensions/satspay/models.py | 1 - .../satspay/templates/satspay/display.html | 15 +++------------ lnbits/extensions/satspay/views.py | 5 ++--- lnbits/extensions/satspay/views_api.py | 17 +++-------------- 6 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 lnbits/extensions/satspay/helpers.py diff --git a/lnbits/extensions/satspay/crud.py b/lnbits/extensions/satspay/crud.py index 47d7a4a8..23d391b7 100644 --- a/lnbits/extensions/satspay/crud.py +++ b/lnbits/extensions/satspay/crud.py @@ -102,7 +102,7 @@ async def check_address_balance(charge_id: str) -> List[Charges]: charge = await get_charge(charge_id) if not charge.paid: if charge.onchainaddress: - config = await get_config(charge.user) + config = await get_charge_config(charge_id) try: async with httpx.AsyncClient() as client: r = await client.get( @@ -122,3 +122,10 @@ async def check_address_balance(charge_id: str) -> List[Charges]: return await update_charge(charge_id=charge_id, balance=charge.amount) row = await db.fetchone("SELECT * FROM satspay.charges WHERE id = ?", (charge_id,)) return Charges.from_row(row) if row else None + + +async def get_charge_config(charge_id: str): + row = await db.fetchone( + """SELECT "user" FROM satspay.charges WHERE id = ?""", (charge_id,) + ) + return await get_config(row.user) diff --git a/lnbits/extensions/satspay/helpers.py b/lnbits/extensions/satspay/helpers.py new file mode 100644 index 00000000..2d15b557 --- /dev/null +++ b/lnbits/extensions/satspay/helpers.py @@ -0,0 +1,17 @@ +from .models import Charges + + +def compact_charge(charge: Charges): + return { + "id": charge.id, + "description": charge.description, + "onchainaddress": charge.onchainaddress, + "payment_request": charge.payment_request, + "payment_hash": charge.payment_hash, + "time": charge.time, + "amount": charge.amount, + "balance": charge.balance, + "paid": charge.paid, + "timestamp": charge.timestamp, + "completelink": charge.completelink, # should be secret? + } diff --git a/lnbits/extensions/satspay/models.py b/lnbits/extensions/satspay/models.py index e8638d5e..daf63f42 100644 --- a/lnbits/extensions/satspay/models.py +++ b/lnbits/extensions/satspay/models.py @@ -19,7 +19,6 @@ class CreateCharge(BaseModel): class Charges(BaseModel): id: str - user: str description: Optional[str] onchainwallet: Optional[str] onchainaddress: Optional[str] diff --git a/lnbits/extensions/satspay/templates/satspay/display.html b/lnbits/extensions/satspay/templates/satspay/display.html index f34ac509..12288c80 100644 --- a/lnbits/extensions/satspay/templates/satspay/display.html +++ b/lnbits/extensions/satspay/templates/satspay/display.html @@ -328,7 +328,7 @@ ) }, checkBalances: async function () { - if (!this.charge.hasStaleBalance) await this.refreshCharge() + if (this.charge.hasStaleBalance) return try { const {data} = await LNbits.api.request( 'GET', @@ -339,18 +339,9 @@ LNbits.utils.notifyApiError(error) } }, - refreshCharge: async function () { - try { - const {data} = await LNbits.api.request( - 'GET', - `/satspay/api/v1/charge/${this.charge.id}` - ) - this.charge = mapCharge(data, this.charge) - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, checkPendingOnchain: async function () { + if (!this.charge.onchainaddress) return + const { bitcoin: {addresses: addressesAPI} } = mempoolJS({ diff --git a/lnbits/extensions/satspay/views.py b/lnbits/extensions/satspay/views.py index 69d81dad..b789bf8f 100644 --- a/lnbits/extensions/satspay/views.py +++ b/lnbits/extensions/satspay/views.py @@ -9,10 +9,9 @@ from starlette.responses import HTMLResponse from lnbits.core.crud import get_wallet from lnbits.core.models import User from lnbits.decorators import check_user_exists -from lnbits.extensions.watchonly.crud import get_config from . import satspay_ext, satspay_renderer -from .crud import get_charge +from .crud import get_charge, get_charge_config templates = Jinja2Templates(directory="templates") @@ -32,7 +31,7 @@ async def display(request: Request, charge_id: str): status_code=HTTPStatus.NOT_FOUND, detail="Charge link does not exist." ) wallet = await get_wallet(charge.lnbitswallet) - onchainwallet_config = await get_config(charge.user) + onchainwallet_config = await get_charge_config(charge_id) inkey = wallet.inkey if wallet else None mempool_endpoint = ( onchainwallet_config.mempool_endpoint if onchainwallet_config else None diff --git a/lnbits/extensions/satspay/views_api.py b/lnbits/extensions/satspay/views_api.py index f94b970a..73c87e7c 100644 --- a/lnbits/extensions/satspay/views_api.py +++ b/lnbits/extensions/satspay/views_api.py @@ -20,6 +20,7 @@ from .crud import ( get_charges, update_charge, ) +from .helpers import compact_charge from .models import CreateCharge #############################CHARGES########################## @@ -123,25 +124,13 @@ async def api_charge_balance(charge_id): try: r = await client.post( charge.webhook, - json={ - "id": charge.id, - "description": charge.description, - "onchainaddress": charge.onchainaddress, - "payment_request": charge.payment_request, - "payment_hash": charge.payment_hash, - "time": charge.time, - "amount": charge.amount, - "balance": charge.balance, - "paid": charge.paid, - "timestamp": charge.timestamp, - "completelink": charge.completelink, - }, + json=compact_charge(charge), timeout=40, ) except AssertionError: charge.webhook = None return { - **charge.dict(), + **compact_charge(charge), **{"time_elapsed": charge.time_elapsed}, **{"time_left": charge.time_left}, **{"paid": charge.paid}, From daf5768d52c39cbcf24279d2c8604a2fefa1a197 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Fri, 21 Oct 2022 15:49:17 +0300 Subject: [PATCH 03/96] fix: remove user from model (#1067) --- lnbits/extensions/watchonly/crud.py | 4 ++-- lnbits/extensions/watchonly/models.py | 1 - lnbits/extensions/watchonly/views_api.py | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/watchonly/crud.py b/lnbits/extensions/watchonly/crud.py index de338b91..c4a1df72 100644 --- a/lnbits/extensions/watchonly/crud.py +++ b/lnbits/extensions/watchonly/crud.py @@ -10,7 +10,7 @@ from .models import Address, Config, WalletAccount ##########################WALLETS#################### -async def create_watch_wallet(w: WalletAccount) -> WalletAccount: +async def create_watch_wallet(user: str, w: WalletAccount) -> WalletAccount: wallet_id = urlsafe_short_hash() await db.execute( """ @@ -30,7 +30,7 @@ async def create_watch_wallet(w: WalletAccount) -> WalletAccount: """, ( wallet_id, - w.user, + user, w.masterpub, w.fingerprint, w.title, diff --git a/lnbits/extensions/watchonly/models.py b/lnbits/extensions/watchonly/models.py index 622f5ec8..d8c278ff 100644 --- a/lnbits/extensions/watchonly/models.py +++ b/lnbits/extensions/watchonly/models.py @@ -14,7 +14,6 @@ class CreateWallet(BaseModel): class WalletAccount(BaseModel): id: str - user: str masterpub: str fingerprint: str title: str diff --git a/lnbits/extensions/watchonly/views_api.py b/lnbits/extensions/watchonly/views_api.py index 750d46c9..9030b9c3 100644 --- a/lnbits/extensions/watchonly/views_api.py +++ b/lnbits/extensions/watchonly/views_api.py @@ -86,7 +86,6 @@ async def api_wallet_create_or_update( new_wallet = WalletAccount( id="none", - user=w.wallet.user, masterpub=data.masterpub, fingerprint=descriptor.keys[0].fingerprint.hex(), type=descriptor.scriptpubkey_type(), @@ -115,7 +114,7 @@ async def api_wallet_create_or_update( ) ) - wallet = await create_watch_wallet(new_wallet) + wallet = await create_watch_wallet(w.wallet.user, new_wallet) await api_get_addresses(wallet.id, w) except Exception as e: From 2d61a8854f38a61507289af3520e52ff54ccb1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 21 Oct 2022 17:35:58 +0200 Subject: [PATCH 04/96] improving on split payment handling --- lnbits/extensions/splitpayments/models.py | 2 +- lnbits/extensions/splitpayments/tasks.py | 72 ++++++++--------------- 2 files changed, 24 insertions(+), 50 deletions(-) diff --git a/lnbits/extensions/splitpayments/models.py b/lnbits/extensions/splitpayments/models.py index 4b95ed18..6338d97f 100644 --- a/lnbits/extensions/splitpayments/models.py +++ b/lnbits/extensions/splitpayments/models.py @@ -14,7 +14,7 @@ class Target(BaseModel): class TargetPutList(BaseModel): wallet: str = Query(...) alias: str = Query("") - percent: float = Query(..., ge=0.01) + percent: float = Query(..., ge=0.01, lt=100) class TargetPut(BaseModel): diff --git a/lnbits/extensions/splitpayments/tasks.py b/lnbits/extensions/splitpayments/tasks.py index cfc6c226..53378b20 100644 --- a/lnbits/extensions/splitpayments/tasks.py +++ b/lnbits/extensions/splitpayments/tasks.py @@ -1,13 +1,11 @@ import asyncio -import json from loguru import logger -from lnbits.core import db as core_db -from lnbits.core.crud import create_payment from lnbits.core.models import Payment -from lnbits.helpers import get_current_extension_name, urlsafe_short_hash -from lnbits.tasks import internal_invoice_queue, register_invoice_listener +from lnbits.core.services import create_invoice, pay_invoice +from lnbits.helpers import get_current_extension_name +from lnbits.tasks import register_invoice_listener from .crud import get_targets @@ -22,60 +20,36 @@ async def wait_for_paid_invoices(): async def on_invoice_paid(payment: Payment) -> None: - if payment.extra.get("tag") == "splitpayments" or payment.extra.get("splitted"): - # already splitted, ignore + if payment.extra.get("tag") == "splitpayments": + # already a splitted payment, ignore return - # now we make some special internal transfers (from no one to the receiver) targets = await get_targets(payment.wallet_id) if not targets: return - transfers = [ - (target.wallet, int(target.percent * payment.amount / 100)) - for target in targets - ] - transfers = [(wallet, amount) for wallet, amount in transfers if amount > 0] - amount_left = payment.amount - sum([amount for _, amount in transfers]) + total_percent = sum([target.percent for target in targets]) - if amount_left < 0: - logger.error( - "splitpayments failure: amount_left is negative.", payment.payment_hash - ) + if total_percent > 100: + logger.error("splitpayment failure: total percent adds up to more than 100%") return - # mark the original payment with one extra key, "splitted" - # (this prevents us from doing this process again and it's informative) - # and reduce it by the amount we're going to send to the producer - await core_db.execute( - """ - UPDATE apipayments - SET extra = ?, amount = ? - WHERE hash = ? - AND checking_id NOT LIKE 'internal_%' - """, - ( - json.dumps(dict(**payment.extra, splitted=True)), - amount_left, - payment.payment_hash, - ), - ) - - # perform the internal transfer using the same payment_hash - for wallet, amount in transfers: - internal_checking_id = f"internal_{urlsafe_short_hash()}" - await create_payment( - wallet_id=wallet, - checking_id=internal_checking_id, - payment_request="", - payment_hash=payment.payment_hash, - amount=amount, - memo=payment.memo, - pending=False, + logger.debug(f"performing split payments to {len(targets)} targets") + for target in targets: + amount = int(payment.amount * target.percent / 100) # msats + payment_hash, payment_request = await create_invoice( + wallet_id=target.wallet, + amount=int(amount / 1000), # sats + internal=True, + memo=f"split payment: {target.percent}% for {target.alias or target.wallet}", extra={"tag": "splitpayments"}, ) + logger.debug(f"created split invoice: {payment_hash}") - # manually send this for now - await internal_invoice_queue.put(internal_checking_id) - return + checking_id = await pay_invoice( + payment_request=payment_request, + wallet_id=payment.wallet_id, + extra={"tag": "splitpayments"}, + ) + logger.debug(f"paid split invoice: {checking_id}") From fe879ad9b964766c25f8b59a0323d009d332166d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 21 Oct 2022 18:15:27 +0200 Subject: [PATCH 05/96] add tpos to pr --- lnbits/extensions/tpos/tasks.py | 58 ++++++++++----------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/lnbits/extensions/tpos/tasks.py b/lnbits/extensions/tpos/tasks.py index f18d1689..6369bbc7 100644 --- a/lnbits/extensions/tpos/tasks.py +++ b/lnbits/extensions/tpos/tasks.py @@ -1,11 +1,11 @@ import asyncio -import json -from lnbits.core import db as core_db -from lnbits.core.crud import create_payment +from loguru import logger + from lnbits.core.models import Payment -from lnbits.helpers import get_current_extension_name, urlsafe_short_hash -from lnbits.tasks import internal_invoice_queue, register_invoice_listener +from lnbits.core.services import create_invoice, pay_invoice +from lnbits.helpers import get_current_extension_name +from lnbits.tasks import register_invoice_listener from .crud import get_tpos @@ -20,11 +20,9 @@ async def wait_for_paid_invoices(): async def on_invoice_paid(payment: Payment) -> None: - if payment.extra.get("tag") == "tpos" and payment.extra.get("tipSplitted"): - # already splitted, ignore + if payment.extra.get("tag") != "tpos": return - # now we make some special internal transfers (from no one to the receiver) tpos = await get_tpos(payment.extra.get("tposId")) tipAmount = payment.extra.get("tipAmount") @@ -32,39 +30,17 @@ async def on_invoice_paid(payment: Payment) -> None: # no tip amount return - tipAmount = tipAmount * 1000 - amount = payment.amount - tipAmount - - # mark the original payment with one extra key, "splitted" - # (this prevents us from doing this process again and it's informative) - # and reduce it by the amount we're going to send to the producer - await core_db.execute( - """ - UPDATE apipayments - SET extra = ?, amount = ? - WHERE hash = ? - AND checking_id NOT LIKE 'internal_%' - """, - ( - json.dumps(dict(**payment.extra, tipSplitted=True)), - amount, - payment.payment_hash, - ), - ) - - # perform the internal transfer using the same payment_hash - internal_checking_id = f"internal_{urlsafe_short_hash()}" - await create_payment( + payment_hash, payment_request = await create_invoice( wallet_id=tpos.tip_wallet, - checking_id=internal_checking_id, - payment_request="", - payment_hash=payment.payment_hash, - amount=tipAmount, - memo=f"Tip for {payment.memo}", - pending=False, - extra={"tipSplitted": True}, + amount=int(tipAmount), # sats + internal=True, + memo=f"tpos tip", ) + logger.debug(f"tpos: tip invoice created: {payment_hash}") - # manually send this for now - await internal_invoice_queue.put(internal_checking_id) - return + checking_id = await pay_invoice( + payment_request=payment_request, + wallet_id=payment.wallet_id, + extra={"tag": "tpos"}, + ) + logger.debug(f"tpos: tip invoice paid: {checking_id}") From e1fbb8c3cd112d56bbd147565cc649f007d42287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 21 Oct 2022 18:28:39 +0200 Subject: [PATCH 06/96] fix livestream extension --- lnbits/extensions/livestream/tasks.py | 52 ++++++++------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/lnbits/extensions/livestream/tasks.py b/lnbits/extensions/livestream/tasks.py index 626c698c..d081332f 100644 --- a/lnbits/extensions/livestream/tasks.py +++ b/lnbits/extensions/livestream/tasks.py @@ -4,10 +4,10 @@ import json from loguru import logger from lnbits.core import db as core_db -from lnbits.core.crud import create_payment from lnbits.core.models import Payment -from lnbits.helpers import get_current_extension_name, urlsafe_short_hash -from lnbits.tasks import internal_invoice_listener, register_invoice_listener +from lnbits.core.services import create_invoice, pay_invoice +from lnbits.helpers import get_current_extension_name +from lnbits.tasks import register_invoice_listener from .crud import get_livestream_by_track, get_producer, get_track @@ -44,44 +44,20 @@ async def on_invoice_paid(payment: Payment) -> None: # now we make a special kind of internal transfer amount = int(payment.amount * (100 - ls.fee_pct) / 100) - # mark the original payment with two extra keys, "shared_with" and "received" - # (this prevents us from doing this process again and it's informative) - # and reduce it by the amount we're going to send to the producer - await core_db.execute( - """ - UPDATE apipayments - SET extra = ?, amount = ? - WHERE hash = ? - AND checking_id NOT LIKE 'internal_%' - """, - ( - json.dumps( - dict( - **payment.extra, - shared_with=[producer.name, producer.id], - received=payment.amount, - ) - ), - payment.amount - amount, - payment.payment_hash, - ), - ) - - # perform an internal transfer using the same payment_hash to the producer wallet - internal_checking_id = f"internal_{urlsafe_short_hash()}" - await create_payment( - wallet_id=producer.wallet, - checking_id=internal_checking_id, - payment_request="", - payment_hash=payment.payment_hash, - amount=amount, + payment_hash, payment_request = await create_invoice( + wallet_id=tpos.tip_wallet, + amount=amount, # sats + internal=True, memo=f"Revenue from '{track.name}'.", - pending=False, ) + logger.debug(f"livestream: producer invoice created: {payment_hash}") - # manually send this for now - # await internal_invoice_paid.send(internal_checking_id) - await internal_invoice_listener.put(internal_checking_id) + checking_id = await pay_invoice( + payment_request=payment_request, + wallet_id=payment.wallet_id, + extra={"tag": "livestream"}, + ) + logger.debug(f"livestream: producer invoice paid: {checking_id}") # so the flow is the following: # - we receive, say, 1000 satoshis From c8a33b0b93bb3e04bfb3d4ef1903cd1396fb3068 Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 21 Oct 2022 20:06:50 +0100 Subject: [PATCH 07/96] Adding loads on gpios to switch --- lnbits/extensions/lnurldevice/lnurl.py | 5 +++-- lnbits/extensions/lnurldevice/tasks.py | 2 +- lnbits/extensions/lnurldevice/views.py | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py index 79892b78..c83db6b8 100644 --- a/lnbits/extensions/lnurldevice/lnurl.py +++ b/lnbits/extensions/lnurldevice/lnurl.py @@ -91,6 +91,7 @@ async def lnurl_v1_params( device_id: str = Query(None), p: str = Query(None), atm: str = Query(None), + gpio: str = Query(None), ): device = await get_lnurldevice(device_id) if not device: @@ -114,7 +115,7 @@ async def lnurl_v1_params( deviceid=device.id, payload="bla", sats=price_msat, - pin=1, + pin=gpio, payhash="bla", ) if not lnurldevicepayment: @@ -236,7 +237,7 @@ async def lnurl_callback( amount=lnurldevicepayment.sats / 1000, memo=device.title + "-" + lnurldevicepayment.id, unhashed_description=(await device.lnurlpay_metadata()).encode("utf-8"), - extra={"tag": "Switch", "id": paymentid, "time": device.amount}, + extra={"tag": "Switch", "pin": ,"id": paymentid, "time": device.amount}, ) lnurldevicepayment = await update_lnurldevicepayment( lnurldevicepayment_id=paymentid, payhash=payment_hash diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py index c8f3db04..55cbaa98 100644 --- a/lnbits/extensions/lnurldevice/tasks.py +++ b/lnbits/extensions/lnurldevice/tasks.py @@ -36,5 +36,5 @@ async def on_invoice_paid(payment: Payment) -> None: lnurldevicepayment = await update_lnurldevicepayment( lnurldevicepayment_id=payment.extra.get("id"), payhash="used" ) - return await updater(lnurldevicepayment.deviceid) + return await updater(lnurldevicepayment.deviceid, lnurldevicepayment.pin) return diff --git a/lnbits/extensions/lnurldevice/views.py b/lnbits/extensions/lnurldevice/views.py index 5c6eba24..d17db7d7 100644 --- a/lnbits/extensions/lnurldevice/views.py +++ b/lnbits/extensions/lnurldevice/views.py @@ -103,8 +103,8 @@ async def websocket_endpoint(websocket: WebSocket, lnurldevice_id: str): manager.disconnect(websocket) -async def updater(lnurldevice_id): +async def updater(lnurldevice_id, lnurldevice_pin): lnurldevice = await get_lnurldevice(lnurldevice_id) if not lnurldevice: return - await manager.send_personal_message(f"{lnurldevice.amount}", lnurldevice_id) + return await manager.send_personal_message(f"pin:{lnurldevice.pin},amount:{lnurldevice.amount}", lnurldevice_id) From 3814989b3a142baaea1d010f7022882b72280f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Pha=CC=A3m-Bachelart?= Date: Mon, 24 Oct 2022 11:46:32 +0200 Subject: [PATCH 08/96] Add cliche wallet (#1071) --- docs/guide/wallets.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/guide/wallets.md b/docs/guide/wallets.md index 80fb54c0..10724f34 100644 --- a/docs/guide/wallets.md +++ b/docs/guide/wallets.md @@ -79,3 +79,8 @@ For the invoice to work you must have a publicly accessible URL in your LNbits. - `LNBITS_BACKEND_WALLET_CLASS`: **OpenNodeWallet** - `OPENNODE_API_ENDPOINT`: https://api.opennode.com/ - `OPENNODE_KEY`: opennodeAdminApiKey + + +### Cliche Wallet + +- `CLICHE_ENDPOINT`: ws://127.0.0.1:12000 From fc1a87f617a60de29552c8196a7f08050bb60ce5 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 24 Oct 2022 11:50:52 +0200 Subject: [PATCH 09/96] Remove unused proxy_fix (#1007) --- lnbits/app.py | 1 - lnbits/proxy_fix.py | 95 --------------------------------------------- 2 files changed, 96 deletions(-) delete mode 100644 lnbits/proxy_fix.py diff --git a/lnbits/app.py b/lnbits/app.py index 8b9cf798..075828ef 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -91,7 +91,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: ) app.add_middleware(GZipMiddleware, minimum_size=1000) - # app.add_middleware(ASGIProxyFix) check_funding_source(app) register_assets(app) diff --git a/lnbits/proxy_fix.py b/lnbits/proxy_fix.py deleted file mode 100644 index 897835e0..00000000 --- a/lnbits/proxy_fix.py +++ /dev/null @@ -1,95 +0,0 @@ -from functools import partial -from typing import Callable, List, Optional -from urllib.parse import urlparse -from urllib.request import parse_http_list as _parse_list_header - -from quart import Request -from quart_trio.asgi import TrioASGIHTTPConnection -from werkzeug.datastructures import Headers - - -class ASGIProxyFix(TrioASGIHTTPConnection): - def _create_request_from_scope(self, send: Callable) -> Request: - headers = Headers() - headers["Remote-Addr"] = (self.scope.get("client") or [""])[0] - for name, value in self.scope["headers"]: - headers.add(name.decode("latin1").title(), value.decode("latin1")) - if self.scope["http_version"] < "1.1": - headers.setdefault("Host", self.app.config["SERVER_NAME"] or "") - - path = self.scope["path"] - path = path if path[0] == "/" else urlparse(path).path - - x_proto = self._get_real_value(1, headers.get("X-Forwarded-Proto")) - if x_proto: - self.scope["scheme"] = x_proto - - x_host = self._get_real_value(1, headers.get("X-Forwarded-Host")) - if x_host: - headers["host"] = x_host.lower() - - return self.app.request_class( - self.scope["method"], - self.scope["scheme"], - path, - self.scope["query_string"], - headers, - self.scope.get("root_path", ""), - self.scope["http_version"], - max_content_length=self.app.config["MAX_CONTENT_LENGTH"], - body_timeout=self.app.config["BODY_TIMEOUT"], - send_push_promise=partial(self._send_push_promise, send), - scope=self.scope, - ) - - def _get_real_value(self, trusted: int, value: Optional[str]) -> Optional[str]: - """Get the real value from a list header based on the configured - number of trusted proxies. - :param trusted: Number of values to trust in the header. - :param value: Comma separated list header value to parse. - :return: The real value, or ``None`` if there are fewer values - than the number of trusted proxies. - .. versionchanged:: 1.0 - Renamed from ``_get_trusted_comma``. - .. versionadded:: 0.15 - """ - if not (trusted and value): - return None - - values = self.parse_list_header(value) - if len(values) >= trusted: - return values[-trusted] - - return None - - def parse_list_header(self, value: str) -> List[str]: - result = [] - for item in _parse_list_header(value): - if item[:1] == item[-1:] == '"': - item = self.unquote_header_value(item[1:-1]) - result.append(item) - return result - - def unquote_header_value(self, value: str, is_filename: bool = False) -> str: - r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). - This does not use the real unquoting but what browsers are actually - using for quoting. - .. versionadded:: 0.5 - :param value: the header value to unquote. - :param is_filename: The value represents a filename or path. - """ - if value and value[0] == value[-1] == '"': - # this is not the real unquoting, but fixing this so that the - # RFC is met will result in bugs with internet explorer and - # probably some other browsers as well. IE for example is - # uploading files with "C:\foo\bar.txt" as filename - value = value[1:-1] - - # if this is a filename and the starting characters look like - # a UNC path, then just return the value without quotes. Using the - # replace sequence below on a UNC path has the effect of turning - # the leading double slash into a single slash and then - # _fix_ie_filename() doesn't work correctly. See #458. - if not is_filename or value[:2] != "\\\\": - return value.replace("\\\\", "\\").replace('\\"', '"') - return value From 193d037ad6aa102152a824611c65a96a6ff876df Mon Sep 17 00:00:00 2001 From: Anton Kovalenko Date: Mon, 24 Oct 2022 13:23:11 +0300 Subject: [PATCH 10/96] Allow more than 2-nd level domain in incoming ln-addresses (#914) --- lnbits/core/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index c07df568..983d5a26 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -476,7 +476,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type except: # parse internet identifier (user@domain.com) name_domain = code.split("@") - if len(name_domain) == 2 and len(name_domain[1].split(".")) == 2: + if len(name_domain) == 2 and len(name_domain[1].split(".")) >= 2: name, domain = name_domain url = ( ("http://" if domain.endswith(".onion") else "https://") From 09cf654427d8f42baee484a5146a180b1261b1e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 24 Oct 2022 12:32:20 +0200 Subject: [PATCH 11/96] Proper Error Handling for qrcode-stream components (#1076) * proper errorhandling for camera * fix javascript for calle --- lnbits/core/static/js/wallet.js | 29 ++++++++++++++++++++++++++ lnbits/core/templates/core/wallet.html | 2 ++ 2 files changed, 31 insertions(+) diff --git a/lnbits/core/static/js/wallet.js b/lnbits/core/static/js/wallet.js index 76d82ad4..66801313 100644 --- a/lnbits/core/static/js/wallet.js +++ b/lnbits/core/static/js/wallet.js @@ -361,6 +361,35 @@ new Vue({ this.receive.status = 'pending' }) }, + onInitQR: async function (promise) { + try { + await promise + } catch (error) { + let mapping = { + NotAllowedError: 'ERROR: you need to grant camera access permission', + NotFoundError: 'ERROR: no camera on this device', + NotSupportedError: + 'ERROR: secure context required (HTTPS, localhost)', + NotReadableError: 'ERROR: is the camera already in use?', + OverconstrainedError: 'ERROR: installed cameras are not suitable', + StreamApiNotSupportedError: + 'ERROR: Stream API is not supported in this browser', + InsecureContextError: + 'ERROR: Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.' + } + let valid_error = Object.keys(mapping).filter(key => { + return error.name === key + }) + let camera_error = valid_error + ? mapping[valid_error] + : `ERROR: Camera error (${error.name})` + this.parse.camera.show = false + this.$q.notify({ + message: camera_error, + type: 'negative' + }) + } + }, decodeQR: function (res) { this.parse.data.request = res this.decodeRequest() diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index bccdc2b4..4bf6067c 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -653,6 +653,7 @@ @@ -671,6 +672,7 @@
From 09871bbabc27b69886d56770178b19f445b7c48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 24 Oct 2022 16:29:30 +0200 Subject: [PATCH 12/96] fix mypy for extensions (#873) * explicitly exclude all extensions from mypy * fix example extension mypy * fix subdomains extension mypy + 1 type error fixed * fix mypy discordbot * mypy check copilot extensnion * copilot black * add invoices ext to ignore * add boltz and boltcard * copilit id is necessary * was discordbot is ok Co-authored-by: dni --- lnbits/extensions/copilot/crud.py | 8 ++--- lnbits/extensions/copilot/tasks.py | 20 ++++++------ lnbits/extensions/copilot/views.py | 8 +++-- lnbits/extensions/copilot/views_api.py | 9 +++--- lnbits/extensions/discordbot/crud.py | 6 ++-- lnbits/extensions/discordbot/views.py | 4 ++- lnbits/extensions/discordbot/views_api.py | 39 +++++++++++++---------- lnbits/extensions/example/views.py | 5 ++- lnbits/extensions/subdomains/crud.py | 4 +-- lnbits/extensions/subdomains/models.py | 30 ++++++++--------- lnbits/extensions/subdomains/tasks.py | 4 +-- lnbits/extensions/subdomains/views.py | 4 ++- lnbits/extensions/subdomains/views_api.py | 25 +++++++++++---- pyproject.toml | 30 +++++++++++++++-- 14 files changed, 125 insertions(+), 71 deletions(-) diff --git a/lnbits/extensions/copilot/crud.py b/lnbits/extensions/copilot/crud.py index d0da044e..5ecb5cd4 100644 --- a/lnbits/extensions/copilot/crud.py +++ b/lnbits/extensions/copilot/crud.py @@ -10,7 +10,7 @@ from .models import Copilots, CreateCopilotData async def create_copilot( data: CreateCopilotData, inkey: Optional[str] = "" -) -> Copilots: +) -> Optional[Copilots]: copilot_id = urlsafe_short_hash() await db.execute( """ @@ -67,19 +67,19 @@ async def create_copilot( async def update_copilot( - data: CreateCopilotData, copilot_id: Optional[str] = "" + data: CreateCopilotData, copilot_id: str ) -> Optional[Copilots]: q = ", ".join([f"{field[0]} = ?" for field in data]) items = [f"{field[1]}" for field in data] items.append(copilot_id) - await db.execute(f"UPDATE copilot.newer_copilots SET {q} WHERE id = ?", (items)) + await db.execute(f"UPDATE copilot.newer_copilots SET {q} WHERE id = ?", (items,)) row = await db.fetchone( "SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,) ) return Copilots(**row) if row else None -async def get_copilot(copilot_id: str) -> Copilots: +async def get_copilot(copilot_id: str) -> Optional[Copilots]: row = await db.fetchone( "SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,) ) diff --git a/lnbits/extensions/copilot/tasks.py b/lnbits/extensions/copilot/tasks.py index c59ef4cc..48ad7813 100644 --- a/lnbits/extensions/copilot/tasks.py +++ b/lnbits/extensions/copilot/tasks.py @@ -26,7 +26,7 @@ async def wait_for_paid_invoices(): async def on_invoice_paid(payment: Payment) -> None: webhook = None data = None - if payment.extra.get("tag") != "copilot": + if not payment.extra or payment.extra.get("tag") != "copilot": # not an copilot invoice return @@ -71,12 +71,12 @@ async def on_invoice_paid(payment: Payment) -> None: async def mark_webhook_sent(payment: Payment, status: int) -> None: - payment.extra["wh_status"] = status - - await core_db.execute( - """ - UPDATE apipayments SET extra = ? - WHERE hash = ? - """, - (json.dumps(payment.extra), payment.payment_hash), - ) + if payment.extra: + payment.extra["wh_status"] = status + await core_db.execute( + """ + UPDATE apipayments SET extra = ? + WHERE hash = ? + """, + (json.dumps(payment.extra), payment.payment_hash), + ) diff --git a/lnbits/extensions/copilot/views.py b/lnbits/extensions/copilot/views.py index 7ee7f590..b4a2354a 100644 --- a/lnbits/extensions/copilot/views.py +++ b/lnbits/extensions/copilot/views.py @@ -15,7 +15,9 @@ templates = Jinja2Templates(directory="templates") @copilot_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, user: User = Depends(check_user_exists) # type: ignore +): return copilot_renderer().TemplateResponse( "copilot/index.html", {"request": request, "user": user.dict()} ) @@ -44,7 +46,7 @@ class ConnectionManager: async def connect(self, websocket: WebSocket, copilot_id: str): await websocket.accept() - websocket.id = copilot_id + websocket.id = copilot_id # type: ignore self.active_connections.append(websocket) def disconnect(self, websocket: WebSocket): @@ -52,7 +54,7 @@ class ConnectionManager: async def send_personal_message(self, message: str, copilot_id: str): for connection in self.active_connections: - if connection.id == copilot_id: + if connection.id == copilot_id: # type: ignore await connection.send_text(message) async def broadcast(self, message: str): diff --git a/lnbits/extensions/copilot/views_api.py b/lnbits/extensions/copilot/views_api.py index 91b0572a..46611a2e 100644 --- a/lnbits/extensions/copilot/views_api.py +++ b/lnbits/extensions/copilot/views_api.py @@ -23,7 +23,7 @@ from .views import updater @copilot_ext.get("/api/v1/copilot") async def api_copilots_retrieve( - req: Request, wallet: WalletTypeInfo = Depends(get_key_type) + req: Request, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): wallet_user = wallet.wallet.user copilots = [copilot.dict() for copilot in await get_copilots(wallet_user)] @@ -37,7 +37,7 @@ async def api_copilots_retrieve( async def api_copilot_retrieve( req: Request, copilot_id: str = Query(None), - wallet: WalletTypeInfo = Depends(get_key_type), + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore ): copilot = await get_copilot(copilot_id) if not copilot: @@ -54,7 +54,7 @@ async def api_copilot_retrieve( async def api_copilot_create_or_update( data: CreateCopilotData, copilot_id: str = Query(None), - wallet: WalletTypeInfo = Depends(require_admin_key), + wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore ): data.user = wallet.wallet.user data.wallet = wallet.wallet.id @@ -67,7 +67,8 @@ async def api_copilot_create_or_update( @copilot_ext.delete("/api/v1/copilot/{copilot_id}") async def api_copilot_delete( - copilot_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key) + copilot_id: str = Query(None), + wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore ): copilot = await get_copilot(copilot_id) diff --git a/lnbits/extensions/discordbot/crud.py b/lnbits/extensions/discordbot/crud.py index 5661fcb4..629a5c00 100644 --- a/lnbits/extensions/discordbot/crud.py +++ b/lnbits/extensions/discordbot/crud.py @@ -98,21 +98,21 @@ async def get_discordbot_wallet(wallet_id: str) -> Optional[Wallets]: return Wallets(**row) if row else None -async def get_discordbot_wallets(admin_id: str) -> Optional[Wallets]: +async def get_discordbot_wallets(admin_id: str) -> List[Wallets]: rows = await db.fetchall( "SELECT * FROM discordbot.wallets WHERE admin = ?", (admin_id,) ) return [Wallets(**row) for row in rows] -async def get_discordbot_users_wallets(user_id: str) -> Optional[Wallets]: +async def get_discordbot_users_wallets(user_id: str) -> List[Wallets]: rows = await db.fetchall( """SELECT * FROM discordbot.wallets WHERE "user" = ?""", (user_id,) ) return [Wallets(**row) for row in rows] -async def get_discordbot_wallet_transactions(wallet_id: str) -> Optional[Payment]: +async def get_discordbot_wallet_transactions(wallet_id: str) -> List[Payment]: return await get_payments( wallet_id=wallet_id, complete=True, pending=False, outgoing=True, incoming=True ) diff --git a/lnbits/extensions/discordbot/views.py b/lnbits/extensions/discordbot/views.py index a5395e21..ec7d18cc 100644 --- a/lnbits/extensions/discordbot/views.py +++ b/lnbits/extensions/discordbot/views.py @@ -9,7 +9,9 @@ from . import discordbot_ext, discordbot_renderer @discordbot_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, user: User = Depends(check_user_exists) # type: ignore +): return discordbot_renderer().TemplateResponse( "discordbot/index.html", {"request": request, "user": user.dict()} ) diff --git a/lnbits/extensions/discordbot/views_api.py b/lnbits/extensions/discordbot/views_api.py index 6f213a89..e6d004db 100644 --- a/lnbits/extensions/discordbot/views_api.py +++ b/lnbits/extensions/discordbot/views_api.py @@ -27,32 +27,37 @@ from .models import CreateUserData, CreateUserWallet @discordbot_ext.get("/api/v1/users", status_code=HTTPStatus.OK) -async def api_discordbot_users(wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_discordbot_users( + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore +): user_id = wallet.wallet.user return [user.dict() for user in await get_discordbot_users(user_id)] @discordbot_ext.get("/api/v1/users/{user_id}", status_code=HTTPStatus.OK) -async def api_discordbot_user(user_id, wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_discordbot_user( + user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore +): user = await get_discordbot_user(user_id) - return user.dict() + if user: + return user.dict() @discordbot_ext.post("/api/v1/users", status_code=HTTPStatus.CREATED) async def api_discordbot_users_create( - data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type) + data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): user = await create_discordbot_user(data) full = user.dict() - full["wallets"] = [ - wallet.dict() for wallet in await get_discordbot_users_wallets(user.id) - ] + wallets = await get_discordbot_users_wallets(user.id) + if wallets: + full["wallets"] = [wallet for wallet in wallets] return full @discordbot_ext.delete("/api/v1/users/{user_id}") async def api_discordbot_users_delete( - user_id, wallet: WalletTypeInfo = Depends(get_key_type) + user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): user = await get_discordbot_user(user_id) if not user: @@ -75,7 +80,7 @@ async def api_discordbot_activate_extension( raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="User does not exist." ) - update_user_extension(user_id=userid, extension=extension, active=active) + await update_user_extension(user_id=userid, extension=extension, active=active) return {"extension": "updated"} @@ -84,7 +89,7 @@ async def api_discordbot_activate_extension( @discordbot_ext.post("/api/v1/wallets") async def api_discordbot_wallets_create( - data: CreateUserWallet, wallet: WalletTypeInfo = Depends(get_key_type) + data: CreateUserWallet, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): user = await create_discordbot_wallet( user_id=data.user_id, wallet_name=data.wallet_name, admin_id=data.admin_id @@ -93,28 +98,30 @@ async def api_discordbot_wallets_create( @discordbot_ext.get("/api/v1/wallets") -async def api_discordbot_wallets(wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_discordbot_wallets( + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore +): admin_id = wallet.wallet.user - return [wallet.dict() for wallet in await get_discordbot_wallets(admin_id)] + return await get_discordbot_wallets(admin_id) @discordbot_ext.get("/api/v1/transactions/{wallet_id}") async def api_discordbot_wallet_transactions( - wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) + wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): return await get_discordbot_wallet_transactions(wallet_id) @discordbot_ext.get("/api/v1/wallets/{user_id}") async def api_discordbot_users_wallets( - user_id, wallet: WalletTypeInfo = Depends(get_key_type) + user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): - return [s_wallet.dict() for s_wallet in await get_discordbot_users_wallets(user_id)] + return await get_discordbot_users_wallets(user_id) @discordbot_ext.delete("/api/v1/wallets/{wallet_id}") async def api_discordbot_wallets_delete( - wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) + wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): get_wallet = await get_discordbot_wallet(wallet_id) if not get_wallet: diff --git a/lnbits/extensions/example/views.py b/lnbits/extensions/example/views.py index 252b4726..29b257f4 100644 --- a/lnbits/extensions/example/views.py +++ b/lnbits/extensions/example/views.py @@ -12,7 +12,10 @@ templates = Jinja2Templates(directory="templates") @example_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, + user: User = Depends(check_user_exists), # type: ignore +): return example_renderer().TemplateResponse( "example/index.html", {"request": request, "user": user.dict()} ) diff --git a/lnbits/extensions/subdomains/crud.py b/lnbits/extensions/subdomains/crud.py index 207e2d1d..aa358d11 100644 --- a/lnbits/extensions/subdomains/crud.py +++ b/lnbits/extensions/subdomains/crud.py @@ -3,10 +3,10 @@ from typing import List, Optional, Union from lnbits.helpers import urlsafe_short_hash from . import db -from .models import CreateDomain, Domains, Subdomains +from .models import CreateDomain, CreateSubdomain, Domains, Subdomains -async def create_subdomain(payment_hash, wallet, data: CreateDomain) -> Subdomains: +async def create_subdomain(payment_hash, wallet, data: CreateSubdomain) -> Subdomains: await db.execute( """ INSERT INTO subdomains.subdomain (id, domain, email, subdomain, ip, wallet, sats, duration, paid, record_type) diff --git a/lnbits/extensions/subdomains/models.py b/lnbits/extensions/subdomains/models.py index 17004504..39e17615 100644 --- a/lnbits/extensions/subdomains/models.py +++ b/lnbits/extensions/subdomains/models.py @@ -3,24 +3,24 @@ from pydantic.main import BaseModel class CreateDomain(BaseModel): - wallet: str = Query(...) - domain: str = Query(...) - cf_token: str = Query(...) - cf_zone_id: str = Query(...) - webhook: str = Query("") - description: str = Query(..., min_length=0) - cost: int = Query(..., ge=0) - allowed_record_types: str = Query(...) + wallet: str = Query(...) # type: ignore + domain: str = Query(...) # type: ignore + cf_token: str = Query(...) # type: ignore + cf_zone_id: str = Query(...) # type: ignore + webhook: str = Query("") # type: ignore + description: str = Query(..., min_length=0) # type: ignore + cost: int = Query(..., ge=0) # type: ignore + allowed_record_types: str = Query(...) # type: ignore class CreateSubdomain(BaseModel): - domain: str = Query(...) - subdomain: str = Query(...) - email: str = Query(...) - ip: str = Query(...) - sats: int = Query(..., ge=0) - duration: int = Query(...) - record_type: str = Query(...) + domain: str = Query(...) # type: ignore + subdomain: str = Query(...) # type: ignore + email: str = Query(...) # type: ignore + ip: str = Query(...) # type: ignore + sats: int = Query(..., ge=0) # type: ignore + duration: int = Query(...) # type: ignore + record_type: str = Query(...) # type: ignore class Domains(BaseModel): diff --git a/lnbits/extensions/subdomains/tasks.py b/lnbits/extensions/subdomains/tasks.py index 04ee2dd4..c5a7f47b 100644 --- a/lnbits/extensions/subdomains/tasks.py +++ b/lnbits/extensions/subdomains/tasks.py @@ -20,7 +20,7 @@ async def wait_for_paid_invoices(): async def on_invoice_paid(payment: Payment) -> None: - if payment.extra.get("tag") != "lnsubdomain": + if not payment.extra or payment.extra.get("tag") != "lnsubdomain": # not an lnurlp invoice return @@ -37,7 +37,7 @@ async def on_invoice_paid(payment: Payment) -> None: ) ### Use webhook to notify about cloudflare registration - if domain.webhook: + if domain and domain.webhook: async with httpx.AsyncClient() as client: try: r = await client.post( diff --git a/lnbits/extensions/subdomains/views.py b/lnbits/extensions/subdomains/views.py index df387ba8..962f850d 100644 --- a/lnbits/extensions/subdomains/views.py +++ b/lnbits/extensions/subdomains/views.py @@ -16,7 +16,9 @@ templates = Jinja2Templates(directory="templates") @subdomains_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, user: User = Depends(check_user_exists) # type:ignore +): return subdomains_renderer().TemplateResponse( "subdomains/index.html", {"request": request, "user": user.dict()} ) diff --git a/lnbits/extensions/subdomains/views_api.py b/lnbits/extensions/subdomains/views_api.py index b01e6ffb..34d8e75b 100644 --- a/lnbits/extensions/subdomains/views_api.py +++ b/lnbits/extensions/subdomains/views_api.py @@ -29,12 +29,15 @@ from .crud import ( @subdomains_ext.get("/api/v1/domains") async def api_domains( - g: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False) + g: WalletTypeInfo = Depends(get_key_type), # type: ignore + all_wallets: bool = Query(False), ): wallet_ids = [g.wallet.id] if all_wallets: - wallet_ids = (await get_user(g.wallet.user)).wallet_ids + user = await get_user(g.wallet.user) + if user is not None: + wallet_ids = user.wallet_ids return [domain.dict() for domain in await get_domains(wallet_ids)] @@ -42,7 +45,9 @@ async def api_domains( @subdomains_ext.post("/api/v1/domains") @subdomains_ext.put("/api/v1/domains/{domain_id}") async def api_domain_create( - data: CreateDomain, domain_id=None, g: WalletTypeInfo = Depends(get_key_type) + data: CreateDomain, + domain_id=None, + g: WalletTypeInfo = Depends(get_key_type), # type: ignore ): if domain_id: domain = await get_domain(domain_id) @@ -63,7 +68,9 @@ async def api_domain_create( @subdomains_ext.delete("/api/v1/domains/{domain_id}") -async def api_domain_delete(domain_id, g: WalletTypeInfo = Depends(get_key_type)): +async def api_domain_delete( + domain_id, g: WalletTypeInfo = Depends(get_key_type) # type: ignore +): domain = await get_domain(domain_id) if not domain: @@ -82,12 +89,14 @@ async def api_domain_delete(domain_id, g: WalletTypeInfo = Depends(get_key_type) @subdomains_ext.get("/api/v1/subdomains") async def api_subdomains( - all_wallets: bool = Query(False), g: WalletTypeInfo = Depends(get_key_type) + all_wallets: bool = Query(False), g: WalletTypeInfo = Depends(get_key_type) # type: ignore ): wallet_ids = [g.wallet.id] if all_wallets: - wallet_ids = (await get_user(g.wallet.user)).wallet_ids + user = await get_user(g.wallet.user) + if user is not None: + wallet_ids = user.wallet_ids return [domain.dict() for domain in await get_subdomains(wallet_ids)] @@ -173,7 +182,9 @@ async def api_subdomain_send_subdomain(payment_hash): @subdomains_ext.delete("/api/v1/subdomains/{subdomain_id}") -async def api_subdomain_delete(subdomain_id, g: WalletTypeInfo = Depends(get_key_type)): +async def api_subdomain_delete( + subdomain_id, g: WalletTypeInfo = Depends(get_key_type) # type: ignore +): subdomain = await get_subdomain(subdomain_id) if not subdomain: diff --git a/pyproject.toml b/pyproject.toml index 19dac860..7418de27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,8 +89,34 @@ profile = "black" ignore_missing_imports = "True" files = "lnbits" exclude = """(?x)( - ^lnbits/extensions. - | ^lnbits/wallets/lnd_grpc_files. + ^lnbits/extensions/bleskomat. + | ^lnbits/extensions/boltz. + | ^lnbits/extensions/boltcards. + | ^lnbits/extensions/events. + | ^lnbits/extensions/hivemind. + | ^lnbits/extensions/invoices. + | ^lnbits/extensions/jukebox. + | ^lnbits/extensions/livestream. + | ^lnbits/extensions/lnaddress. + | ^lnbits/extensions/lndhub. + | ^lnbits/extensions/lnticket. + | ^lnbits/extensions/lnurldevice. + | ^lnbits/extensions/lnurlp. + | ^lnbits/extensions/lnurlpayout. + | ^lnbits/extensions/ngrok. + | ^lnbits/extensions/offlineshop. + | ^lnbits/extensions/paywall. + | ^lnbits/extensions/satsdice. + | ^lnbits/extensions/satspay. + | ^lnbits/extensions/scrub. + | ^lnbits/extensions/splitpayments. + | ^lnbits/extensions/streamalerts. + | ^lnbits/extensions/tipjar. + | ^lnbits/extensions/tpos. + | ^lnbits/extensions/usermanager. + | ^lnbits/extensions/watchonly. + | ^lnbits/extensions/withdraw. + | ^lnbits/wallets/lnd_grpc_files. )""" [tool.pytest.ini_options] From 3552443f993d4cdf484912f72024401e767afa03 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 25 Oct 2022 11:10:34 +0100 Subject: [PATCH 13/96] adding migrations --- lnbits/extensions/lnurldevice/migrations.py | 25 +++++++++++++++++++++ lnbits/extensions/lnurldevice/models.py | 14 ++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lnbits/extensions/lnurldevice/migrations.py b/lnbits/extensions/lnurldevice/migrations.py index 7305cceb..52ecba7e 100644 --- a/lnbits/extensions/lnurldevice/migrations.py +++ b/lnbits/extensions/lnurldevice/migrations.py @@ -88,3 +88,28 @@ async def m003_redux(db): await db.execute( "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount INT DEFAULT 0;" ) + +async def m004_redux(db): + """ + Add 'meta' for storing various metadata about the wallet + """ + await db.execute( + """ + ALTER TABLE lnurldevice.lnurldevices ADD COLUMN ( + amount1, + amount2, + amount3, + amount4, + time, + time1, + time2, + time3, + time4, + pin1, + pin2, + pin3, + pin4, + ) + INT DEFAULT 0; + """ + ) \ No newline at end of file diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py index 01bcc2ba..a4af4404 100644 --- a/lnbits/extensions/lnurldevice/models.py +++ b/lnbits/extensions/lnurldevice/models.py @@ -18,6 +18,20 @@ class createLnurldevice(BaseModel): device: str profit: float amount: int + amount1: int + amount2: int + amount3: int + amount4: int + pin: int + pin1: int + pin2: int + pin3: int + pin4: int + time: int + time1: int + time2: int + time3: int + time4: int class lnurldevices(BaseModel): From e88b18a41746c028b125a96fef4d8c2afd89e584 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 25 Oct 2022 12:09:40 +0100 Subject: [PATCH 14/96] added migrate --- lnbits/extensions/lnurldevice/crud.py | 30 ++- lnbits/extensions/lnurldevice/lnurl.py | 8 +- lnbits/extensions/lnurldevice/migrations.py | 37 ++-- lnbits/extensions/lnurldevice/models.py | 30 ++- lnbits/extensions/lnurldevice/tasks.py | 2 +- .../templates/lnurldevice/index.html | 189 ++++++++++++++++-- lnbits/extensions/lnurldevice/views.py | 4 +- 7 files changed, 241 insertions(+), 59 deletions(-) diff --git a/lnbits/extensions/lnurldevice/crud.py b/lnbits/extensions/lnurldevice/crud.py index 4c25e4cb..f9a13b58 100644 --- a/lnbits/extensions/lnurldevice/crud.py +++ b/lnbits/extensions/lnurldevice/crud.py @@ -23,9 +23,22 @@ async def create_lnurldevice( currency, device, profit, - amount + amount, + pin, + profit1, + amount1, + pin1, + profit2, + amount2, + pin2, + profit3, + amount3, + pin3, + profit4, + amount4, + pin4, ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( lnurldevice_id, @@ -36,6 +49,19 @@ async def create_lnurldevice( data.device, data.profit, data.amount, + data.pin, + data.profit1, + data.amount1, + data.pin1, + data.profit2, + data.amount2, + data.pin2, + data.profit3, + data.amount3, + data.pin3, + data.profit4, + data.amount4, + data.pin4 ), ) return await get_lnurldevice(lnurldevice_id) diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py index c83db6b8..72749e85 100644 --- a/lnbits/extensions/lnurldevice/lnurl.py +++ b/lnbits/extensions/lnurldevice/lnurl.py @@ -92,6 +92,8 @@ async def lnurl_v1_params( p: str = Query(None), atm: str = Query(None), gpio: str = Query(None), + profit: str = Query(None), + amount: str = Query(None), ): device = await get_lnurldevice(device_id) if not device: @@ -106,14 +108,14 @@ async def lnurl_v1_params( if device.device == "switch": price_msat = ( - await fiat_amount_as_satoshis(float(device.profit), device.currency) + await fiat_amount_as_satoshis(float(profit), device.currency) if device.currency != "sat" else amount_in_cent ) * 1000 lnurldevicepayment = await create_lnurldevicepayment( deviceid=device.id, - payload="bla", + payload=amount, sats=price_msat, pin=gpio, payhash="bla", @@ -237,7 +239,7 @@ async def lnurl_callback( amount=lnurldevicepayment.sats / 1000, memo=device.title + "-" + lnurldevicepayment.id, unhashed_description=(await device.lnurlpay_metadata()).encode("utf-8"), - extra={"tag": "Switch", "pin": ,"id": paymentid, "time": device.amount}, + extra={"tag": "Switch", "pin": lnurldevicepayment.pin,"amount": int(lnurldevicepayment.payload),"id": paymentid}, ) lnurldevicepayment = await update_lnurldevicepayment( lnurldevicepayment_id=paymentid, payhash=payment_hash diff --git a/lnbits/extensions/lnurldevice/migrations.py b/lnbits/extensions/lnurldevice/migrations.py index 52ecba7e..ebe45301 100644 --- a/lnbits/extensions/lnurldevice/migrations.py +++ b/lnbits/extensions/lnurldevice/migrations.py @@ -93,23 +93,20 @@ async def m004_redux(db): """ Add 'meta' for storing various metadata about the wallet """ - await db.execute( - """ - ALTER TABLE lnurldevice.lnurldevices ADD COLUMN ( - amount1, - amount2, - amount3, - amount4, - time, - time1, - time2, - time3, - time4, - pin1, - pin2, - pin3, - pin4, - ) - INT DEFAULT 0; - """ - ) \ No newline at end of file + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin INT DEFAULT 0") + + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit1 FLOAT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount1 INT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin1 INT DEFAULT 0") + + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit2 FLOAT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount2 INT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin2 INT DEFAULT 0") + + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit3 FLOAT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount3 INT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin3 INT DEFAULT 0") + + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit4 FLOAT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount4 INT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin4 INT DEFAULT 0") \ No newline at end of file diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py index a4af4404..f4638bcd 100644 --- a/lnbits/extensions/lnurldevice/models.py +++ b/lnbits/extensions/lnurldevice/models.py @@ -18,20 +18,19 @@ class createLnurldevice(BaseModel): device: str profit: float amount: int - amount1: int - amount2: int - amount3: int - amount4: int pin: int + profit1: float + amount1: int pin1: int + profit2: float + amount2: int pin2: int + profit3: float + amount3: int pin3: int + profit4: float + amount4: int pin4: int - time: int - time1: int - time2: int - time3: int - time4: int class lnurldevices(BaseModel): @@ -43,6 +42,19 @@ class lnurldevices(BaseModel): device: str profit: float amount: int + pin: int + profit1: float + amount1: int + pin1: int + profit2: float + amount2: int + pin2: int + profit3: float + amount3: int + pin3: int + profit4: float + amount4: int + pin4: int timestamp: str def from_row(cls, row: Row) -> "lnurldevices": diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py index 55cbaa98..2867577d 100644 --- a/lnbits/extensions/lnurldevice/tasks.py +++ b/lnbits/extensions/lnurldevice/tasks.py @@ -36,5 +36,5 @@ async def on_invoice_paid(payment: Payment) -> None: lnurldevicepayment = await update_lnurldevicepayment( lnurldevicepayment_id=payment.extra.get("id"), payhash="used" ) - return await updater(lnurldevicepayment.deviceid, lnurldevicepayment.pin) + return await updater(lnurldevicepayment.deviceid, lnurldevicepayment.pin, lnurldevicepayment.amount) return diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html index 028dd94b..ae56c283 100644 --- a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html +++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html @@ -230,27 +230,167 @@ label="Profit margin (% added to invoices/deducted from faucets)" >
- - +

Switches

+ + + + + + + + + + +
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
@@ -338,6 +478,11 @@ wslocation: window.location.hostname, filter: '', currency: 'USD', + switch1: false, + switch2: false, + switch3: false, + switch4: false, + switch5: false, lnurldeviceLinks: [], lnurldeviceLinksObj: [], devices: [ @@ -592,7 +737,7 @@ LNbits.utils.notifyApiError(error) }) }, - clearFormDialoglnurldevice() { + clearFormDialoglnurldevice () { this.formDialoglnurldevice.data = { lnurl_toggle: false, show_message: false, diff --git a/lnbits/extensions/lnurldevice/views.py b/lnbits/extensions/lnurldevice/views.py index d17db7d7..d5d047af 100644 --- a/lnbits/extensions/lnurldevice/views.py +++ b/lnbits/extensions/lnurldevice/views.py @@ -103,8 +103,8 @@ async def websocket_endpoint(websocket: WebSocket, lnurldevice_id: str): manager.disconnect(websocket) -async def updater(lnurldevice_id, lnurldevice_pin): +async def updater(lnurldevice_id, lnurldevice_pin, lnurldevice_amount): lnurldevice = await get_lnurldevice(lnurldevice_id) if not lnurldevice: return - return await manager.send_personal_message(f"pin:{lnurldevice.pin},amount:{lnurldevice.amount}", lnurldevice_id) + return await manager.send_personal_message(f"pin:{lnurldevice_pin},amount:{lnurldevice_amount}", lnurldevice_id) From 7881125c71af913aa26844fb3a97a1e94ffa12db Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 26 Oct 2022 10:54:46 +0100 Subject: [PATCH 15/96] Fixed migrate --- lnbits/extensions/lnurldevice/migrations.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/lnurldevice/migrations.py b/lnbits/extensions/lnurldevice/migrations.py index ebe45301..2ba5b0b3 100644 --- a/lnbits/extensions/lnurldevice/migrations.py +++ b/lnbits/extensions/lnurldevice/migrations.py @@ -94,19 +94,24 @@ async def m004_redux(db): Add 'meta' for storing various metadata about the wallet """ await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin INT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit FLOAT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit1 FLOAT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount1 INT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin1 INT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit1 FLOAT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit2 FLOAT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount2 INT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin2 INT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit2 FLOAT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit3 FLOAT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount3 INT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin3 INT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit3 FLOAT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit4 FLOAT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount4 INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin4 INT DEFAULT 0") \ No newline at end of file + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin4 INT DEFAULT 0") + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit4 FLOAT DEFAULT 0") \ No newline at end of file From 1bfa28da19ad5585fdb113a4283e2cd8ad9be021 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 26 Oct 2022 10:58:13 +0100 Subject: [PATCH 16/96] reverted migrate --- lnbits/extensions/lnurldevice/migrations.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lnbits/extensions/lnurldevice/migrations.py b/lnbits/extensions/lnurldevice/migrations.py index 2ba5b0b3..f29bca99 100644 --- a/lnbits/extensions/lnurldevice/migrations.py +++ b/lnbits/extensions/lnurldevice/migrations.py @@ -94,24 +94,20 @@ async def m004_redux(db): Add 'meta' for storing various metadata about the wallet """ await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit FLOAT DEFAULT 0") - + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit1 FLOAT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount1 INT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin1 INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit1 FLOAT DEFAULT 0") - + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit2 FLOAT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount2 INT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin2 INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit2 FLOAT DEFAULT 0") - + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit3 FLOAT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount3 INT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin3 INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit3 FLOAT DEFAULT 0") - + await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit4 FLOAT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount4 INT DEFAULT 0") await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin4 INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit4 FLOAT DEFAULT 0") \ No newline at end of file + \ No newline at end of file From 440c4b696671dc6809e81ee04c2c71236220b221 Mon Sep 17 00:00:00 2001 From: calle <93376500+callebtc@users.noreply.github.com> Date: Wed, 26 Oct 2022 12:56:15 +0200 Subject: [PATCH 17/96] Update issue templates (#1085) --- .github/ISSUE_TEMPLATE/bug_report.md | 31 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..bfaddbeb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - LNbits version: [e.g. 0.9.2 or commit hash] + - Database [e.g. sqlite, postgres] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..4f49a497 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[Feature request]" +labels: feature request +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 2f1f92f1ee7465281cc57f2dd4e984b2c4d799b6 Mon Sep 17 00:00:00 2001 From: calle <93376500+callebtc@users.noreply.github.com> Date: Wed, 26 Oct 2022 12:57:53 +0200 Subject: [PATCH 18/96] Update issue templates (#1086) --- .github/ISSUE_TEMPLATE/something-else.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/something-else.md diff --git a/.github/ISSUE_TEMPLATE/something-else.md b/.github/ISSUE_TEMPLATE/something-else.md new file mode 100644 index 00000000..1b3b7355 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/something-else.md @@ -0,0 +1,10 @@ +--- +name: Something else +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + From 1d9be9619f46018e8fce7889d268f63ea915cf43 Mon Sep 17 00:00:00 2001 From: calle <93376500+callebtc@users.noreply.github.com> Date: Wed, 26 Oct 2022 13:01:02 +0200 Subject: [PATCH 19/96] Update issue templates (#1087) --- .github/ISSUE_TEMPLATE/something-else.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/something-else.md b/.github/ISSUE_TEMPLATE/something-else.md index 1b3b7355..4bd9ec2a 100644 --- a/.github/ISSUE_TEMPLATE/something-else.md +++ b/.github/ISSUE_TEMPLATE/something-else.md @@ -1,6 +1,6 @@ --- name: Something else -about: Describe this issue template's purpose here. +about: Anything else that you need to say title: '' labels: '' assignees: '' From 7d736c3f77a1365a145de07da9cd8cc297091717 Mon Sep 17 00:00:00 2001 From: Lee Salminen Date: Wed, 26 Oct 2022 05:02:35 -0600 Subject: [PATCH 20/96] Add `FORWARDED_ALLOW_IPS` to example fly.io (#1082) This PR adds the needed `FORWARDED_ALLOW_IPS="*"` environment variable to the fly.io installation method, which is needed to work. --- docs/guide/installation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 6b95f93b..11ded8e8 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -155,6 +155,7 @@ kill_timeout = 30 HOST="127.0.0.1" PORT=5000 LNBITS_FORCE_HTTPS=true + FORWARDED_ALLOW_IPS="*" LNBITS_DATA_FOLDER="/data" ${PUT_YOUR_LNBITS_ENV_VARS_HERE} From 5a3524da8377d6706a171d31a9cd7d9aa0757bca Mon Sep 17 00:00:00 2001 From: rolznz <33993199+rolznz@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:05:31 +0700 Subject: [PATCH 21/96] Fix usermanager API docs (#1073) - update incorrect response status codes - update invalid activate extension example --- .../templates/usermanager/_api_docs.html | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html index 886589e6..de477834 100644 --- a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html +++ b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html @@ -38,7 +38,7 @@ >
Body (application/json)
- Returns 201 CREATED (application/json) + Returns 200 OK (application/json)
JSON list of users
Curl example
@@ -57,10 +57,15 @@ /usermanager/api/v1/users/<user_id>
Body (application/json)
+
- Returns 201 CREATED (application/json) + Returns 200 OK (application/json)
- JSON list of users + {"id": <string>, "name": <string>, "admin": + <string>, "email": <string>, "password": <string>} +
Curl example
curl -X GET {{ request.base_url @@ -81,7 +86,7 @@ {"X-Api-Key": <string>}
Body (application/json)
- Returns 201 CREATED (application/json) + Returns 200 OK (application/json)
JSON wallet data
Curl example
@@ -104,7 +109,7 @@ {"X-Api-Key": <string>}
Body (application/json)
- Returns 201 CREATED (application/json) + Returns 200 OK (application/json)
JSON a wallets transactions
Curl example
@@ -254,11 +259,15 @@ {"X-Api-Key": <string>}
Curl example
curl -X POST {{ request.base_url }}usermanager/api/v1/extensions -d - '{"userid": <string>, "extension": <string>, "active": - <integer>}' -H "X-Api-Key: {{ user.wallets[0].inkey }}" -H + >curl -X POST {{ request.base_url }}usermanager/api/v1/extensions?extension=withdraw&userid=user_id&active=true -H "X-Api-Key: {{ user.wallets[0].inkey }}" -H "Content-type: application/json" +
+ Returns 200 OK (application/json) +
+ {"extension": "updated"} From d702e1b6a5bb6fe304f98236d0ddddddad223107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 26 Oct 2022 13:07:34 +0200 Subject: [PATCH 22/96] add FORWARDED_ALLOW_IPS variable to .example.env (#1077) --- .env.example | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 987c6ca6..4edaea97 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,9 @@ HOST=127.0.0.1 PORT=5000 +# uvicorn variable, allow https behind a proxy +# FORWARDED_ALLOW_IPS="*" + DEBUG=false LNBITS_ALLOWED_USERS="" @@ -13,7 +16,7 @@ LNBITS_DEFAULT_WALLET_NAME="LNbits wallet" LNBITS_AD_SPACE="" # Hides wallet api, extensions can choose to honor -LNBITS_HIDE_API=false +LNBITS_HIDE_API=false # Disable extensions for all users, use "all" to disable all extensions LNBITS_DISABLED_EXTENSIONS="amilk" @@ -67,7 +70,7 @@ LNBITS_KEY=LNBITS_ADMIN_KEY LND_REST_ENDPOINT=https://127.0.0.1:8080/ LND_REST_CERT="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/tls.cert" LND_REST_MACAROON="/home/bob/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/admin.macaroon or HEXSTRING" -# To use an AES-encrypted macaroon, set +# To use an AES-encrypted macaroon, set # LND_REST_MACAROON_ENCRYPTED="eNcRyPtEdMaCaRoOn" # LNPayWallet From 937654e4f73e75b4f4446f1e7d15166fd89517ab Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 26 Oct 2022 13:15:50 +0100 Subject: [PATCH 23/96] Fixed models --- lnbits/extensions/lnurldevice/crud.py | 2 +- lnbits/extensions/lnurldevice/models.py | 24 +-- .../templates/lnurldevice/index.html | 146 +++++++++++++----- 3 files changed, 124 insertions(+), 48 deletions(-) diff --git a/lnbits/extensions/lnurldevice/crud.py b/lnbits/extensions/lnurldevice/crud.py index f9a13b58..818b99fd 100644 --- a/lnbits/extensions/lnurldevice/crud.py +++ b/lnbits/extensions/lnurldevice/crud.py @@ -36,7 +36,7 @@ async def create_lnurldevice( pin3, profit4, amount4, - pin4, + pin4 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py index f4638bcd..62917632 100644 --- a/lnbits/extensions/lnurldevice/models.py +++ b/lnbits/extensions/lnurldevice/models.py @@ -19,18 +19,18 @@ class createLnurldevice(BaseModel): profit: float amount: int pin: int - profit1: float - amount1: int - pin1: int - profit2: float - amount2: int - pin2: int - profit3: float - amount3: int - pin3: int - profit4: float - amount4: int - pin4: int + profit1: float = 0 + amount1: int = 0 + pin1: int = 0 + profit2: float = 0 + amount2: int = 0 + pin2: int = 0 + profit3: float = 0 + amount3: int = 0 + pin3: int = 0 + profit4: float = 0 + amount4: int = 0 + pin4: int = 0 class lnurldevices(BaseModel): diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html index ae56c283..72ea8082 100644 --- a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html +++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html @@ -105,7 +105,7 @@ @click="openQrCodeDialog(props.row.id)" > LNURLs only work over HTTPS view LNURL view LNURLS + + + + + + + + + + + + +
Mails
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +
+ + +
Alarms
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +
+ + +
Movies
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. +
+
+ +
LNURLDevice device string
-

Switches

- - + + - - - - - -
- +
+
+ - +
+ - +
+ +
+
-
- +
+
+ - +
+ - +
+ +
+
-
- +
+
+ - +
+ - +
+ +
+
-
- +
+
+ - +
+ - +
+ +
+
-
- +
+
+ - +
+ - +
+ +
+
-
Date: Wed, 26 Oct 2022 20:19:04 +0100 Subject: [PATCH 24/96] Web socket working --- lnbits/extensions/lnurldevice/lnurl.py | 25 +++++-- lnbits/extensions/lnurldevice/models.py | 29 ++++++-- lnbits/extensions/lnurldevice/tasks.py | 2 +- .../templates/lnurldevice/index.html | 69 +++++-------------- lnbits/extensions/lnurldevice/views_api.py | 8 +-- 5 files changed, 64 insertions(+), 69 deletions(-) diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py index 72749e85..37a4c57d 100644 --- a/lnbits/extensions/lnurldevice/lnurl.py +++ b/lnbits/extensions/lnurldevice/lnurl.py @@ -9,6 +9,7 @@ from embit import bech32, compact from fastapi import Request from fastapi.param_functions import Query from starlette.exceptions import HTTPException +from loguru import logger from lnbits.core.services import create_invoice from lnbits.core.views.api import pay_invoice @@ -113,6 +114,14 @@ async def lnurl_v1_params( else amount_in_cent ) * 1000 + # Check they're not trying to trick the switch! + check = False + for switch in device.switches(request): + if switch[0] == gpio and switch[1] == profit and switch[2] == amount: + check = True + if not check: + return {"status": "ERROR", "reason": f"Switch params wrong"} + lnurldevicepayment = await create_lnurldevicepayment( deviceid=device.id, payload=amount, @@ -129,7 +138,7 @@ async def lnurl_v1_params( ), "minSendable": price_msat, "maxSendable": price_msat, - "metadata": await device.lnurlpay_metadata(), + "metadata": device.lnurlpay_metadata, } if len(p) % 4 > 0: p += "=" * (4 - (len(p) % 4)) @@ -236,11 +245,17 @@ async def lnurl_callback( if device.device == "switch": payment_hash, payment_request = await create_invoice( wallet_id=device.wallet, - amount=lnurldevicepayment.sats / 1000, - memo=device.title + "-" + lnurldevicepayment.id, - unhashed_description=(await device.lnurlpay_metadata()).encode("utf-8"), - extra={"tag": "Switch", "pin": lnurldevicepayment.pin,"amount": int(lnurldevicepayment.payload),"id": paymentid}, + amount=int(lnurldevicepayment.sats / 1000), + memo=device.id + " PIN " + str(lnurldevicepayment.pin), + unhashed_description=device.lnurlpay_metadata.encode("utf-8"), + extra={ + "tag": "Switch", + "pin": str(lnurldevicepayment.pin), + "amount": str(lnurldevicepayment.payload), + "id": paymentid + }, ) + lnurldevicepayment = await update_lnurldevicepayment( lnurldevicepayment_id=paymentid, payhash=payment_hash ) diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py index 62917632..ace22ca1 100644 --- a/lnbits/extensions/lnurldevice/models.py +++ b/lnbits/extensions/lnurldevice/models.py @@ -1,6 +1,6 @@ import json from sqlite3 import Row -from typing import Optional +from typing import Optional, List from fastapi import Request from lnurl import Lnurl @@ -9,7 +9,7 @@ from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore from lnurl.types import LnurlPayMetadata # type: ignore from pydantic import BaseModel from pydantic.main import BaseModel - +from loguru import logger class createLnurldevice(BaseModel): title: str @@ -60,13 +60,28 @@ class lnurldevices(BaseModel): def from_row(cls, row: Row) -> "lnurldevices": return cls(**dict(row)) - def lnurl(self, req: Request) -> Lnurl: - url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) - return lnurl_encode(url) - - async def lnurlpay_metadata(self) -> LnurlPayMetadata: + @property + def lnurlpay_metadata(self) -> LnurlPayMetadata: return LnurlPayMetadata(json.dumps([["text/plain", self.title]])) + def switches(self, req: Request) -> List: + switches = [] + if self.profit > 0: + url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) + switches.append([str(self.pin), str(self.profit), str(self.amount), lnurl_encode(url + "?gpio=" + str(self.pin) + "&profit=" + str(self.profit) + "&amount=" + str(self.amount))]) + if self.profit1 > 0: + url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) + switches.append([str(self.pin1), str(self.profit1), str(self.amount1), lnurl_encode(url + "?gpio=" + str(self.pin1) + "&profit=" + str(self.profit1) + "&amount=" + str(self.amount1))]) + if self.profit2 > 0: + url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) + switches.append([str(self.pin2), str(self.profit2), str(self.amount2), lnurl_encode(url + "?gpio=" + str(self.pin2) + "&profit=" + str(self.profit2) + "&amount=" + str(self.amount2))]) + if self.profit3 > 0: + url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) + switches.append([str(self.pin3), str(self.profit3), str(self.amount3), lnurl_encode(url + "?gpio=" + str(self.pin3) + "&profit=" + str(self.profit3) + "&amount=" + str(self.amount3))]) + if self.profit4 > 0: + url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) + switches.append([str(self.pin4), str(self.profit4), str(self.amount4), lnurl_encode(url + "?gpio=" + str(self.pin4) + "&profit=" + str(self.profit4) + "&amount=" + str(self.amount4))]) + return switches class lnurldevicepayment(BaseModel): id: str diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py index 2867577d..ca7aabb4 100644 --- a/lnbits/extensions/lnurldevice/tasks.py +++ b/lnbits/extensions/lnurldevice/tasks.py @@ -36,5 +36,5 @@ async def on_invoice_paid(payment: Payment) -> None: lnurldevicepayment = await update_lnurldevicepayment( lnurldevicepayment_id=payment.extra.get("id"), payhash="used" ) - return await updater(lnurldevicepayment.deviceid, lnurldevicepayment.pin, lnurldevicepayment.amount) + return await updater(lnurldevicepayment.deviceid, lnurldevicepayment.pin, lnurldevicepayment.payload) return diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html index 72ea8082..17ce9f28 100644 --- a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html +++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html @@ -148,42 +148,6 @@ style="width: 700px; max-width: 80vw" class="q-pa-lg q-pt-xl lnbits__dialog-card" > - - - - - - - - - - - - -
Mails
- Lorem ipsum dolor sit amet consectetur adipisicing elit. -
- - -
Alarms
- Lorem ipsum dolor sit amet consectetur adipisicing elit. -
- - -
Movies
- Lorem ipsum dolor sit amet consectetur adipisicing elit. -
-
- -
LNURLDevice device string
+ - {% raw %} -

- ID: {{ qrCodeDialog.data.id }}
-

- {% endraw %} -
- Copy LNURL +
+
+ + Close
+
@@ -545,11 +508,13 @@ mixins: [windowMixin], data: function () { return { + tab: 'mails', protocol: window.location.protocol, location: window.location.hostname, wslocation: window.location.hostname, filter: '', currency: 'USD', + lnurlValue: '', switches: 0, lnurldeviceLinks: [], lnurldeviceLinksObj: [], @@ -599,12 +564,6 @@ label: 'device', field: 'device' }, - { - name: 'profit', - align: 'left', - label: 'profit', - field: 'profit' - }, { name: 'currency', align: 'left', @@ -653,8 +612,12 @@ this.qrCodeDialog.data = _.clone(lnurldevice) this.qrCodeDialog.data.url = window.location.protocol + '//' + window.location.host + this.lnurlValueFetch(this.qrCodeDialog.data.switches[0][3]) this.qrCodeDialog.show = true }, + lnurlValueFetch: function (lnurl){ + this.lnurlValue = lnurl + }, addSwitch: function () { var self = this self.switches = self.switches + 1 @@ -719,7 +682,9 @@ .then(function (response) { if (response.data) { self.lnurldeviceLinks = response.data.map(maplnurldevice) + console.log("response.data") console.log(response.data) + console.log("response.data") } }) .catch(function (error) { diff --git a/lnbits/extensions/lnurldevice/views_api.py b/lnbits/extensions/lnurldevice/views_api.py index c034f66e..c6766423 100644 --- a/lnbits/extensions/lnurldevice/views_api.py +++ b/lnbits/extensions/lnurldevice/views_api.py @@ -39,10 +39,10 @@ async def api_lnurldevice_create_or_update( ): if not lnurldevice_id: lnurldevice = await create_lnurldevice(data) - return {**lnurldevice.dict(), **{"lnurl": lnurldevice.lnurl(req)}} + return {**lnurldevice.dict(), **{"switches": lnurldevice.switches(req)}} else: lnurldevice = await update_lnurldevice(data, lnurldevice_id=lnurldevice_id) - return {**lnurldevice.dict(), **{"lnurl": lnurldevice.lnurl(req)}} + return {**lnurldevice.dict(), **{"switches": lnurldevice.switches(req)}} @lnurldevice_ext.get("/api/v1/lnurlpos") @@ -52,7 +52,7 @@ async def api_lnurldevices_retrieve( wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids try: return [ - {**lnurldevice.dict(), **{"lnurl": lnurldevice.lnurl(req)}} + {**lnurldevice.dict(), **{"switches": lnurldevice.switches(req)}} for lnurldevice in await get_lnurldevices(wallet_ids) ] except: @@ -78,7 +78,7 @@ async def api_lnurldevice_retrieve( ) if not lnurldevice.lnurl_toggle: return {**lnurldevice.dict()} - return {**lnurldevice.dict(), **{"lnurl": lnurldevice.lnurl(req)}} + return {**lnurldevice.dict(), **{"switches": lnurldevice.switches(req)}} @lnurldevice_ext.delete("/api/v1/lnurlpos/{lnurldevice_id}") From 98d712fa7966e45a91563be66e3ced5d41b5b9bc Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 27 Oct 2022 00:26:07 +0100 Subject: [PATCH 25/96] Fixed pin bug --- lnbits/extensions/lnurldevice/models.py | 2 +- lnbits/extensions/lnurldevice/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py index ace22ca1..29d49d3f 100644 --- a/lnbits/extensions/lnurldevice/models.py +++ b/lnbits/extensions/lnurldevice/models.py @@ -18,7 +18,7 @@ class createLnurldevice(BaseModel): device: str profit: float amount: int - pin: int + pin: int = 0 profit1: float = 0 amount1: int = 0 pin1: int = 0 diff --git a/lnbits/extensions/lnurldevice/views.py b/lnbits/extensions/lnurldevice/views.py index d5d047af..f5fd598c 100644 --- a/lnbits/extensions/lnurldevice/views.py +++ b/lnbits/extensions/lnurldevice/views.py @@ -107,4 +107,4 @@ async def updater(lnurldevice_id, lnurldevice_pin, lnurldevice_amount): lnurldevice = await get_lnurldevice(lnurldevice_id) if not lnurldevice: return - return await manager.send_personal_message(f"pin:{lnurldevice_pin},amount:{lnurldevice_amount}", lnurldevice_id) + return await manager.send_personal_message(f"{lnurldevice_pin}-{lnurldevice_amount}", lnurldevice_id) From 5b1df5fffc02337f40b374fc9402900c17797c85 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 27 Oct 2022 00:35:49 +0100 Subject: [PATCH 26/96] fixed some ints --- lnbits/extensions/lnurldevice/lnurl.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py index 37a4c57d..ce2d1c69 100644 --- a/lnbits/extensions/lnurldevice/lnurl.py +++ b/lnbits/extensions/lnurldevice/lnurl.py @@ -200,7 +200,7 @@ async def lnurl_v1_params( ), "minSendable": price_msat * 1000, "maxSendable": price_msat * 1000, - "metadata": await device.lnurlpay_metadata(), + "metadata": device.lnurlpay_metadata, } @@ -266,9 +266,9 @@ async def lnurl_callback( payment_hash, payment_request = await create_invoice( wallet_id=device.wallet, - amount=lnurldevicepayment.sats / 1000, + amount=int(lnurldevicepayment.sats / 1000), memo=device.title, - unhashed_description=(await device.lnurlpay_metadata()).encode("utf-8"), + unhashed_description=device.lnurlpay_metadata.encode("utf-8"), extra={"tag": "PoS"}, ) lnurldevicepayment = await update_lnurldevicepayment( From 047d0fc761097d868c494009adde91bb39f86913 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 27 Oct 2022 00:37:15 +0100 Subject: [PATCH 27/96] black --- lnbits/extensions/lnurldevice/crud.py | 2 +- lnbits/extensions/lnurldevice/lnurl.py | 10 +-- lnbits/extensions/lnurldevice/migrations.py | 60 ++++++++++---- lnbits/extensions/lnurldevice/models.py | 87 +++++++++++++++++++-- lnbits/extensions/lnurldevice/tasks.py | 6 +- lnbits/extensions/lnurldevice/views.py | 4 +- 6 files changed, 139 insertions(+), 30 deletions(-) diff --git a/lnbits/extensions/lnurldevice/crud.py b/lnbits/extensions/lnurldevice/crud.py index 818b99fd..e02d23b8 100644 --- a/lnbits/extensions/lnurldevice/crud.py +++ b/lnbits/extensions/lnurldevice/crud.py @@ -61,7 +61,7 @@ async def create_lnurldevice( data.pin3, data.profit4, data.amount4, - data.pin4 + data.pin4, ), ) return await get_lnurldevice(lnurldevice_id) diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py index ce2d1c69..6a80f74e 100644 --- a/lnbits/extensions/lnurldevice/lnurl.py +++ b/lnbits/extensions/lnurldevice/lnurl.py @@ -9,7 +9,7 @@ from embit import bech32, compact from fastapi import Request from fastapi.param_functions import Query from starlette.exceptions import HTTPException -from loguru import logger +from loguru import logger from lnbits.core.services import create_invoice from lnbits.core.views.api import pay_invoice @@ -249,13 +249,13 @@ async def lnurl_callback( memo=device.id + " PIN " + str(lnurldevicepayment.pin), unhashed_description=device.lnurlpay_metadata.encode("utf-8"), extra={ - "tag": "Switch", + "tag": "Switch", "pin": str(lnurldevicepayment.pin), "amount": str(lnurldevicepayment.payload), - "id": paymentid - }, + "id": paymentid, + }, ) - + lnurldevicepayment = await update_lnurldevicepayment( lnurldevicepayment_id=paymentid, payhash=payment_hash ) diff --git a/lnbits/extensions/lnurldevice/migrations.py b/lnbits/extensions/lnurldevice/migrations.py index f29bca99..1df04075 100644 --- a/lnbits/extensions/lnurldevice/migrations.py +++ b/lnbits/extensions/lnurldevice/migrations.py @@ -89,25 +89,51 @@ async def m003_redux(db): "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount INT DEFAULT 0;" ) + async def m004_redux(db): """ Add 'meta' for storing various metadata about the wallet """ - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin INT DEFAULT 0") - - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit1 FLOAT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount1 INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin1 INT DEFAULT 0") + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin INT DEFAULT 0" + ) - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit2 FLOAT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount2 INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin2 INT DEFAULT 0") - - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit3 FLOAT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount3 INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin3 INT DEFAULT 0") - - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit4 FLOAT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount4 INT DEFAULT 0") - await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin4 INT DEFAULT 0") - \ No newline at end of file + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit1 FLOAT DEFAULT 0" + ) + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount1 INT DEFAULT 0" + ) + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin1 INT DEFAULT 0" + ) + + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit2 FLOAT DEFAULT 0" + ) + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount2 INT DEFAULT 0" + ) + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin2 INT DEFAULT 0" + ) + + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit3 FLOAT DEFAULT 0" + ) + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount3 INT DEFAULT 0" + ) + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin3 INT DEFAULT 0" + ) + + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN profit4 FLOAT DEFAULT 0" + ) + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount4 INT DEFAULT 0" + ) + await db.execute( + "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN pin4 INT DEFAULT 0" + ) diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py index 29d49d3f..1b7c0ab8 100644 --- a/lnbits/extensions/lnurldevice/models.py +++ b/lnbits/extensions/lnurldevice/models.py @@ -11,6 +11,7 @@ from pydantic import BaseModel from pydantic.main import BaseModel from loguru import logger + class createLnurldevice(BaseModel): title: str wallet: str @@ -68,21 +69,97 @@ class lnurldevices(BaseModel): switches = [] if self.profit > 0: url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) - switches.append([str(self.pin), str(self.profit), str(self.amount), lnurl_encode(url + "?gpio=" + str(self.pin) + "&profit=" + str(self.profit) + "&amount=" + str(self.amount))]) + switches.append( + [ + str(self.pin), + str(self.profit), + str(self.amount), + lnurl_encode( + url + + "?gpio=" + + str(self.pin) + + "&profit=" + + str(self.profit) + + "&amount=" + + str(self.amount) + ), + ] + ) if self.profit1 > 0: url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) - switches.append([str(self.pin1), str(self.profit1), str(self.amount1), lnurl_encode(url + "?gpio=" + str(self.pin1) + "&profit=" + str(self.profit1) + "&amount=" + str(self.amount1))]) + switches.append( + [ + str(self.pin1), + str(self.profit1), + str(self.amount1), + lnurl_encode( + url + + "?gpio=" + + str(self.pin1) + + "&profit=" + + str(self.profit1) + + "&amount=" + + str(self.amount1) + ), + ] + ) if self.profit2 > 0: url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) - switches.append([str(self.pin2), str(self.profit2), str(self.amount2), lnurl_encode(url + "?gpio=" + str(self.pin2) + "&profit=" + str(self.profit2) + "&amount=" + str(self.amount2))]) + switches.append( + [ + str(self.pin2), + str(self.profit2), + str(self.amount2), + lnurl_encode( + url + + "?gpio=" + + str(self.pin2) + + "&profit=" + + str(self.profit2) + + "&amount=" + + str(self.amount2) + ), + ] + ) if self.profit3 > 0: url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) - switches.append([str(self.pin3), str(self.profit3), str(self.amount3), lnurl_encode(url + "?gpio=" + str(self.pin3) + "&profit=" + str(self.profit3) + "&amount=" + str(self.amount3))]) + switches.append( + [ + str(self.pin3), + str(self.profit3), + str(self.amount3), + lnurl_encode( + url + + "?gpio=" + + str(self.pin3) + + "&profit=" + + str(self.profit3) + + "&amount=" + + str(self.amount3) + ), + ] + ) if self.profit4 > 0: url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id) - switches.append([str(self.pin4), str(self.profit4), str(self.amount4), lnurl_encode(url + "?gpio=" + str(self.pin4) + "&profit=" + str(self.profit4) + "&amount=" + str(self.amount4))]) + switches.append( + [ + str(self.pin4), + str(self.profit4), + str(self.amount4), + lnurl_encode( + url + + "?gpio=" + + str(self.pin4) + + "&profit=" + + str(self.profit4) + + "&amount=" + + str(self.amount4) + ), + ] + ) return switches + class lnurldevicepayment(BaseModel): id: str deviceid: str diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py index ca7aabb4..d3248ad5 100644 --- a/lnbits/extensions/lnurldevice/tasks.py +++ b/lnbits/extensions/lnurldevice/tasks.py @@ -36,5 +36,9 @@ async def on_invoice_paid(payment: Payment) -> None: lnurldevicepayment = await update_lnurldevicepayment( lnurldevicepayment_id=payment.extra.get("id"), payhash="used" ) - return await updater(lnurldevicepayment.deviceid, lnurldevicepayment.pin, lnurldevicepayment.payload) + return await updater( + lnurldevicepayment.deviceid, + lnurldevicepayment.pin, + lnurldevicepayment.payload, + ) return diff --git a/lnbits/extensions/lnurldevice/views.py b/lnbits/extensions/lnurldevice/views.py index f5fd598c..f435931b 100644 --- a/lnbits/extensions/lnurldevice/views.py +++ b/lnbits/extensions/lnurldevice/views.py @@ -107,4 +107,6 @@ async def updater(lnurldevice_id, lnurldevice_pin, lnurldevice_amount): lnurldevice = await get_lnurldevice(lnurldevice_id) if not lnurldevice: return - return await manager.send_personal_message(f"{lnurldevice_pin}-{lnurldevice_amount}", lnurldevice_id) + return await manager.send_personal_message( + f"{lnurldevice_pin}-{lnurldevice_amount}", lnurldevice_id + ) From 1ab0d0f70132ce5d75f41d4ecb3df37135695a4b Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 27 Oct 2022 01:05:09 +0100 Subject: [PATCH 28/96] isort --- lnbits/extensions/lnurldevice/lnurl.py | 12 ++++-------- lnbits/extensions/lnurldevice/models.py | 4 ++-- lnbits/extensions/lnurldevice/tasks.py | 3 ++- lnbits/extensions/lnurldevice/views_api.py | 9 ++------- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py index 6a80f74e..bdbcbdaa 100644 --- a/lnbits/extensions/lnurldevice/lnurl.py +++ b/lnbits/extensions/lnurldevice/lnurl.py @@ -8,21 +8,17 @@ from typing import Optional from embit import bech32, compact from fastapi import Request from fastapi.param_functions import Query -from starlette.exceptions import HTTPException from loguru import logger +from starlette.exceptions import HTTPException from lnbits.core.services import create_invoice from lnbits.core.views.api import pay_invoice from lnbits.utils.exchange_rates import fiat_amount_as_satoshis from . import lnurldevice_ext -from .crud import ( - create_lnurldevicepayment, - get_lnurldevice, - get_lnurldevicepayment, - get_lnurlpayload, - update_lnurldevicepayment, -) +from .crud import (create_lnurldevicepayment, get_lnurldevice, + get_lnurldevicepayment, get_lnurlpayload, + update_lnurldevicepayment) def bech32_decode(bech): diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py index 1b7c0ab8..c27470b7 100644 --- a/lnbits/extensions/lnurldevice/models.py +++ b/lnbits/extensions/lnurldevice/models.py @@ -1,15 +1,15 @@ import json from sqlite3 import Row -from typing import Optional, List +from typing import List, Optional from fastapi import Request from lnurl import Lnurl from lnurl import encode as lnurl_encode # type: ignore from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore from lnurl.types import LnurlPayMetadata # type: ignore +from loguru import logger from pydantic import BaseModel from pydantic.main import BaseModel -from loguru import logger class createLnurldevice(BaseModel): diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py index d3248ad5..2c2ac713 100644 --- a/lnbits/extensions/lnurldevice/tasks.py +++ b/lnbits/extensions/lnurldevice/tasks.py @@ -12,7 +12,8 @@ from lnbits.core.services import pay_invoice from lnbits.helpers import get_current_extension_name from lnbits.tasks import register_invoice_listener -from .crud import get_lnurldevice, get_lnurldevicepayment, update_lnurldevicepayment +from .crud import (get_lnurldevice, get_lnurldevicepayment, + update_lnurldevicepayment) from .views import updater diff --git a/lnbits/extensions/lnurldevice/views_api.py b/lnbits/extensions/lnurldevice/views_api.py index c6766423..d0ea8659 100644 --- a/lnbits/extensions/lnurldevice/views_api.py +++ b/lnbits/extensions/lnurldevice/views_api.py @@ -11,13 +11,8 @@ from lnbits.extensions.lnurldevice import lnurldevice_ext from lnbits.utils.exchange_rates import currencies from . import lnurldevice_ext -from .crud import ( - create_lnurldevice, - delete_lnurldevice, - get_lnurldevice, - get_lnurldevices, - update_lnurldevice, -) +from .crud import (create_lnurldevice, delete_lnurldevice, get_lnurldevice, + get_lnurldevices, update_lnurldevice) from .models import createLnurldevice From 8bafb9ab1992deaa7c9fe4c0cf9e01263ec10703 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 27 Oct 2022 01:10:03 +0100 Subject: [PATCH 29/96] black --- lnbits/extensions/lnurldevice/lnurl.py | 10 +++++++--- lnbits/extensions/lnurldevice/tasks.py | 3 +-- lnbits/extensions/lnurldevice/views_api.py | 9 +++++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py index bdbcbdaa..c8f9675e 100644 --- a/lnbits/extensions/lnurldevice/lnurl.py +++ b/lnbits/extensions/lnurldevice/lnurl.py @@ -16,9 +16,13 @@ from lnbits.core.views.api import pay_invoice from lnbits.utils.exchange_rates import fiat_amount_as_satoshis from . import lnurldevice_ext -from .crud import (create_lnurldevicepayment, get_lnurldevice, - get_lnurldevicepayment, get_lnurlpayload, - update_lnurldevicepayment) +from .crud import ( + create_lnurldevicepayment, + get_lnurldevice, + get_lnurldevicepayment, + get_lnurlpayload, + update_lnurldevicepayment, +) def bech32_decode(bech): diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py index 2c2ac713..d3248ad5 100644 --- a/lnbits/extensions/lnurldevice/tasks.py +++ b/lnbits/extensions/lnurldevice/tasks.py @@ -12,8 +12,7 @@ from lnbits.core.services import pay_invoice from lnbits.helpers import get_current_extension_name from lnbits.tasks import register_invoice_listener -from .crud import (get_lnurldevice, get_lnurldevicepayment, - update_lnurldevicepayment) +from .crud import get_lnurldevice, get_lnurldevicepayment, update_lnurldevicepayment from .views import updater diff --git a/lnbits/extensions/lnurldevice/views_api.py b/lnbits/extensions/lnurldevice/views_api.py index d0ea8659..c6766423 100644 --- a/lnbits/extensions/lnurldevice/views_api.py +++ b/lnbits/extensions/lnurldevice/views_api.py @@ -11,8 +11,13 @@ from lnbits.extensions.lnurldevice import lnurldevice_ext from lnbits.utils.exchange_rates import currencies from . import lnurldevice_ext -from .crud import (create_lnurldevice, delete_lnurldevice, get_lnurldevice, - get_lnurldevices, update_lnurldevice) +from .crud import ( + create_lnurldevice, + delete_lnurldevice, + get_lnurldevice, + get_lnurldevices, + update_lnurldevice, +) from .models import createLnurldevice From 904dea37fc25641f3c283a3850af22474700d751 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 27 Oct 2022 01:16:05 +0100 Subject: [PATCH 30/96] prettier --- .../templates/lnurldevice/index.html | 317 ++++++++++-------- 1 file changed, 168 insertions(+), 149 deletions(-) diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html index 17ce9f28..83ff4571 100644 --- a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html +++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html @@ -230,201 +230,217 @@ label="Profit margin (% added to invoices/deducted from faucets)" >
- - - - + +
-
+
+ ref="setAmount" + filled + dense + v-model.trim="formDialoglnurldevice.data.profit" + class="q-pb-md" + :label="'Amount (' + formDialoglnurldevice.data.currency + ') *'" + :mask="'#.##'" + fill-mask="0" + reverse-fill-mask + :step="'0.01'" + value="0.00" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.amount" + type="number" + value="1000" + label="milesecs to turn Switch on for (1sec = 1000ms)" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.pin" + type="number" + label="GPIO to turn on" + >
-
+
+ ref="setAmount" + filled + dense + v-model.trim="formDialoglnurldevice.data.profit1" + class="q-pb-md" + :label="'Amount (' + formDialoglnurldevice.data.currency + ') *'" + :mask="'#.##'" + fill-mask="0" + reverse-fill-mask + :step="'0.01'" + value="0.00" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.amount1" + type="number" + value="1000" + label="milesecs to turn Switch on for (1sec = 1000ms)" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.pin1" + type="number" + label="GPIO to turn on" + >
-
+
+ ref="setAmount" + filled + dense + v-model.trim="formDialoglnurldevice.data.profit2" + class="q-pb-md" + :label="'Amount (' + formDialoglnurldevice.data.currency + ') *'" + :mask="'#.##'" + fill-mask="0" + reverse-fill-mask + :step="'0.01'" + value="0.00" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.amount2" + type="number" + value="1000" + label="milesecs to turn Switch on for (1sec = 1000ms)" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.pin2" + type="number" + label="GPIO to turn on" + >
-
+
-
+
+ ref="setAmount" + filled + dense + v-model.trim="formDialoglnurldevice.data.profit3" + class="q-pb-md" + :label="'Amount (' + formDialoglnurldevice.data.currency + ') *'" + :mask="'#.##'" + fill-mask="0" + reverse-fill-mask + :step="'0.01'" + value="0.00" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.amount3" + type="number" + value="1000" + label="milesecs to turn Switch on for (1sec = 1000ms)" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.pin3" + type="number" + label="GPIO to turn on" + >
-
+
-
+
+ ref="setAmount" + filled + dense + v-model.trim="formDialoglnurldevice.data.profit4" + class="q-pb-md" + :label="'Amount (' + formDialoglnurldevice.data.currency + ') *'" + :mask="'#.##'" + fill-mask="0" + reverse-fill-mask + :step="'0.01'" + value="0.00" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.amount4" + type="number" + value="1000" + label="milesecs to turn Switch on for (1sec = 1000ms)" + >
+ filled + dense + v-model.trim="formDialoglnurldevice.data.pin4" + type="number" + label="GPIO to turn on" + >
@@ -458,7 +474,6 @@ - Copy LNURL -
+ outline + color="grey" + @click="copyText(lnurlValue, 'LNURL copied to clipboard!')" + >Copy LNURL +
- - + Close
-
@@ -615,7 +634,7 @@ this.lnurlValueFetch(this.qrCodeDialog.data.switches[0][3]) this.qrCodeDialog.show = true }, - lnurlValueFetch: function (lnurl){ + lnurlValueFetch: function (lnurl) { this.lnurlValue = lnurl }, addSwitch: function () { @@ -682,9 +701,9 @@ .then(function (response) { if (response.data) { self.lnurldeviceLinks = response.data.map(maplnurldevice) - console.log("response.data") + console.log('response.data') console.log(response.data) - console.log("response.data") + console.log('response.data') } }) .catch(function (error) { From 7be45d5d912a94b704178298c715676c8f484418 Mon Sep 17 00:00:00 2001 From: Arc <33088785+arcbtc@users.noreply.github.com> Date: Fri, 28 Oct 2022 10:22:59 +0100 Subject: [PATCH 31/96] Update installation.md --- docs/guide/installation.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 11ded8e8..bf40418d 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -48,7 +48,9 @@ poetry run lnbits # Note that you have to add the line DEBUG=true in your .env file, too. ``` -## Option 2: Nix +## Option 2: Nix + +> note: currently not supported while we make some architectural changes on the path to leave beta ```sh git clone https://github.com/lnbits/lnbits-legend.git From b92fedbcc9f22415ed7b34d26a83e1c4f2ce8864 Mon Sep 17 00:00:00 2001 From: calle <93376500+callebtc@users.noreply.github.com> Date: Fri, 28 Oct 2022 11:26:02 +0200 Subject: [PATCH 32/96] refactor try except block (#1084) --- lnbits/extensions/withdraw/lnurl.py | 55 +++++++++++++++-------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index 18a99599..660e5b7d 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -78,34 +78,35 @@ async def api_lnurl_callback( return {"status": "ERROR", "reason": f"Wait {link.open_time - now} seconds."} usescsv = "" + + for x in range(1, link.uses - link.used): + usecv = link.usescsv.split(",") + usescsv += "," + str(usecv[x]) + usecsvback = usescsv + + found = False + if id_unique_hash is not None: + useslist = link.usescsv.split(",") + for ind, x in enumerate(useslist): + tohash = link.id + link.unique_hash + str(x) + if id_unique_hash == shortuuid.uuid(name=tohash): + found = True + useslist.pop(ind) + usescsv = ",".join(useslist) + if not found: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found." + ) + else: + usescsv = usescsv[1:] + + changesback = { + "open_time": link.wait_time, + "used": link.used, + "usescsv": usecsvback, + } + try: - for x in range(1, link.uses - link.used): - usecv = link.usescsv.split(",") - usescsv += "," + str(usecv[x]) - usecsvback = usescsv - - found = False - if id_unique_hash is not None: - useslist = link.usescsv.split(",") - for ind, x in enumerate(useslist): - tohash = link.id + link.unique_hash + str(x) - if id_unique_hash == shortuuid.uuid(name=tohash): - found = True - useslist.pop(ind) - usescsv = ",".join(useslist) - if not found: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found." - ) - else: - usescsv = usescsv[1:] - - changesback = { - "open_time": link.wait_time, - "used": link.used, - "usescsv": usecsvback, - } - changes = { "open_time": link.wait_time + now, "used": link.used + 1, From 4ee571ca7139ffbfb132522fb29d7d1f9f23ea8a Mon Sep 17 00:00:00 2001 From: ChuckNorrison <2964146+ChuckNorrison@users.noreply.github.com> Date: Mon, 7 Nov 2022 11:30:18 +0100 Subject: [PATCH 33/96] Documentation for postgresql default port (#1106) * Documentation for postgresql default port A port is necessary for postgresql config. Add postgresql default port 5432 in documentation for LNBITS_DATABASE_URL config * Update docs/guide/installation.md Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> --- docs/guide/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index bf40418d..072c4d91 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -220,8 +220,8 @@ You need to edit the `.env` file. ```sh # add the database connection string to .env 'nano .env' LNBITS_DATABASE_URL= -# postgres://:@/ - alter line bellow with your user, password and db name -LNBITS_DATABASE_URL="postgres://postgres:postgres@localhost/lnbits" +# postgres://:@:/ - alter line bellow with your user, password and db name +LNBITS_DATABASE_URL="postgres://postgres:postgres@localhost:5432/lnbits" # save and exit ``` From c3ef4d726038aa81b9298afce682d573f9aa12d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 7 Nov 2022 11:30:44 +0100 Subject: [PATCH 34/96] formatting fixes, prettier (#1108) --- .../lnurldevice/templates/lnurldevice/index.html | 2 +- .../templates/usermanager/_api_docs.html | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html index 83ff4571..b0b223ff 100644 --- a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html +++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html @@ -797,7 +797,7 @@ LNbits.utils.notifyApiError(error) }) }, - clearFormDialoglnurldevice () { + clearFormDialoglnurldevice() { this.formDialoglnurldevice.data = { lnurl_toggle: false, show_message: false, diff --git a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html index de477834..36593d74 100644 --- a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html +++ b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html @@ -57,13 +57,14 @@ /usermanager/api/v1/users/<user_id>
Body (application/json)
- +
Returns 200 OK (application/json)
{"id": <string>, "name": <string>, "admin": - <string>, "email": <string>, "password": <string>}
Curl example
@@ -259,15 +260,15 @@ {"X-Api-Key": <string>}
Curl example
curl -X POST {{ request.base_url }}usermanager/api/v1/extensions?extension=withdraw&userid=user_id&active=true -H "X-Api-Key: {{ user.wallets[0].inkey }}" -H - "Content-type: application/json" + >curl -X POST {{ request.base_url + }}usermanager/api/v1/extensions?extension=withdraw&userid=user_id&active=true + -H "X-Api-Key: {{ user.wallets[0].inkey }}" -H "Content-type: + application/json"
Returns 200 OK (application/json)
- {"extension": "updated"} + {"extension": "updated"} From 0ea225b87e19e2e2b72657f3da0f8095b99d5257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 7 Nov 2022 13:31:42 +0100 Subject: [PATCH 35/96] add api docs button --- lnbits/templates/base.html | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lnbits/templates/base.html b/lnbits/templates/base.html index 67241bb5..ef270371 100644 --- a/lnbits/templates/base.html +++ b/lnbits/templates/base.html @@ -199,6 +199,18 @@ > + + API DOCS + View LNbits Swagger API docs + Date: Tue, 8 Nov 2022 01:15:54 +0100 Subject: [PATCH 36/96] fix sys is not defined --- tools/conv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/conv.py b/tools/conv.py index 5084660f..cb8f0573 100644 --- a/tools/conv.py +++ b/tools/conv.py @@ -24,7 +24,7 @@ sqfolder = env.str("LNBITS_DATA_FOLDER", default=None) LNBITS_DATABASE_URL = env.str("LNBITS_DATABASE_URL", default=None) if LNBITS_DATABASE_URL is None: print("missing LNBITS_DATABASE_URL") - sys.exit(1) + os.sys.exit(1) else: # parse postgres://lnbits:postgres@localhost:5432/lnbits pgdb = LNBITS_DATABASE_URL.split("/")[-1] From 61dd9817b6ed966df90f175520da046c47ee28e6 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 8 Nov 2022 13:47:03 +0000 Subject: [PATCH 37/96] fixed atm --- lnbits/extensions/lnurldevice/lnurl.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py index c8f9675e..dd8dcb08 100644 --- a/lnbits/extensions/lnurldevice/lnurl.py +++ b/lnbits/extensions/lnurldevice/lnurl.py @@ -105,9 +105,9 @@ async def lnurl_v1_params( paymentcheck = await get_lnurlpayload(p) if device.device == "atm": if paymentcheck: - return {"status": "ERROR", "reason": f"Payment already claimed"} + if paymentcheck.payhash != "payment_hash": + return {"status": "ERROR", "reason": f"Payment already claimed"} if device.device == "switch": - price_msat = ( await fiat_amount_as_satoshis(float(profit), device.currency) if device.currency != "sat" @@ -177,7 +177,7 @@ async def lnurl_v1_params( "callback": request.url_for( "lnurldevice.lnurl_callback", paymentid=lnurldevicepayment.id ), - "k1": lnurldevicepayment.id, + "k1": p, "minWithdrawable": price_msat * 1000, "maxWithdrawable": price_msat * 1000, "defaultDescription": device.title, @@ -227,14 +227,13 @@ async def lnurl_callback( status_code=HTTPStatus.FORBIDDEN, detail="No payment request" ) else: - if lnurldevicepayment.id != k1: + if lnurldevicepayment.payload != k1: return {"status": "ERROR", "reason": "Bad K1"} if lnurldevicepayment.payhash != "payment_hash": return {"status": "ERROR", "reason": f"Payment already claimed"} lnurldevicepayment = await update_lnurldevicepayment( lnurldevicepayment_id=paymentid, payhash=lnurldevicepayment.payload ) - await pay_invoice( wallet_id=device.wallet, payment_request=pr, From 0ae11df52f62d670a4759eb72a6e41460e809e9f Mon Sep 17 00:00:00 2001 From: Bitkarrot <73979971+bitkarrot@users.noreply.github.com> Date: Thu, 10 Nov 2022 22:22:17 -0800 Subject: [PATCH 38/96] Docker file fix for missing directory Currently Docker will error and exit because `data` directory is not created. This commit adds automatic `data` directory creation to Dockerfile so that the following docker command will not error. ``` docker run --detach --publish 5000:5000 --name lnbits-legend --volume ${PWD}/.env:/app/.env --volume ${PWD}/data/:/app/data lnbits-legend ``` --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 6259fe7b..af3f3616 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,7 @@ RUN curl -sSL https://install.python-poetry.org | python3 - ENV PATH="/root/.local/bin:$PATH" WORKDIR /app +RUN mkdir -p /app/lnbits/data COPY . . From db4232f2ced4e7e062e3e77ac14a8f1cb7998c73 Mon Sep 17 00:00:00 2001 From: ChuckNorrison <2964146+ChuckNorrison@users.noreply.github.com> Date: Fri, 11 Nov 2022 12:06:35 +0100 Subject: [PATCH 39/96] Documentation improved in example env LNBITS_ALLOWED_USERS and LNBITS_ADMIN_USERS wants usr string from wallet url List needs to be comma separated if used more than one user --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index 4edaea97..e76296ab 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,7 @@ PORT=5000 DEBUG=false +# Find "usr" string in wallet url to explicit allow users or set admins (comma separated list) LNBITS_ALLOWED_USERS="" LNBITS_ADMIN_USERS="" # Extensions only admin can access From 029e8baf5310a664cfc809b768b80974bb291563 Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 11 Nov 2022 23:46:22 +0000 Subject: [PATCH 40/96] added tasks to make tickets --- lnbits/extensions/events/__init__.py | 10 +++++++ lnbits/extensions/events/tasks.py | 40 +++++++++++++++++++++++++++ lnbits/extensions/events/views_api.py | 10 +++---- 3 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 lnbits/extensions/events/tasks.py diff --git a/lnbits/extensions/events/__init__.py b/lnbits/extensions/events/__init__.py index d0aa27bc..c64a866b 100644 --- a/lnbits/extensions/events/__init__.py +++ b/lnbits/extensions/events/__init__.py @@ -1,7 +1,11 @@ +import asyncio + from fastapi import APIRouter from lnbits.db import Database from lnbits.helpers import template_renderer +from lnbits.tasks import catch_everything_and_restart + db = Database("ext_events") @@ -13,5 +17,11 @@ def events_renderer(): return template_renderer(["lnbits/extensions/events/templates"]) +from .tasks import wait_for_paid_invoices from .views import * # noqa from .views_api import * # noqa + + +def events_start(): + loop = asyncio.get_event_loop() + loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) diff --git a/lnbits/extensions/events/tasks.py b/lnbits/extensions/events/tasks.py new file mode 100644 index 00000000..1dcca9fa --- /dev/null +++ b/lnbits/extensions/events/tasks.py @@ -0,0 +1,40 @@ +import asyncio +import json +from http import HTTPStatus +from urllib.parse import urlparse + +import httpx +from fastapi import HTTPException + +from lnbits import bolt11 +from lnbits.core.models import Payment +from lnbits.core.services import pay_invoice +from lnbits.helpers import get_current_extension_name +from lnbits.tasks import register_invoice_listener + +from .views_api import api_ticket_send_ticket +from loguru import logger +from lnbits.extensions.events.models import CreateTicket + + +async def wait_for_paid_invoices(): + invoice_queue = asyncio.Queue() + register_invoice_listener(invoice_queue, get_current_extension_name()) + + while True: + payment = await invoice_queue.get() + await on_invoice_paid(payment) + + +async def on_invoice_paid(payment: Payment) -> None: + # (avoid loops) + logger.debug(f"cunt: sdvcsd") + if ( + "events" == payment.extra.get("tag") + and payment.extra.get("name") + and payment.extra.get("email") + ): + CreateTicket.name = str(payment.extra.get("name")) + CreateTicket.email = str(payment.extra.get("email")) + await api_ticket_send_ticket(payment.memo, payment.payment_hash, CreateTicket) + return diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py index 9cb18f04..08651f5d 100644 --- a/lnbits/extensions/events/views_api.py +++ b/lnbits/extensions/events/views_api.py @@ -10,6 +10,7 @@ from lnbits.core.services import create_invoice from lnbits.core.views.api import api_payment from lnbits.decorators import WalletTypeInfo, get_key_type from lnbits.extensions.events.models import CreateEvent, CreateTicket +from loguru import logger from . import events_ext from .crud import ( @@ -96,8 +97,8 @@ async def api_tickets( return [ticket.dict() for ticket in await get_tickets(wallet_ids)] -@events_ext.get("/api/v1/tickets/{event_id}") -async def api_ticket_make_ticket(event_id): +@events_ext.get("/api/v1/tickets/{event_id}/{name}/{email}") +async def api_ticket_make_ticket(event_id, name, email): event = await get_event(event_id) if not event: raise HTTPException( @@ -108,11 +109,10 @@ async def api_ticket_make_ticket(event_id): wallet_id=event.wallet, amount=event.price_per_ticket, memo=f"{event_id}", - extra={"tag": "events"}, + extra={"tag": "events", "name": name, "email": email}, ) except Exception as e: raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) - return {"payment_hash": payment_hash, "payment_request": payment_request} @@ -156,7 +156,7 @@ async def api_ticket_delete(ticket_id, wallet: WalletTypeInfo = Depends(get_key_ ) await delete_ticket(ticket_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT # Event Tickets From 9f84e7e408ac69f6e61b425ccfbe060557df5d6b Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 11 Nov 2022 23:59:29 +0000 Subject: [PATCH 41/96] typo --- lnbits/extensions/events/tasks.py | 1 - lnbits/extensions/events/templates/events/display.html | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lnbits/extensions/events/tasks.py b/lnbits/extensions/events/tasks.py index 1dcca9fa..2068b3fa 100644 --- a/lnbits/extensions/events/tasks.py +++ b/lnbits/extensions/events/tasks.py @@ -28,7 +28,6 @@ async def wait_for_paid_invoices(): async def on_invoice_paid(payment: Payment) -> None: # (avoid loops) - logger.debug(f"cunt: sdvcsd") if ( "events" == payment.extra.get("tag") and payment.extra.get("name") diff --git a/lnbits/extensions/events/templates/events/display.html b/lnbits/extensions/events/templates/events/display.html index 4589c578..9dbb95bc 100644 --- a/lnbits/extensions/events/templates/events/display.html +++ b/lnbits/extensions/events/templates/events/display.html @@ -135,7 +135,7 @@ var self = this axios - .get('/events/api/v1/tickets/' + '{{ event_id }}') + .get('/events/api/v1/tickets/' + '{{ event_id }}' + '/' + self.formDialog.data.name + '/' + self.formDialog.data.email) .then(function (response) { self.paymentReq = response.data.payment_request self.paymentCheck = response.data.payment_hash From f3b720b690c533b4b28793209f5a71fd01b9af6e Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Sat, 12 Nov 2022 17:11:24 +0000 Subject: [PATCH 42/96] Fix usermanager API keys (#1117) * fix wrong API keys on examples * add hability to keep wallet after user delete #344 * make format --- lnbits/extensions/usermanager/crud.py | 9 +++++---- .../templates/usermanager/_api_docs.html | 13 ++++++------- lnbits/extensions/usermanager/views_api.py | 10 ++++++---- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lnbits/extensions/usermanager/crud.py b/lnbits/extensions/usermanager/crud.py index 1ce66d4f..649888a8 100644 --- a/lnbits/extensions/usermanager/crud.py +++ b/lnbits/extensions/usermanager/crud.py @@ -63,10 +63,11 @@ async def get_usermanager_users(user_id: str) -> List[Users]: return [Users(**row) for row in rows] -async def delete_usermanager_user(user_id: str) -> None: - wallets = await get_usermanager_wallets(user_id) - for wallet in wallets: - await delete_wallet(user_id=user_id, wallet_id=wallet.id) +async def delete_usermanager_user(user_id: str, delete_core: bool = True) -> None: + if delete_core: + wallets = await get_usermanager_wallets(user_id) + for wallet in wallets: + await delete_wallet(user_id=user_id, wallet_id=wallet.id) await db.execute("DELETE FROM usermanager.users WHERE id = ?", (user_id,)) await db.execute("""DELETE FROM usermanager.wallets WHERE "user" = ?""", (user_id,)) diff --git a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html index 36593d74..ff3ba85a 100644 --- a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html +++ b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html @@ -44,7 +44,7 @@
Curl example
curl -X GET {{ request.base_url }}usermanager/api/v1/users -H - "X-Api-Key: {{ user.wallets[0].inkey }}" + "X-Api-Key: {{ user.wallets[0].adminkey }}" @@ -81,7 +81,7 @@ GET - /usermanager/api/v1/wallets/<user_id>
Headers
{"X-Api-Key": <string>} @@ -92,9 +92,8 @@ JSON wallet data
Curl example
curl -X GET {{ request.base_url - }}usermanager/api/v1/wallets/<user_id> -H "X-Api-Key: {{ - user.wallets[0].inkey }}" + >curl -X GET {{ request.base_url }}usermanager/api/v1/wallets -H + "X-Api-Key: {{ user.wallets[0].adminkey }}"
@@ -221,7 +220,7 @@ curl -X DELETE {{ request.base_url }}usermanager/api/v1/users/<user_id> -H "X-Api-Key: {{ - user.wallets[0].inkey }}" + user.wallets[0].adminkey }}" @@ -239,7 +238,7 @@ curl -X DELETE {{ request.base_url }}usermanager/api/v1/wallets/<wallet_id> -H "X-Api-Key: {{ - user.wallets[0].inkey }}" + user.wallets[0].adminkey }}" diff --git a/lnbits/extensions/usermanager/views_api.py b/lnbits/extensions/usermanager/views_api.py index 7e7b7653..b1bf8ef8 100644 --- a/lnbits/extensions/usermanager/views_api.py +++ b/lnbits/extensions/usermanager/views_api.py @@ -52,15 +52,17 @@ async def api_usermanager_users_create( @usermanager_ext.delete("/api/v1/users/{user_id}") async def api_usermanager_users_delete( - user_id, wallet: WalletTypeInfo = Depends(require_admin_key) + user_id, + delete_core: bool = Query(True), + wallet: WalletTypeInfo = Depends(require_admin_key), ): user = await get_usermanager_user(user_id) if not user: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="User does not exist." ) - await delete_usermanager_user(user_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + await delete_usermanager_user(user_id, delete_core) + return "", HTTPStatus.NO_CONTENT # Activate Extension @@ -124,4 +126,4 @@ async def api_usermanager_wallets_delete( status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist." ) await delete_usermanager_wallet(wallet_id, get_wallet.user) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT From 91be3c30b60ef76282ca67a4a31dce9dd37aa05e Mon Sep 17 00:00:00 2001 From: ChuckNorrison <2964146+ChuckNorrison@users.noreply.github.com> Date: Wed, 16 Nov 2022 14:13:25 +0100 Subject: [PATCH 43/96] add missing import sys allow to call sys module directly --- tools/conv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/conv.py b/tools/conv.py index cb8f0573..5a535a8b 100644 --- a/tools/conv.py +++ b/tools/conv.py @@ -1,5 +1,5 @@ import argparse -import os +import os, sys import sqlite3 from typing import List @@ -24,7 +24,7 @@ sqfolder = env.str("LNBITS_DATA_FOLDER", default=None) LNBITS_DATABASE_URL = env.str("LNBITS_DATABASE_URL", default=None) if LNBITS_DATABASE_URL is None: print("missing LNBITS_DATABASE_URL") - os.sys.exit(1) + sys.exit(1) else: # parse postgres://lnbits:postgres@localhost:5432/lnbits pgdb = LNBITS_DATABASE_URL.split("/")[-1] From 00aa0c6cd9e8c23e2ea658e3220caf5e641c8e5d Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 16 Nov 2022 16:32:57 +0200 Subject: [PATCH 44/96] chore: code format --- lnbits/extensions/events/__init__.py | 1 - lnbits/extensions/events/tasks.py | 4 ++-- lnbits/extensions/events/templates/events/display.html | 9 ++++++++- lnbits/extensions/events/views_api.py | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/events/__init__.py b/lnbits/extensions/events/__init__.py index c64a866b..f689aaa6 100644 --- a/lnbits/extensions/events/__init__.py +++ b/lnbits/extensions/events/__init__.py @@ -6,7 +6,6 @@ from lnbits.db import Database from lnbits.helpers import template_renderer from lnbits.tasks import catch_everything_and_restart - db = Database("ext_events") diff --git a/lnbits/extensions/events/tasks.py b/lnbits/extensions/events/tasks.py index 2068b3fa..d29215bf 100644 --- a/lnbits/extensions/events/tasks.py +++ b/lnbits/extensions/events/tasks.py @@ -5,16 +5,16 @@ from urllib.parse import urlparse import httpx from fastapi import HTTPException +from loguru import logger from lnbits import bolt11 from lnbits.core.models import Payment from lnbits.core.services import pay_invoice +from lnbits.extensions.events.models import CreateTicket from lnbits.helpers import get_current_extension_name from lnbits.tasks import register_invoice_listener from .views_api import api_ticket_send_ticket -from loguru import logger -from lnbits.extensions.events.models import CreateTicket async def wait_for_paid_invoices(): diff --git a/lnbits/extensions/events/templates/events/display.html b/lnbits/extensions/events/templates/events/display.html index 9dbb95bc..e65b4e61 100644 --- a/lnbits/extensions/events/templates/events/display.html +++ b/lnbits/extensions/events/templates/events/display.html @@ -135,7 +135,14 @@ var self = this axios - .get('/events/api/v1/tickets/' + '{{ event_id }}' + '/' + self.formDialog.data.name + '/' + self.formDialog.data.email) + .get( + '/events/api/v1/tickets/' + + '{{ event_id }}' + + '/' + + self.formDialog.data.name + + '/' + + self.formDialog.data.email + ) .then(function (response) { self.paymentReq = response.data.payment_request self.paymentCheck = response.data.payment_hash diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py index 08651f5d..30e7962e 100644 --- a/lnbits/extensions/events/views_api.py +++ b/lnbits/extensions/events/views_api.py @@ -2,6 +2,7 @@ from http import HTTPStatus from fastapi.param_functions import Query from fastapi.params import Depends +from loguru import logger from starlette.exceptions import HTTPException from starlette.requests import Request @@ -10,7 +11,6 @@ from lnbits.core.services import create_invoice from lnbits.core.views.api import api_payment from lnbits.decorators import WalletTypeInfo, get_key_type from lnbits.extensions.events.models import CreateEvent, CreateTicket -from loguru import logger from . import events_ext from .crud import ( From 1971b9a24bd53a750ee8520b4714b4068d6f9dda Mon Sep 17 00:00:00 2001 From: ChuckNorrison <2964146+ChuckNorrison@users.noreply.github.com> Date: Wed, 16 Nov 2022 23:06:21 +0100 Subject: [PATCH 45/96] fix integer out of range for apipayments related to #1030, this was missing --- lnbits/core/migrations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index ebecb5e3..d92f384a 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -51,7 +51,7 @@ async def m001_initial(db): f""" CREATE TABLE IF NOT EXISTS apipayments ( payhash TEXT NOT NULL, - amount INTEGER NOT NULL, + amount {db.big_int} NOT NULL, fee INTEGER NOT NULL DEFAULT 0, wallet TEXT NOT NULL, pending BOOLEAN NOT NULL, From fcc787ed35b6190da65395eec0f711d180a2c487 Mon Sep 17 00:00:00 2001 From: bitkarrot <73979971+bitkarrot@users.noreply.github.com> Date: Wed, 16 Nov 2022 15:34:35 -0800 Subject: [PATCH 46/96] update Dockerfile dir to lnbits/data --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index af3f3616..f107f68c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN curl -sSL https://install.python-poetry.org | python3 - ENV PATH="/root/.local/bin:$PATH" WORKDIR /app -RUN mkdir -p /app/lnbits/data +RUN mkdir -p lnbits/data COPY . . From 032df0e50ca0151751ab6675b8c3fb01de9cd56f Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Tue, 8 Nov 2022 20:01:09 +0200 Subject: [PATCH 47/96] feat: allow decimal fee rate --- .../watchonly/static/components/fee-rate/fee-rate.html | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.html b/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.html index c65ad1c4..0df5bebf 100644 --- a/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.html +++ b/lnbits/extensions/watchonly/static/components/fee-rate/fee-rate.html @@ -6,6 +6,7 @@ filled dense v-model.number="feeRate" + step="any" :rules="[val => !!val || 'Field is required']" type="number" label="sats/vbyte" From 0673245d112b4a781cb956a62c62c231d77b758e Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Tue, 8 Nov 2022 10:34:11 +0200 Subject: [PATCH 48/96] chore: update watchonly db --- tests/data/mock_data.zip | Bin 24852 -> 30790 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/data/mock_data.zip b/tests/data/mock_data.zip index d184f94ad6f20b2806bb438d047802f452c0847b..5a52941c74a0ef02265cb3ad046998b113cfa3a0 100644 GIT binary patch delta 18912 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 delta 12346 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 jTQ Date: Wed, 2 Nov 2022 15:30:18 +0000 Subject: [PATCH 49/96] fix raise error on delete request --- lnbits/extensions/bleskomat/views_api.py | 2 +- lnbits/extensions/boltcards/views_api.py | 2 +- lnbits/extensions/discordbot/views_api.py | 4 ++-- lnbits/extensions/events/views_api.py | 2 +- lnbits/extensions/livestream/views_api.py | 8 ++++---- lnbits/extensions/lnaddress/views_api.py | 4 ++-- lnbits/extensions/lnticket/views_api.py | 4 ++-- lnbits/extensions/lnurlpayout/views_api.py | 2 +- lnbits/extensions/offlineshop/views_api.py | 2 +- lnbits/extensions/paywall/views_api.py | 2 +- lnbits/extensions/satspay/views_api.py | 2 +- lnbits/extensions/scrub/views_api.py | 2 +- lnbits/extensions/streamalerts/views_api.py | 4 ++-- lnbits/extensions/subdomains/views_api.py | 4 ++-- lnbits/extensions/tpos/views_api.py | 2 +- 15 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lnbits/extensions/bleskomat/views_api.py b/lnbits/extensions/bleskomat/views_api.py index e29e3fe7..b6e417bb 100644 --- a/lnbits/extensions/bleskomat/views_api.py +++ b/lnbits/extensions/bleskomat/views_api.py @@ -95,4 +95,4 @@ async def api_bleskomat_delete( ) await delete_bleskomat(bleskomat_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT diff --git a/lnbits/extensions/boltcards/views_api.py b/lnbits/extensions/boltcards/views_api.py index 7b8357cf..3dfa6c21 100644 --- a/lnbits/extensions/boltcards/views_api.py +++ b/lnbits/extensions/boltcards/views_api.py @@ -129,7 +129,7 @@ async def api_card_delete(card_id, wallet: WalletTypeInfo = Depends(require_admi raise HTTPException(detail="Not your card.", status_code=HTTPStatus.FORBIDDEN) await delete_card(card_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT @boltcards_ext.get("/api/v1/hits") diff --git a/lnbits/extensions/discordbot/views_api.py b/lnbits/extensions/discordbot/views_api.py index e6d004db..b69c274a 100644 --- a/lnbits/extensions/discordbot/views_api.py +++ b/lnbits/extensions/discordbot/views_api.py @@ -65,7 +65,7 @@ async def api_discordbot_users_delete( status_code=HTTPStatus.NOT_FOUND, detail="User does not exist." ) await delete_discordbot_user(user_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT # Activate Extension @@ -129,4 +129,4 @@ async def api_discordbot_wallets_delete( status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist." ) await delete_discordbot_wallet(wallet_id, get_wallet.user) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py index 30e7962e..668e7f77 100644 --- a/lnbits/extensions/events/views_api.py +++ b/lnbits/extensions/events/views_api.py @@ -79,7 +79,7 @@ async def api_form_delete(event_id, wallet: WalletTypeInfo = Depends(get_key_typ await delete_event(event_id) await delete_event_tickets(event_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT #########Tickets########## diff --git a/lnbits/extensions/livestream/views_api.py b/lnbits/extensions/livestream/views_api.py index cc173a66..0c169a71 100644 --- a/lnbits/extensions/livestream/views_api.py +++ b/lnbits/extensions/livestream/views_api.py @@ -60,14 +60,14 @@ async def api_update_track(track_id, g: WalletTypeInfo = Depends(get_key_type)): ls = await get_or_create_livestream_by_wallet(g.wallet.id) await update_current_track(ls.id, id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT @livestream_ext.put("/api/v1/livestream/fee/{fee_pct}") async def api_update_fee(fee_pct, g: WalletTypeInfo = Depends(get_key_type)): ls = await get_or_create_livestream_by_wallet(g.wallet.id) await update_livestream_fee(ls.id, int(fee_pct)) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT @livestream_ext.post("/api/v1/livestream/tracks") @@ -93,8 +93,8 @@ async def api_add_track( return -@livestream_ext.route("/api/v1/livestream/tracks/{track_id}") +@livestream_ext.delete("/api/v1/livestream/tracks/{track_id}") async def api_delete_track(track_id, g: WalletTypeInfo = Depends(get_key_type)): ls = await get_or_create_livestream_by_wallet(g.wallet.id) await delete_track_from_livestream(ls.id, track_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT diff --git a/lnbits/extensions/lnaddress/views_api.py b/lnbits/extensions/lnaddress/views_api.py index 8f403a38..46ef6b99 100644 --- a/lnbits/extensions/lnaddress/views_api.py +++ b/lnbits/extensions/lnaddress/views_api.py @@ -93,7 +93,7 @@ async def api_domain_delete(domain_id, g: WalletTypeInfo = Depends(get_key_type) raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your domain") await delete_domain(domain_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT # ADDRESSES @@ -253,4 +253,4 @@ async def api_address_delete(address_id, g: WalletTypeInfo = Depends(get_key_typ ) await delete_address(address_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT diff --git a/lnbits/extensions/lnticket/views_api.py b/lnbits/extensions/lnticket/views_api.py index 7c9eb52c..cf6145b3 100644 --- a/lnbits/extensions/lnticket/views_api.py +++ b/lnbits/extensions/lnticket/views_api.py @@ -78,7 +78,7 @@ async def api_form_delete(form_id, wallet: WalletTypeInfo = Depends(get_key_type await delete_form(form_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT #########tickets########## @@ -160,4 +160,4 @@ async def api_ticket_delete(ticket_id, wallet: WalletTypeInfo = Depends(get_key_ raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your ticket.") await delete_ticket(ticket_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT diff --git a/lnbits/extensions/lnurlpayout/views_api.py b/lnbits/extensions/lnurlpayout/views_api.py index 67562909..324eb5dd 100644 --- a/lnbits/extensions/lnurlpayout/views_api.py +++ b/lnbits/extensions/lnurlpayout/views_api.py @@ -80,7 +80,7 @@ async def api_lnurlpayout_delete( ) await delete_lnurlpayout(lnurlpayout_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT @lnurlpayout_ext.get("/api/v1/lnurlpayouts/{lnurlpayout_id}", status_code=HTTPStatus.OK) diff --git a/lnbits/extensions/offlineshop/views_api.py b/lnbits/extensions/offlineshop/views_api.py index 1bd045dd..71583b9e 100644 --- a/lnbits/extensions/offlineshop/views_api.py +++ b/lnbits/extensions/offlineshop/views_api.py @@ -93,7 +93,7 @@ async def api_add_or_update_item( async def api_delete_item(item_id, wallet: WalletTypeInfo = Depends(get_key_type)): shop = await get_or_create_shop_by_wallet(wallet.wallet.id) await delete_item_from_shop(shop.id, item_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT class CreateMethodData(BaseModel): diff --git a/lnbits/extensions/paywall/views_api.py b/lnbits/extensions/paywall/views_api.py index d2d49853..3c1cd8fc 100644 --- a/lnbits/extensions/paywall/views_api.py +++ b/lnbits/extensions/paywall/views_api.py @@ -49,7 +49,7 @@ async def api_paywall_delete( ) await delete_paywall(paywall_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT @paywall_ext.post("/api/v1/paywalls/invoice/{paywall_id}") diff --git a/lnbits/extensions/satspay/views_api.py b/lnbits/extensions/satspay/views_api.py index 73c87e7c..e1b87c41 100644 --- a/lnbits/extensions/satspay/views_api.py +++ b/lnbits/extensions/satspay/views_api.py @@ -94,7 +94,7 @@ async def api_charge_delete(charge_id, wallet: WalletTypeInfo = Depends(get_key_ ) await delete_charge(charge_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT #############################BALANCE########################## diff --git a/lnbits/extensions/scrub/views_api.py b/lnbits/extensions/scrub/views_api.py index 3714a304..cc55c15d 100644 --- a/lnbits/extensions/scrub/views_api.py +++ b/lnbits/extensions/scrub/views_api.py @@ -109,4 +109,4 @@ async def api_link_delete(link_id, wallet: WalletTypeInfo = Depends(require_admi ) await delete_scrub_link(link_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT diff --git a/lnbits/extensions/streamalerts/views_api.py b/lnbits/extensions/streamalerts/views_api.py index bb2998ee..058f5126 100644 --- a/lnbits/extensions/streamalerts/views_api.py +++ b/lnbits/extensions/streamalerts/views_api.py @@ -245,7 +245,7 @@ async def api_delete_donation(donation_id, g: WalletTypeInfo = Depends(get_key_t detail="Not authorized to delete this donation!", ) await delete_donation(donation_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT @streamalerts_ext.delete("/api/v1/services/{service_id}") @@ -262,4 +262,4 @@ async def api_delete_service(service_id, g: WalletTypeInfo = Depends(get_key_typ detail="Not authorized to delete this service!", ) await delete_service(service_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT diff --git a/lnbits/extensions/subdomains/views_api.py b/lnbits/extensions/subdomains/views_api.py index 34d8e75b..b30daabd 100644 --- a/lnbits/extensions/subdomains/views_api.py +++ b/lnbits/extensions/subdomains/views_api.py @@ -81,7 +81,7 @@ async def api_domain_delete( raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your domain.") await delete_domain(domain_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT #########subdomains########## @@ -198,4 +198,4 @@ async def api_subdomain_delete( ) await delete_subdomain(subdomain_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py index b7f14b98..80501491 100644 --- a/lnbits/extensions/tpos/views_api.py +++ b/lnbits/extensions/tpos/views_api.py @@ -51,7 +51,7 @@ async def api_tpos_delete( raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your TPoS.") await delete_tpos(tpos_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + return "", HTTPStatus.NO_CONTENT @tpos_ext.post("/api/v1/tposs/{tpos_id}/invoices", status_code=HTTPStatus.CREATED) From 0b883d02b3f2cd6a302d60ea09c60641306b1911 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Tue, 8 Nov 2022 13:16:08 +0000 Subject: [PATCH 50/96] get payment from db --- lnbits/extensions/tpos/crud.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lnbits/extensions/tpos/crud.py b/lnbits/extensions/tpos/crud.py index 94e2c006..04b5eaa9 100644 --- a/lnbits/extensions/tpos/crud.py +++ b/lnbits/extensions/tpos/crud.py @@ -1,5 +1,6 @@ from typing import List, Optional, Union +from lnbits.core.models import Payment from lnbits.helpers import urlsafe_short_hash from . import db @@ -47,3 +48,12 @@ async def get_tposs(wallet_ids: Union[str, List[str]]) -> List[TPoS]: async def delete_tpos(tpos_id: str) -> None: await db.execute("DELETE FROM tpos.tposs WHERE id = ?", (tpos_id,)) + + +async def get_tpos_payments(tpos_id: str, limit: int = 5): + + rows = await db.fetchall( + f"SELECT * FROM apipayments WHERE extra LIKE '%tposId%' AND extra LIKE '%{tpos_id}%' ORDER BY time DESC LIMIT {limit}" + ) + + return [Payment.from_row(row) for row in rows] From 8d8a309258dcd560d2277a8d0e7c4d1cf1394515 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Tue, 8 Nov 2022 13:16:27 +0000 Subject: [PATCH 51/96] api endpoint to get last payments --- lnbits/extensions/tpos/views_api.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py index 80501491..9167b5a1 100644 --- a/lnbits/extensions/tpos/views_api.py +++ b/lnbits/extensions/tpos/views_api.py @@ -13,7 +13,7 @@ from lnbits.core.views.api import api_payment from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key from . import tpos_ext -from .crud import create_tpos, delete_tpos, get_tpos, get_tposs +from .crud import create_tpos, delete_tpos, get_tpos, get_tpos_payments, get_tposs from .models import CreateTposData, PayLnurlWData @@ -81,6 +81,22 @@ async def api_tpos_create_invoice( return {"payment_hash": payment_hash, "payment_request": payment_request} +@tpos_ext.get("/api/v1/tposs/{tpos_id}/invoices") +async def api_tpos_get_latest_invoices(tpos_id: str = None): + payments = await get_tpos_payments(tpos_id) + + return [ + { + "checking_id": payment.checking_id, + "amount": payment.amount, + "time": payment.time, + "bolt11": payment.bolt11, + "pending": payment.pending, + } + for payment in payments + ] + + @tpos_ext.post( "/api/v1/tposs/{tpos_id}/invoices/{payment_request}/pay", status_code=HTTPStatus.OK ) From 9f7b4c1370a93eb51a419951b2db0d570f572eb2 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Tue, 8 Nov 2022 13:16:47 +0000 Subject: [PATCH 52/96] add button and list --- .../extensions/tpos/templates/tpos/tpos.html | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index ad04b3be..36d76cf7 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -148,6 +148,9 @@
+ + + + + + + + + + + + + {%raw%} + + {{payment.amount / 1000}} sats + {{payment.checking_id}} + + + {{payment.dateFrom}} + + + {%endraw%} + + + + {% endblock %} {% block styles %} @@ -296,6 +322,10 @@ tipAmount: 0.0, hasNFC: false, nfcTagReading: false, + lastPaymentsDialog: { + show: false, + data: [] + }, invoiceDialog: { show: false, data: null, @@ -520,6 +550,25 @@ self.exchangeRate = response.data.data['BTC' + self.currency][self.currency] }) + }, + getLastPayments(){ + return axios + .get(`/tpos/api/v1/tposs/${this.tposId}/invoices`) + .then(res => { + if(res.data && res.data.length){ + let last = [...res.data] + //last.length = Math.min(last.length, 5) + this.lastPaymentsDialog.data = last.map(obj => { + obj.dateFrom = moment(obj.time * 1000).fromNow() + return obj + }) + } + }) + .catch(e => console.error(e)) + }, + showLastPayments(){ + this.getLastPayments() + this.lastPaymentsDialog.show = true } }, created: function () { From 8e85651c4fbefdec0c389af029a419c7ad3966c5 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Tue, 8 Nov 2022 13:18:10 +0000 Subject: [PATCH 53/96] make format --- .../extensions/tpos/templates/tpos/tpos.html | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 36d76cf7..32ed0b88 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -149,7 +149,12 @@
- + {{payment.amount / 1000}} sats - {{payment.checking_id}} + {{payment.checking_id}} {{payment.dateFrom}} - + {%endraw%} - + @@ -551,11 +561,11 @@ response.data.data['BTC' + self.currency][self.currency] }) }, - getLastPayments(){ + getLastPayments() { return axios .get(`/tpos/api/v1/tposs/${this.tposId}/invoices`) .then(res => { - if(res.data && res.data.length){ + if (res.data && res.data.length) { let last = [...res.data] //last.length = Math.min(last.length, 5) this.lastPaymentsDialog.data = last.map(obj => { @@ -566,7 +576,7 @@ }) .catch(e => console.error(e)) }, - showLastPayments(){ + showLastPayments() { this.getLastPayments() this.lastPaymentsDialog.show = true } From a2092675cdceaf974bce48fcee1257107c51361b Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Tue, 8 Nov 2022 15:18:07 +0000 Subject: [PATCH 54/96] added some UI sugar, removed pending tx --- lnbits/extensions/tpos/crud.py | 8 +++++++- lnbits/extensions/tpos/templates/tpos/tpos.html | 11 +++++------ lnbits/extensions/tpos/views_api.py | 1 - 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lnbits/extensions/tpos/crud.py b/lnbits/extensions/tpos/crud.py index 04b5eaa9..c403eb5a 100644 --- a/lnbits/extensions/tpos/crud.py +++ b/lnbits/extensions/tpos/crud.py @@ -53,7 +53,13 @@ async def delete_tpos(tpos_id: str) -> None: async def get_tpos_payments(tpos_id: str, limit: int = 5): rows = await db.fetchall( - f"SELECT * FROM apipayments WHERE extra LIKE '%tposId%' AND extra LIKE '%{tpos_id}%' ORDER BY time DESC LIMIT {limit}" + f""" + SELECT * FROM apipayments + WHERE pending = 'false' + AND extra LIKE '%tposId%' + AND extra LIKE '%{tpos_id}%' + ORDER BY time DESC LIMIT {limit} + """ ) return [Payment.from_row(row) for row in rows] diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 32ed0b88..8fe61452 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -275,17 +275,16 @@ {%raw%} - {{payment.amount / 1000}} sats + {{payment.amount / 1000}} sats {{payment.checking_id}}Hash: {{payment.checking_id.slice(0, 30)}}... {{payment.dateFrom}} - + {%endraw%} diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py index 9167b5a1..206c1956 100644 --- a/lnbits/extensions/tpos/views_api.py +++ b/lnbits/extensions/tpos/views_api.py @@ -90,7 +90,6 @@ async def api_tpos_get_latest_invoices(tpos_id: str = None): "checking_id": payment.checking_id, "amount": payment.amount, "time": payment.time, - "bolt11": payment.bolt11, "pending": payment.pending, } for payment in payments From e667d2dc190edfb94736374bf90643b1553f8bc0 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Thu, 17 Nov 2022 14:59:03 +0200 Subject: [PATCH 55/96] chore: code format --- tools/conv.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/conv.py b/tools/conv.py index 5a535a8b..541a0b75 100644 --- a/tools/conv.py +++ b/tools/conv.py @@ -1,6 +1,7 @@ import argparse -import os, sys +import os import sqlite3 +import sys from typing import List import psycopg2 From 4549190e8688a58f874706c41ee2ddf223586d79 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Thu, 17 Nov 2022 12:59:40 +0000 Subject: [PATCH 56/96] abstract get latest payments for extensions --- lnbits/core/crud.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index bb1ca0c1..881d1001 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -229,6 +229,24 @@ async def get_wallet_payment( return Payment.from_row(row) if row else None +async def get_latest_payments_by_extension(ext_name: str, ext_id: str, limit: int = 5): + rows = await db.fetchall( + f""" + SELECT * FROM apipayments + WHERE pending = 'false' + AND extra LIKE ? + AND extra LIKE ? + ORDER BY time DESC LIMIT {limit} + """, + ( + f"%{ext_name}%", + f"%{ext_id}%", + ), + ) + + return rows + + async def get_payments( *, wallet_id: Optional[str] = None, From facc7bbf5e3e7ffb70da6490f695dafeb5428d9f Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Thu, 17 Nov 2022 13:00:17 +0000 Subject: [PATCH 57/96] remove core db action from extension --- lnbits/extensions/tpos/crud.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/lnbits/extensions/tpos/crud.py b/lnbits/extensions/tpos/crud.py index c403eb5a..94e2c006 100644 --- a/lnbits/extensions/tpos/crud.py +++ b/lnbits/extensions/tpos/crud.py @@ -1,6 +1,5 @@ from typing import List, Optional, Union -from lnbits.core.models import Payment from lnbits.helpers import urlsafe_short_hash from . import db @@ -48,18 +47,3 @@ async def get_tposs(wallet_ids: Union[str, List[str]]) -> List[TPoS]: async def delete_tpos(tpos_id: str) -> None: await db.execute("DELETE FROM tpos.tposs WHERE id = ?", (tpos_id,)) - - -async def get_tpos_payments(tpos_id: str, limit: int = 5): - - rows = await db.fetchall( - f""" - SELECT * FROM apipayments - WHERE pending = 'false' - AND extra LIKE '%tposId%' - AND extra LIKE '%{tpos_id}%' - ORDER BY time DESC LIMIT {limit} - """ - ) - - return [Payment.from_row(row) for row in rows] From 121331fa0b0d884b705eb89ce8160b530c3375c6 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Thu, 17 Nov 2022 13:00:53 +0000 Subject: [PATCH 58/96] refactor get latest payments --- lnbits/extensions/tpos/views_api.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py index 206c1956..e8dec2b4 100644 --- a/lnbits/extensions/tpos/views_api.py +++ b/lnbits/extensions/tpos/views_api.py @@ -7,13 +7,14 @@ from lnurl import decode as decode_lnurl from loguru import logger from starlette.exceptions import HTTPException -from lnbits.core.crud import get_user +from lnbits.core.crud import get_user, get_latest_payments_by_extension from lnbits.core.services import create_invoice from lnbits.core.views.api import api_payment from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key +from lnbits.core.models import Payment from . import tpos_ext -from .crud import create_tpos, delete_tpos, get_tpos, get_tpos_payments, get_tposs +from .crud import create_tpos, delete_tpos, get_tpos, get_tposs from .models import CreateTposData, PayLnurlWData @@ -83,7 +84,12 @@ async def api_tpos_create_invoice( @tpos_ext.get("/api/v1/tposs/{tpos_id}/invoices") async def api_tpos_get_latest_invoices(tpos_id: str = None): - payments = await get_tpos_payments(tpos_id) + payments = [ + Payment.from_row(row) + for row in await get_latest_payments_by_extension( + ext_name="tpos", ext_id=tpos_id + ) + ] return [ { From e93d1b322be5b71567a8e6ca4ed8cd5eadbea74a Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Thu, 17 Nov 2022 13:02:32 +0000 Subject: [PATCH 59/96] make format --- lnbits/extensions/tpos/views_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py index e8dec2b4..204548b1 100644 --- a/lnbits/extensions/tpos/views_api.py +++ b/lnbits/extensions/tpos/views_api.py @@ -7,11 +7,11 @@ from lnurl import decode as decode_lnurl from loguru import logger from starlette.exceptions import HTTPException -from lnbits.core.crud import get_user, get_latest_payments_by_extension +from lnbits.core.crud import get_latest_payments_by_extension, get_user +from lnbits.core.models import Payment from lnbits.core.services import create_invoice from lnbits.core.views.api import api_payment from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key -from lnbits.core.models import Payment from . import tpos_ext from .crud import create_tpos, delete_tpos, get_tpos, get_tposs From 9f03d25a8c7563ea5241c5e40db14cb6c4fe0a59 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Fri, 28 Oct 2022 11:27:11 +0100 Subject: [PATCH 60/96] fix issue with export to CSV --- lnbits/extensions/withdraw/static/js/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/withdraw/static/js/index.js b/lnbits/extensions/withdraw/static/js/index.js index 943e9024..c8d24f10 100644 --- a/lnbits/extensions/withdraw/static/js/index.js +++ b/lnbits/extensions/withdraw/static/js/index.js @@ -290,8 +290,8 @@ new Vue({ }) } }, - exportCSV: function () { - LNbits.utils.exportCSV(this.paywallsTable.columns, this.paywalls) + exportCSV() { + LNbits.utils.exportCSV(this.withdrawLinksTable.columns, this.withdrawLinks) } }, created: function () { From bd85e5dbe37ef1ee818ca333764c982e528633f5 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Thu, 17 Nov 2022 15:05:12 +0200 Subject: [PATCH 61/96] feat: set custom file name to the exported `CSV` --- lnbits/extensions/withdraw/static/js/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/withdraw/static/js/index.js b/lnbits/extensions/withdraw/static/js/index.js index c8d24f10..a3eaa593 100644 --- a/lnbits/extensions/withdraw/static/js/index.js +++ b/lnbits/extensions/withdraw/static/js/index.js @@ -291,7 +291,11 @@ new Vue({ } }, exportCSV() { - LNbits.utils.exportCSV(this.withdrawLinksTable.columns, this.withdrawLinks) + LNbits.utils.exportCSV( + this.withdrawLinksTable.columns, + this.withdrawLinks, + 'withdraw-links' + ) } }, created: function () { From 8370acc1f79b7b362850de8ed043746c386258f0 Mon Sep 17 00:00:00 2001 From: cryptoteun Date: Thu, 1 Sep 2022 21:59:38 +0200 Subject: [PATCH 62/96] added warning to backup keys --- lnbits/extensions/boltcards/templates/boltcards/index.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html index 55cc1e5e..b95a52bf 100644 --- a/lnbits/extensions/boltcards/templates/boltcards/index.html +++ b/lnbits/extensions/boltcards/templates/boltcards/index.html @@ -380,7 +380,10 @@ Lock key: {{ qrCodeDialog.data.k0 }}
Meta key: {{ qrCodeDialog.data.k1 }}
File key: {{ qrCodeDialog.data.k2 }}
+
+ Always backup all keys that you're trying to write on the card. Without them you may not be able to change them in the future!

+
Date: Thu, 17 Nov 2022 15:48:34 +0200 Subject: [PATCH 63/96] chore: code format --- lnbits/extensions/boltcards/templates/boltcards/index.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html index b95a52bf..6398c20e 100644 --- a/lnbits/extensions/boltcards/templates/boltcards/index.html +++ b/lnbits/extensions/boltcards/templates/boltcards/index.html @@ -380,8 +380,9 @@ Lock key: {{ qrCodeDialog.data.k0 }}
Meta key: {{ qrCodeDialog.data.k1 }}
File key: {{ qrCodeDialog.data.k2 }}
-
- Always backup all keys that you're trying to write on the card. Without them you may not be able to change them in the future!
+
+ Always backup all keys that you're trying to write on the card. Without + them you may not be able to change them in the future!


From 2eaf5e5668501c2bfb348d9af34c2b03f3e1a12f Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Thu, 17 Nov 2022 15:33:20 +0000 Subject: [PATCH 64/96] remove commented out code --- lnbits/extensions/tpos/templates/tpos/tpos.html | 1 - 1 file changed, 1 deletion(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 8fe61452..12e18bec 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -566,7 +566,6 @@ .then(res => { if (res.data && res.data.length) { let last = [...res.data] - //last.length = Math.min(last.length, 5) this.lastPaymentsDialog.data = last.map(obj => { obj.dateFrom = moment(obj.time * 1000).fromNow() return obj From ebeecdecca4bd15fff506e6fd383b90bfdf879d9 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Fri, 18 Nov 2022 10:22:11 +0000 Subject: [PATCH 65/96] add "No paid invoices" if... no paid invoices --- lnbits/extensions/tpos/templates/tpos/tpos.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 12e18bec..6bc6a626 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -272,6 +272,13 @@ + + + No paid invoices + + {%raw%} From 28e62f3333f5ea651bae4d52923d59e4f793399d Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Fri, 18 Nov 2022 10:27:23 +0000 Subject: [PATCH 66/96] make format --- lnbits/extensions/tpos/templates/tpos/tpos.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 6bc6a626..c66238f7 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -274,9 +274,7 @@ - No paid invoices + No paid invoices From 79fc0b9be4785f7a8a3f403c785e9029338a4544 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Fri, 18 Nov 2022 10:56:14 +0000 Subject: [PATCH 67/96] wrap db call in try except --- lnbits/extensions/tpos/views_api.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py index 204548b1..fe63a247 100644 --- a/lnbits/extensions/tpos/views_api.py +++ b/lnbits/extensions/tpos/views_api.py @@ -84,12 +84,16 @@ async def api_tpos_create_invoice( @tpos_ext.get("/api/v1/tposs/{tpos_id}/invoices") async def api_tpos_get_latest_invoices(tpos_id: str = None): - payments = [ - Payment.from_row(row) - for row in await get_latest_payments_by_extension( - ext_name="tpos", ext_id=tpos_id - ) - ] + try: + payments = [ + Payment.from_row(row) + for row in await get_latest_payments_by_extension( + ext_name="tpos", ext_id=tpos_id + ) + ] + + except Exception as e: + raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) return [ { From b0b14464835a9b460407d1c8fe4d142b43bbf3a2 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Thu, 27 Oct 2022 19:14:26 +0100 Subject: [PATCH 68/96] fixed display amount --- .../extensions/tpos/templates/tpos/tpos.html | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index c66238f7..9e462aba 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -360,15 +360,15 @@ computed: { amount: function () { if (!this.stack.length) return 0.0 - return (Number(this.stack.join('')) / 100).toFixed(2) + return Number(this.stack.join('') / 100) }, famount: function () { - return LNbits.utils.formatCurrency(this.amount, this.currency) + return LNbits.utils.formatCurrency(this.amount.toFixed(2), this.currency) }, sat: function () { if (!this.exchangeRate) return 0 return Math.ceil( - ((this.amount - this.tipAmount) / this.exchangeRate) * 100000000 + (this.amount / this.exchangeRate) * 100000000 ) }, tipAmountSat: function () { @@ -392,26 +392,25 @@ processTipSelection: function (selectedTipOption) { this.tipDialog.show = false - if (selectedTipOption) { - const tipAmount = parseFloat( - parseFloat((selectedTipOption / 100) * this.amount) - ) - const subtotal = parseFloat(this.amount) - const grandTotal = parseFloat((tipAmount + subtotal).toFixed(2)) - const totalString = grandTotal.toFixed(2).toString() + if(!selectedTipOption) return this.showInvoice() - this.stack = [] - for (var i = 0; i < totalString.length; i++) { - const char = totalString[i] + const tipAmount = (selectedTipOption / 100) * this.amount + const subtotal = this.amount + const grandTotal = (tipAmount + subtotal) + const totalString = grandTotal.toFixed(2) - if (char !== '.') { - this.stack.push(char) - } + this.stack = [] + for (var i = 0; i < totalString.length; i++) { + const char = totalString[i] + + if (char !== '.') { + this.stack.push(char) } - - this.tipAmount = tipAmount } + + this.tipAmount = tipAmount + this.showInvoice() }, submitForm: function () { From 476f915fd28a41b2676025d7b1834d850ca0c914 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Thu, 27 Oct 2022 19:16:40 +0100 Subject: [PATCH 69/96] format --- lnbits/extensions/tpos/templates/tpos/tpos.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 9e462aba..929af4d7 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -363,13 +363,14 @@ return Number(this.stack.join('') / 100) }, famount: function () { - return LNbits.utils.formatCurrency(this.amount.toFixed(2), this.currency) + return LNbits.utils.formatCurrency( + this.amount.toFixed(2), + this.currency + ) }, sat: function () { if (!this.exchangeRate) return 0 - return Math.ceil( - (this.amount / this.exchangeRate) * 100000000 - ) + return Math.ceil((this.amount / this.exchangeRate) * 100000000) }, tipAmountSat: function () { if (!this.exchangeRate) return 0 @@ -392,11 +393,11 @@ processTipSelection: function (selectedTipOption) { this.tipDialog.show = false - if(!selectedTipOption) return this.showInvoice() + if (!selectedTipOption) return this.showInvoice() const tipAmount = (selectedTipOption / 100) * this.amount const subtotal = this.amount - const grandTotal = (tipAmount + subtotal) + const grandTotal = tipAmount + subtotal const totalString = grandTotal.toFixed(2) this.stack = [] @@ -408,7 +409,6 @@ } } - this.tipAmount = tipAmount this.showInvoice() From 9ecf6f273b092f33573c9f541e616852be330e2f Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Fri, 18 Nov 2022 11:42:30 +0200 Subject: [PATCH 70/96] fix: total amount for invoice --- lnbits/extensions/tpos/templates/tpos/tpos.html | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 929af4d7..0dd105b9 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -395,22 +395,7 @@ if (!selectedTipOption) return this.showInvoice() - const tipAmount = (selectedTipOption / 100) * this.amount - const subtotal = this.amount - const grandTotal = tipAmount + subtotal - const totalString = grandTotal.toFixed(2) - - this.stack = [] - for (var i = 0; i < totalString.length; i++) { - const char = totalString[i] - - if (char !== '.') { - this.stack.push(char) - } - } - - this.tipAmount = tipAmount - + this.tipAmount = (selectedTipOption / 100) * this.amount this.showInvoice() }, submitForm: function () { From 0039a48e730565c308d2e5bef02e97de823207bc Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Fri, 18 Nov 2022 12:02:51 +0200 Subject: [PATCH 71/96] fix: total amount (amount + tip) --- .../extensions/tpos/templates/tpos/tpos.html | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 0dd105b9..0f9620a9 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -13,7 +13,7 @@
-

{% raw %}{{ famount }}{% endraw %}

+

{% raw %}{{ amountFormatted }}{% endraw %}

{% raw %}{{ fsat }}{% endraw %} sat
@@ -173,12 +173,14 @@ >
-

{% raw %}{{ famount }}{% endraw %}

+

+ {% raw %}{{ amountWithTipFormatted }}{% endraw %} +

{% raw %}{{ fsat }} sat ( + {{ tipAmountSat }} tip)( + {{ tipAmountFormatted }} tip) {% endraw %}
@@ -362,12 +364,18 @@ if (!this.stack.length) return 0.0 return Number(this.stack.join('') / 100) }, - famount: function () { + amountFormatted: function () { return LNbits.utils.formatCurrency( this.amount.toFixed(2), this.currency ) }, + amountWithTipFormatted: function () { + return LNbits.utils.formatCurrency( + (this.amount + this.tipAmount).toFixed(2), + this.currency + ) + }, sat: function () { if (!this.exchangeRate) return 0 return Math.ceil((this.amount / this.exchangeRate) * 100000000) @@ -376,6 +384,9 @@ if (!this.exchangeRate) return 0 return Math.ceil((this.tipAmount / this.exchangeRate) * 100000000) }, + tipAmountFormatted: function () { + return LNbits.utils.formatSat(this.tipAmountSat) + }, fsat: function () { return LNbits.utils.formatSat(this.sat) } @@ -393,7 +404,10 @@ processTipSelection: function (selectedTipOption) { this.tipDialog.show = false - if (!selectedTipOption) return this.showInvoice() + if (!selectedTipOption) { + this.tipAmount = 0.0 + return this.showInvoice() + } this.tipAmount = (selectedTipOption / 100) * this.amount this.showInvoice() From 96046b8d0cd459ca7bf54cd13b3b74398c737f94 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Fri, 14 Oct 2022 21:59:28 +0100 Subject: [PATCH 72/96] add default tip by rounding to value --- .../extensions/tpos/templates/tpos/tpos.html | 63 ++++++++++++++++++- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 0f9620a9..4b1b0ad3 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -17,6 +17,7 @@
{% raw %}{{ fsat }}{% endraw %} sat
+

{% raw %}{{ parseFloat(roundToSugestion) }}{% endraw %}

@@ -214,14 +215,37 @@ style="padding: 10px; margin: 3px" unelevated @click="processTipSelection(tip)" - size="xl" + size="lg" :outline="!($q.dark.isActive)" rounded color="primary" - v-for="tip in this.tip_options" + v-for="tip in tip_options.filter(f => f != 'Round')" :key="tip" >{% raw %}{{ tip }}{% endraw %}% + + + +

No, thanks

@@ -336,6 +360,7 @@ exchangeRate: null, stack: [], tipAmount: 0.0, + tipRounding: null, hasNFC: false, nfcTagReading: false, lastPaymentsDialog: { @@ -356,7 +381,8 @@ }, complete: { show: false - } + }, + rounding: false } }, computed: { @@ -389,9 +415,36 @@ }, fsat: function () { return LNbits.utils.formatSat(this.sat) + }, + isRoundValid(){ + return this.tipRounding > this.amount + }, + roundToSugestion(){ + //let toNext = 1 + switch(true){ + case this.amount > 50: + toNext = 10 + break + case this.amount > 6: + toNext = 5 + break + case this.amount > 2.5: + toNext = 1 + break + default: + toNext = 0.5 + break + } + + return Math.ceil(this.amount/toNext)*toNext } }, methods: { + setRounding(){ + this.rounding = true + this.tipRounding = this.roundToSugestion + this.$nextTick(() => this.$refs.inputRounding.focus()) + }, closeInvoiceDialog: function () { this.stack = [] this.tipAmount = 0.0 @@ -589,6 +642,10 @@ '{{ tpos.tip_options | tojson }}' == 'null' ? null : JSON.parse('{{ tpos.tip_options }}') + + if('{{ tpos.tip_wallet }}') { + this.tip_options.push("Round") + } setInterval(function () { getRates() }, 120000) From 9430aa28c7cb68c014076aaace4cf30827e08a4c Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Mon, 17 Oct 2022 11:40:30 +0100 Subject: [PATCH 73/96] add rounding tip option --- .../extensions/tpos/templates/tpos/index.html | 8 +++- .../extensions/tpos/templates/tpos/tpos.html | 47 ++++++++++++------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/index.html b/lnbits/extensions/tpos/templates/tpos/index.html index 3c4fa24f..1aa75fcf 100644 --- a/lnbits/extensions/tpos/templates/tpos/index.html +++ b/lnbits/extensions/tpos/templates/tpos/index.html @@ -139,8 +139,12 @@ input-debounce="0" new-value-mode="add-unique" label="Tip % Options (hit enter to add values)" - >Hit enter to add values + >Hit enter to add values + +
{% raw %}{{ fsat }}{% endraw %} sat -

{% raw %}{{ parseFloat(roundToSugestion) }}{% endraw %}

@@ -232,9 +231,9 @@ rounded color="primary" label="Round" - >
+ >
- - -
-
+ No, thanks Close
@@ -416,12 +415,12 @@ fsat: function () { return LNbits.utils.formatSat(this.sat) }, - isRoundValid(){ + isRoundValid() { return this.tipRounding > this.amount }, - roundToSugestion(){ + roundToSugestion() { //let toNext = 1 - switch(true){ + switch (true) { case this.amount > 50: toNext = 10 break @@ -435,16 +434,28 @@ toNext = 0.5 break } - - return Math.ceil(this.amount/toNext)*toNext + + return Math.ceil(this.amount / toNext) * toNext } }, methods: { - setRounding(){ + setRounding() { this.rounding = true this.tipRounding = this.roundToSugestion this.$nextTick(() => this.$refs.inputRounding.focus()) }, + calculatePercent() { + let change = ((this.tipRounding - this.amount) / this.amount) * 100 + if (change < 0) { + this.$q.notify({ + type: 'warning', + message: 'Value must be greater than initial amount' + }) + this.tipRounding = this.roundToSugestion + return + } + this.processTipSelection(change) + }, closeInvoiceDialog: function () { this.stack = [] this.tipAmount = 0.0 @@ -643,8 +654,8 @@ ? null : JSON.parse('{{ tpos.tip_options }}') - if('{{ tpos.tip_wallet }}') { - this.tip_options.push("Round") + if ('{{ tpos.tip_wallet }}') { + this.tip_options.push('Round') } setInterval(function () { getRates() From f4c0c92655ac0373d8e98d36313134f6fd85c8f8 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Mon, 24 Oct 2022 14:09:40 +0200 Subject: [PATCH 74/96] strings --- lnbits/extensions/tpos/templates/tpos/tpos.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 6a74d95b..f432901a 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -230,7 +230,7 @@ :outline="!($q.dark.isActive)" rounded color="primary" - label="Round" + label="Custom" >
@@ -449,7 +449,7 @@ if (change < 0) { this.$q.notify({ type: 'warning', - message: 'Value must be greater than initial amount' + message: 'Amount with tip must be greater than initial amount.' }) this.tipRounding = this.roundToSugestion return From 12b69a7f826a178ba2ee7d30141266295d4cf1f3 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Thu, 17 Nov 2022 15:13:21 +0000 Subject: [PATCH 75/96] fix reseting rounding and better UX --- .../extensions/tpos/templates/tpos/tpos.html | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index f432901a..3be6d4a5 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -230,21 +230,25 @@ :outline="!($q.dark.isActive)" rounded color="primary" - label="Custom" + label="Round to" > - + + + + Ok - - +
Date: Thu, 17 Nov 2022 15:14:53 +0000 Subject: [PATCH 76/96] format --- lnbits/extensions/tpos/templates/tpos/tpos.html | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 3be6d4a5..c0adfca4 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -245,9 +245,13 @@ --> - Ok + Ok
From edf76ad7cc7d34165c5034fdab573ee7041ab2fb Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Thu, 17 Nov 2022 15:32:20 +0000 Subject: [PATCH 77/96] add input number --- lnbits/extensions/tpos/templates/tpos/tpos.html | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index c0adfca4..6c56b8fb 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -238,6 +238,7 @@ ref="inputRounding" v-model.number="tipRounding" :placeholder="roundToSugestion" + type="number" hint="Total amount including tip" :prefix="currency" > @@ -673,4 +674,18 @@ } }) + {% endblock %} From d64e960ef67990b143ed951a0a9d5920091f0eb7 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Fri, 18 Nov 2022 13:58:56 +0200 Subject: [PATCH 78/96] chore: code clean-up and formatting --- lnbits/extensions/tpos/templates/tpos/tpos.html | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html index 6c56b8fb..c3886234 100644 --- a/lnbits/extensions/tpos/templates/tpos/tpos.html +++ b/lnbits/extensions/tpos/templates/tpos/tpos.html @@ -242,9 +242,6 @@ hint="Total amount including tip" :prefix="currency" > - this.amount }, roundToSugestion() { - //let toNext = 1 switch (true) { case this.amount > 50: toNext = 10 @@ -675,17 +671,15 @@ }) {% endblock %} From b698dac52c281ca0188e6a6edbf41785ed020877 Mon Sep 17 00:00:00 2001 From: Thomas Verstreken <111072251+ThomasFarstrike@users.noreply.github.com> Date: Sat, 19 Nov 2022 21:10:12 +0100 Subject: [PATCH 79/96] Fix typo's (#1136) Costumer to Customer, costumer to customer --- lnbits/extensions/offlineshop/README.md | 12 ++++++------ lnbits/extensions/satspay/README.md | 2 +- lnbits/extensions/tpos/README.md | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lnbits/extensions/offlineshop/README.md b/lnbits/extensions/offlineshop/README.md index 79bbe41d..25ead5df 100644 --- a/lnbits/extensions/offlineshop/README.md +++ b/lnbits/extensions/offlineshop/README.md @@ -6,9 +6,9 @@ LNBits Offline Shop allows for merchants to receive Bitcoin payments while offline and without any electronic device. -Merchant will create items and associate a QR code ([a LNURLp](https://github.com/lnbits/lnbits/blob/master/lnbits/extensions/lnurlp/README.md)) with a price. He can then print the QR codes and display them on their shop. When a costumer chooses an item, scans the QR code, gets the description and price. After payment, the costumer gets a confirmation code that the merchant can validate to be sure the payment was successful. +Merchant will create items and associate a QR code ([a LNURLp](https://github.com/lnbits/lnbits/blob/master/lnbits/extensions/lnurlp/README.md)) with a price. He can then print the QR codes and display them on their shop. When a customer chooses an item, scans the QR code, gets the description and price. After payment, the customer gets a confirmation code that the merchant can validate to be sure the payment was successful. -Costumers must use an LNURL pay capable wallet. +Customers must use an LNURL pay capable wallet. [**Wallets supporting LNURL**](https://github.com/fiatjaf/awesome-lnurl#wallets) @@ -18,18 +18,18 @@ Costumers must use an LNURL pay capable wallet. ![offline shop back office](https://i.imgur.com/Ei7cxj9.png) 2. Begin by creating an item, click "ADD NEW ITEM" - set the item name and a small description - - you can set an optional, preferably square image, that will show up on the costumer wallet - _depending on wallet_ - - set the item price, if you choose a fiat currency the bitcoin conversion will happen at the time costumer scans to pay\ + - you can set an optional, preferably square image, that will show up on the customer wallet - _depending on wallet_ + - set the item price, if you choose a fiat currency the bitcoin conversion will happen at the time customer scans to pay\ ![add new item](https://i.imgur.com/pkZqRgj.png) 3. After creating some products, click on "PRINT QR CODES"\ ![print qr codes](https://i.imgur.com/2GAiSTe.png) 4. You'll see a QR code for each product in your LNBits Offline Shop with a title and price ready for printing\ ![qr codes sheet](https://i.imgur.com/faEqOcd.png) 5. Place the printed QR codes on your shop, or at the fair stall, or have them as a menu style laminated sheet -6. Choose what type of confirmation do you want costumers to report to merchant after a successful payment\ +6. Choose what type of confirmation do you want customers to report to merchant after a successful payment\ ![wordlist](https://i.imgur.com/9aM6NUL.png) - - Wordlist is the default option: after a successful payment the costumer will receive a word from this list, **sequentially**. Starting in _albatross_ as costumers pay for the items they will get the next word in the list until _zebra_, then it starts at the top again. The list can be changed, for example if you think A-Z is a big list to track, you can use _apple_, _banana_, _coconut_\ + - Wordlist is the default option: after a successful payment the customer will receive a word from this list, **sequentially**. Starting in _albatross_ as customers pay for the items they will get the next word in the list until _zebra_, then it starts at the top again. The list can be changed, for example if you think A-Z is a big list to track, you can use _apple_, _banana_, _coconut_\ ![totp authenticator](https://i.imgur.com/MrJXFxz.png) - TOTP (time-based one time password) can be used instead. If you use Google Authenticator just scan the presented QR with the app and after a successful payment the user will get the password that you can check with GA\ ![disable confirmations](https://i.imgur.com/2OFs4yi.png) diff --git a/lnbits/extensions/satspay/README.md b/lnbits/extensions/satspay/README.md index d52547ae..7a12feb3 100644 --- a/lnbits/extensions/satspay/README.md +++ b/lnbits/extensions/satspay/README.md @@ -18,7 +18,7 @@ Easilly create invoices that support Lightning Network and on-chain BTC payment. ![charge form](https://i.imgur.com/F10yRiW.png) 3. The charge will appear on the _Charges_ section\ ![charges](https://i.imgur.com/zqHpVxc.png) -4. Your costumer/payee will get the payment page +4. Your customer/payee will get the payment page - they can choose to pay on LN\ ![offchain payment](https://i.imgur.com/4191SMV.png) - or pay on chain\ diff --git a/lnbits/extensions/tpos/README.md b/lnbits/extensions/tpos/README.md index 04e049e3..c7e3481d 100644 --- a/lnbits/extensions/tpos/README.md +++ b/lnbits/extensions/tpos/README.md @@ -11,5 +11,5 @@ An easy, fast and secure way to accept Bitcoin, over Lightning Network, at your ![create](https://imgur.com/8jNj8Zq.jpg) 3. Open TPOS on the browser\ ![open](https://imgur.com/LZuoWzb.jpg) -4. Present invoice QR to costumer\ +4. Present invoice QR to customer\ ![pay](https://imgur.com/tOwxn77.jpg) From 22bb67a249a42953054d6141948994820cbe00c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Sat, 19 Nov 2022 21:12:33 +0100 Subject: [PATCH 80/96] lnurl withdraw, better errorhandling for callback endpoint (#1107) --- lnbits/extensions/withdraw/lnurl.py | 37 ++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index 660e5b7d..5737e54f 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -9,7 +9,7 @@ from fastapi import HTTPException from fastapi.param_functions import Query from loguru import logger from starlette.requests import Request -from starlette.responses import HTMLResponse # type: ignore +from starlette.responses import HTMLResponse from lnbits.core.services import pay_invoice @@ -51,10 +51,24 @@ async def api_lnurl_response(request: Request, unique_hash): # CALLBACK -@withdraw_ext.get("/api/v1/lnurl/cb/{unique_hash}", name="withdraw.api_lnurl_callback") +@withdraw_ext.get( + "/api/v1/lnurl/cb/{unique_hash}", + name="withdraw.api_lnurl_callback", + summary="lnurl withdraw callback", + description=""" + This enpoints allows you to put unique_hash, k1 + and a payment_request to get your payment_request paid. + """, + response_description="JSON with status", + responses={ + 200: {"description": "status: OK"}, + 400: {"description": "k1 is wrong or link open time or withdraw not working."}, + 404: {"description": "withdraw link not found."}, + 405: {"description": "withdraw link is spent."}, + }, +) async def api_lnurl_callback( unique_hash, - request: Request, k1: str = Query(...), pr: str = Query(...), id_unique_hash=None, @@ -63,19 +77,22 @@ async def api_lnurl_callback( now = int(datetime.now().timestamp()) if not link: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found" + status_code=HTTPStatus.NOT_FOUND, detail="withdraw not found." ) if link.is_spent: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Withdraw is spent." + status_code=HTTPStatus.METHOD_NOT_ALLOWED, detail="withdraw is spent." ) if link.k1 != k1: - raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Bad request.") + raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="k1 is wrong.") if now < link.open_time: - return {"status": "ERROR", "reason": f"Wait {link.open_time - now} seconds."} + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, + detail=f"wait link open_time {link.open_time - now} seconds.", + ) usescsv = "" @@ -95,7 +112,7 @@ async def api_lnurl_callback( usescsv = ",".join(useslist) if not found: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found." + status_code=HTTPStatus.NOT_FOUND, detail="withdraw not found." ) else: usescsv = usescsv[1:] @@ -144,7 +161,9 @@ async def api_lnurl_callback( except Exception as e: await update_withdraw_link(link.id, **changesback) logger.error(traceback.format_exc()) - return {"status": "ERROR", "reason": "Link not working"} + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, detail=f"withdraw not working. {str(e)}" + ) # FOR LNURLs WHICH ARE UNIQUE From 0096ce8ad1c75df3a3dcca91e480d963987781dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Sat, 19 Nov 2022 21:41:37 +0100 Subject: [PATCH 81/96] let poetry run lnbits recognize forwarded_ips for https (#1109) --- lnbits/server.py | 17 +++++++++++++---- lnbits/settings.py | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lnbits/server.py b/lnbits/server.py index e9849851..7aaaa964 100644 --- a/lnbits/server.py +++ b/lnbits/server.py @@ -1,9 +1,7 @@ -import time - import click import uvicorn -from lnbits.settings import HOST, PORT +from lnbits.settings import FORWARDED_ALLOW_IPS, HOST, PORT @click.command( @@ -14,10 +12,20 @@ from lnbits.settings import HOST, PORT ) @click.option("--port", default=PORT, help="Port to listen on") @click.option("--host", default=HOST, help="Host to run LNBits on") +@click.option( + "--forwarded-allow-ips", default=FORWARDED_ALLOW_IPS, help="Allowed proxy servers" +) @click.option("--ssl-keyfile", default=None, help="Path to SSL keyfile") @click.option("--ssl-certfile", default=None, help="Path to SSL certificate") @click.pass_context -def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str): +def main( + ctx, + port: int, + host: str, + forwarded_allow_ips: str, + ssl_keyfile: str, + ssl_certfile: str, +): """Launched with `poetry run lnbits` at root level""" # this beautiful beast parses all command line arguments and passes them to the uvicorn server d = dict() @@ -37,6 +45,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str): "lnbits.__main__:app", port=port, host=host, + forwarded_allow_ips=forwarded_allow_ips, ssl_keyfile=ssl_keyfile, ssl_certfile=ssl_certfile, **d diff --git a/lnbits/settings.py b/lnbits/settings.py index 3f4e31cc..73b0d6c9 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -18,6 +18,8 @@ DEBUG = env.bool("DEBUG", default=False) HOST = env.str("HOST", default="127.0.0.1") PORT = env.int("PORT", default=5000) +FORWARDED_ALLOW_IPS = env.str("FORWARDED_ALLOW_IPS", default="127.0.0.1") + LNBITS_PATH = path.dirname(path.realpath(__file__)) LNBITS_DATA_FOLDER = env.str( "LNBITS_DATA_FOLDER", default=path.join(LNBITS_PATH, "data") From 604e52c45dc14b4a088c98580cfd492268494f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Sat, 19 Nov 2022 21:42:10 +0100 Subject: [PATCH 82/96] more mypy extension fixes (#1096) * mypy fix jukebox * mypy fix ngrok * mypy fix satsdice * remove exts from mypy exclude --- lnbits/extensions/jukebox/crud.py | 16 ++--- lnbits/extensions/jukebox/models.py | 29 ++++---- lnbits/extensions/jukebox/tasks.py | 9 +-- lnbits/extensions/jukebox/views.py | 7 +- lnbits/extensions/jukebox/views_api.py | 93 +++++++++++-------------- lnbits/extensions/ngrok/views.py | 5 +- lnbits/extensions/satsdice/crud.py | 47 ++++++------- lnbits/extensions/satsdice/lnurl.py | 37 +++++----- lnbits/extensions/satsdice/models.py | 7 +- lnbits/extensions/satsdice/views.py | 36 ++++++---- lnbits/extensions/satsdice/views_api.py | 24 ++++--- pyproject.toml | 3 - 12 files changed, 157 insertions(+), 156 deletions(-) diff --git a/lnbits/extensions/jukebox/crud.py b/lnbits/extensions/jukebox/crud.py index d160daee..9d6548b6 100644 --- a/lnbits/extensions/jukebox/crud.py +++ b/lnbits/extensions/jukebox/crud.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import List, Optional, Union from lnbits.helpers import urlsafe_short_hash @@ -6,11 +6,9 @@ from . import db from .models import CreateJukeboxPayment, CreateJukeLinkData, Jukebox, JukeboxPayment -async def create_jukebox( - data: CreateJukeLinkData, inkey: Optional[str] = "" -) -> Jukebox: +async def create_jukebox(data: CreateJukeLinkData) -> Jukebox: juke_id = urlsafe_short_hash() - result = await db.execute( + await db.execute( """ INSERT INTO jukebox.jukebox (id, "user", title, wallet, sp_user, sp_secret, sp_access_token, sp_refresh_token, sp_device, sp_playlists, price, profit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) @@ -36,13 +34,13 @@ async def create_jukebox( async def update_jukebox( - data: CreateJukeLinkData, juke_id: Optional[str] = "" + data: Union[CreateJukeLinkData, Jukebox], juke_id: str = "" ) -> Optional[Jukebox]: q = ", ".join([f"{field[0]} = ?" for field in data]) items = [f"{field[1]}" for field in data] items.append(juke_id) q = q.replace("user", '"user"', 1) # hack to make user be "user"! - await db.execute(f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (items)) + await db.execute(f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (items,)) row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,)) return Jukebox(**row) if row else None @@ -72,7 +70,7 @@ async def delete_jukebox(juke_id: str): """ DELETE FROM jukebox.jukebox WHERE id = ? """, - (juke_id), + (juke_id,), ) @@ -80,7 +78,7 @@ async def delete_jukebox(juke_id: str): async def create_jukebox_payment(data: CreateJukeboxPayment) -> JukeboxPayment: - result = await db.execute( + await db.execute( """ INSERT INTO jukebox.jukebox_payment (payment_hash, juke_id, song_id, paid) VALUES (?, ?, ?, ?) diff --git a/lnbits/extensions/jukebox/models.py b/lnbits/extensions/jukebox/models.py index 90984b03..70cf6523 100644 --- a/lnbits/extensions/jukebox/models.py +++ b/lnbits/extensions/jukebox/models.py @@ -1,6 +1,3 @@ -from sqlite3 import Row -from typing import NamedTuple, Optional - from fastapi.param_functions import Query from pydantic import BaseModel from pydantic.main import BaseModel @@ -20,19 +17,19 @@ class CreateJukeLinkData(BaseModel): class Jukebox(BaseModel): - id: Optional[str] - user: Optional[str] - title: Optional[str] - wallet: Optional[str] - inkey: Optional[str] - sp_user: Optional[str] - sp_secret: Optional[str] - sp_access_token: Optional[str] - sp_refresh_token: Optional[str] - sp_device: Optional[str] - sp_playlists: Optional[str] - price: Optional[int] - profit: Optional[int] + id: str + user: str + title: str + wallet: str + inkey: str + sp_user: str + sp_secret: str + sp_access_token: str + sp_refresh_token: str + sp_device: str + sp_playlists: str + price: int + profit: int class JukeboxPayment(BaseModel): diff --git a/lnbits/extensions/jukebox/tasks.py b/lnbits/extensions/jukebox/tasks.py index 5614d926..8a68fd27 100644 --- a/lnbits/extensions/jukebox/tasks.py +++ b/lnbits/extensions/jukebox/tasks.py @@ -17,7 +17,8 @@ async def wait_for_paid_invoices(): async def on_invoice_paid(payment: Payment) -> None: - if payment.extra.get("tag") != "jukebox": - # not a jukebox invoice - return - await update_jukebox_payment(payment.payment_hash, paid=True) + if payment.extra: + if payment.extra.get("tag") != "jukebox": + # not a jukebox invoice + return + await update_jukebox_payment(payment.payment_hash, paid=True) diff --git a/lnbits/extensions/jukebox/views.py b/lnbits/extensions/jukebox/views.py index 56774394..28359a9a 100644 --- a/lnbits/extensions/jukebox/views.py +++ b/lnbits/extensions/jukebox/views.py @@ -17,7 +17,9 @@ templates = Jinja2Templates(directory="templates") @jukebox_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, user: User = Depends(check_user_exists) # type: ignore +): return jukebox_renderer().TemplateResponse( "jukebox/index.html", {"request": request, "user": user.dict()} ) @@ -31,6 +33,7 @@ async def connect_to_jukebox(request: Request, juke_id): status_code=HTTPStatus.NOT_FOUND, detail="Jukebox does not exist." ) devices = await api_get_jukebox_device_check(juke_id) + deviceConnected = False for device in devices["devices"]: if device["id"] == jukebox.sp_device.split("-")[1]: deviceConnected = True @@ -48,5 +51,5 @@ async def connect_to_jukebox(request: Request, juke_id): else: return jukebox_renderer().TemplateResponse( "jukebox/error.html", - {"request": request, "jukebox": jukebox.jukebox(req=request)}, + {"request": request, "jukebox": jukebox.dict()}, ) diff --git a/lnbits/extensions/jukebox/views_api.py b/lnbits/extensions/jukebox/views_api.py index 1f3723a7..5cf1a83b 100644 --- a/lnbits/extensions/jukebox/views_api.py +++ b/lnbits/extensions/jukebox/views_api.py @@ -3,7 +3,6 @@ import json from http import HTTPStatus import httpx -from fastapi import Request from fastapi.param_functions import Query from fastapi.params import Depends from starlette.exceptions import HTTPException @@ -29,9 +28,7 @@ from .models import CreateJukeboxPayment, CreateJukeLinkData @jukebox_ext.get("/api/v1/jukebox") async def api_get_jukeboxs( - req: Request, - wallet: WalletTypeInfo = Depends(require_admin_key), - all_wallets: bool = Query(False), + wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore ): wallet_user = wallet.wallet.user @@ -53,54 +50,52 @@ async def api_check_credentials_callbac( access_token: str = Query(None), refresh_token: str = Query(None), ): - sp_code = "" - sp_access_token = "" - sp_refresh_token = "" - try: - jukebox = await get_jukebox(juke_id) - except: + jukebox = await get_jukebox(juke_id) + if not jukebox: raise HTTPException(detail="No Jukebox", status_code=HTTPStatus.FORBIDDEN) if code: jukebox.sp_access_token = code - jukebox = await update_jukebox(jukebox, juke_id=juke_id) + await update_jukebox(jukebox, juke_id=juke_id) if access_token: jukebox.sp_access_token = access_token jukebox.sp_refresh_token = refresh_token - jukebox = await update_jukebox(jukebox, juke_id=juke_id) + await update_jukebox(jukebox, juke_id=juke_id) return "

Success!

You can close this window

" -@jukebox_ext.get("/api/v1/jukebox/{juke_id}") -async def api_check_credentials_check( - juke_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key) -): +@jukebox_ext.get("/api/v1/jukebox/{juke_id}", dependencies=[Depends(require_admin_key)]) +async def api_check_credentials_check(juke_id: str = Query(None)): jukebox = await get_jukebox(juke_id) return jukebox -@jukebox_ext.post("/api/v1/jukebox", status_code=HTTPStatus.CREATED) +@jukebox_ext.post( + "/api/v1/jukebox", + status_code=HTTPStatus.CREATED, + dependencies=[Depends(require_admin_key)], +) @jukebox_ext.put("/api/v1/jukebox/{juke_id}", status_code=HTTPStatus.OK) async def api_create_update_jukebox( - data: CreateJukeLinkData, - juke_id: str = Query(None), - wallet: WalletTypeInfo = Depends(require_admin_key), + data: CreateJukeLinkData, juke_id: str = Query(None) ): if juke_id: jukebox = await update_jukebox(data, juke_id=juke_id) else: - jukebox = await create_jukebox(data, inkey=wallet.wallet.inkey) + jukebox = await create_jukebox(data) return jukebox -@jukebox_ext.delete("/api/v1/jukebox/{juke_id}") +@jukebox_ext.delete( + "/api/v1/jukebox/{juke_id}", dependencies=[Depends(require_admin_key)] +) async def api_delete_item( - juke_id=None, wallet: WalletTypeInfo = Depends(require_admin_key) + juke_id: str = Query(None), ): await delete_jukebox(juke_id) - try: - return [{**jukebox} for jukebox in await get_jukeboxs(wallet.wallet.user)] - except: - raise HTTPException(status_code=HTTPStatus.NO_CONTENT, detail="No Jukebox") + # try: + # return [{**jukebox} for jukebox in await get_jukeboxs(wallet.wallet.user)] + # except: + # raise HTTPException(status_code=HTTPStatus.NO_CONTENT, detail="No Jukebox") ################JUKEBOX ENDPOINTS################## @@ -114,9 +109,8 @@ async def api_get_jukebox_song( sp_playlist: str = Query(None), retry: bool = Query(False), ): - try: - jukebox = await get_jukebox(juke_id) - except: + jukebox = await get_jukebox(juke_id) + if not jukebox: raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No Jukeboxes") tracks = [] async with httpx.AsyncClient() as client: @@ -152,14 +146,13 @@ async def api_get_jukebox_song( } ) except: - something = None + pass return [track for track in tracks] -async def api_get_token(juke_id=None): - try: - jukebox = await get_jukebox(juke_id) - except: +async def api_get_token(juke_id): + jukebox = await get_jukebox(juke_id) + if not jukebox: raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No Jukeboxes") async with httpx.AsyncClient() as client: @@ -187,7 +180,7 @@ async def api_get_token(juke_id=None): jukebox.sp_access_token = r.json()["access_token"] await update_jukebox(jukebox, juke_id=juke_id) except: - something = None + pass return True @@ -198,9 +191,8 @@ async def api_get_token(juke_id=None): async def api_get_jukebox_device_check( juke_id: str = Query(None), retry: bool = Query(False) ): - try: - jukebox = await get_jukebox(juke_id) - except: + jukebox = await get_jukebox(juke_id) + if not jukebox: raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No Jukeboxes") async with httpx.AsyncClient() as client: rDevice = await client.get( @@ -221,7 +213,7 @@ async def api_get_jukebox_device_check( status_code=HTTPStatus.FORBIDDEN, detail="Failed to get auth" ) else: - return api_get_jukebox_device_check(juke_id, retry=True) + return await api_get_jukebox_device_check(juke_id, retry=True) else: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="No device connected" @@ -233,10 +225,8 @@ async def api_get_jukebox_device_check( @jukebox_ext.get("/api/v1/jukebox/jb/invoice/{juke_id}/{song_id}") async def api_get_jukebox_invoice(juke_id, song_id): - try: - jukebox = await get_jukebox(juke_id) - - except: + jukebox = await get_jukebox(juke_id) + if not jukebox: raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No jukebox") try: @@ -266,8 +256,7 @@ async def api_get_jukebox_invoice(juke_id, song_id): invoice=invoice[1], payment_hash=payment_hash, juke_id=juke_id, song_id=song_id ) jukebox_payment = await create_jukebox_payment(data) - - return data + return jukebox_payment @jukebox_ext.get("/api/v1/jukebox/jb/checkinvoice/{pay_hash}/{juke_id}") @@ -296,13 +285,12 @@ async def api_get_jukebox_invoice_paid( pay_hash: str = Query(None), retry: bool = Query(False), ): - try: - jukebox = await get_jukebox(juke_id) - except: + jukebox = await get_jukebox(juke_id) + if not jukebox: raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No jukebox") await api_get_jukebox_invoice_check(pay_hash, juke_id) jukebox_payment = await get_jukebox_payment(pay_hash) - if jukebox_payment.paid: + if jukebox_payment and jukebox_payment.paid: async with httpx.AsyncClient() as client: r = await client.get( "https://api.spotify.com/v1/me/player/currently-playing?market=ES", @@ -407,9 +395,8 @@ async def api_get_jukebox_invoice_paid( async def api_get_jukebox_currently( retry: bool = Query(False), juke_id: str = Query(None) ): - try: - jukebox = await get_jukebox(juke_id) - except: + jukebox = await get_jukebox(juke_id) + if not jukebox: raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No jukebox") async with httpx.AsyncClient() as client: try: diff --git a/lnbits/extensions/ngrok/views.py b/lnbits/extensions/ngrok/views.py index 1a34fd51..16c3708a 100644 --- a/lnbits/extensions/ngrok/views.py +++ b/lnbits/extensions/ngrok/views.py @@ -1,3 +1,4 @@ +# type: ignore from os import getenv from fastapi import Request @@ -34,7 +35,9 @@ ngrok_tunnel = ngrok.connect(port) @ngrok_ext.get("/") -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, user: User = Depends(check_user_exists) # type: ignore +): return ngrok_renderer().TemplateResponse( "ngrok/index.html", {"request": request, "ngrok": string5, "user": user.dict()} ) diff --git a/lnbits/extensions/satsdice/crud.py b/lnbits/extensions/satsdice/crud.py index 7da5a1f1..6aeaf31f 100644 --- a/lnbits/extensions/satsdice/crud.py +++ b/lnbits/extensions/satsdice/crud.py @@ -8,7 +8,6 @@ from .models import ( CreateSatsDiceLink, CreateSatsDicePayment, CreateSatsDiceWithdraw, - HashCheck, satsdiceLink, satsdicePayment, satsdiceWithdraw, @@ -76,7 +75,7 @@ async def get_satsdice_pays(wallet_ids: Union[str, List[str]]) -> List[satsdiceL return [satsdiceLink(**row) for row in rows] -async def update_satsdice_pay(link_id: int, **kwargs) -> Optional[satsdiceLink]: +async def update_satsdice_pay(link_id: str, **kwargs) -> satsdiceLink: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) await db.execute( f"UPDATE satsdice.satsdice_pay SET {q} WHERE id = ?", @@ -85,10 +84,10 @@ async def update_satsdice_pay(link_id: int, **kwargs) -> Optional[satsdiceLink]: row = await db.fetchone( "SELECT * FROM satsdice.satsdice_pay WHERE id = ?", (link_id,) ) - return satsdiceLink(**row) if row else None + return satsdiceLink(**row) -async def increment_satsdice_pay(link_id: int, **kwargs) -> Optional[satsdiceLink]: +async def increment_satsdice_pay(link_id: str, **kwargs) -> Optional[satsdiceLink]: q = ", ".join([f"{field[0]} = {field[0]} + ?" for field in kwargs.items()]) await db.execute( f"UPDATE satsdice.satsdice_pay SET {q} WHERE id = ?", @@ -100,7 +99,7 @@ async def increment_satsdice_pay(link_id: int, **kwargs) -> Optional[satsdiceLin return satsdiceLink(**row) if row else None -async def delete_satsdice_pay(link_id: int) -> None: +async def delete_satsdice_pay(link_id: str) -> None: await db.execute("DELETE FROM satsdice.satsdice_pay WHERE id = ?", (link_id,)) @@ -119,9 +118,15 @@ async def create_satsdice_payment(data: CreateSatsDicePayment) -> satsdicePaymen ) VALUES (?, ?, ?, ?, ?) """, - (data["payment_hash"], data["satsdice_pay"], data["value"], False, False), + ( + data.payment_hash, + data.satsdice_pay, + data.value, + False, + False, + ), ) - payment = await get_satsdice_payment(data["payment_hash"]) + payment = await get_satsdice_payment(data.payment_hash) assert payment, "Newly created withdraw couldn't be retrieved" return payment @@ -134,9 +139,7 @@ async def get_satsdice_payment(payment_hash: str) -> Optional[satsdicePayment]: return satsdicePayment(**row) if row else None -async def update_satsdice_payment( - payment_hash: int, **kwargs -) -> Optional[satsdicePayment]: +async def update_satsdice_payment(payment_hash: str, **kwargs) -> satsdicePayment: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) await db.execute( @@ -147,7 +150,7 @@ async def update_satsdice_payment( "SELECT * FROM satsdice.satsdice_payment WHERE payment_hash = ?", (payment_hash,), ) - return satsdicePayment(**row) if row else None + return satsdicePayment(**row) ##################SATSDICE WITHDRAW LINKS @@ -168,16 +171,16 @@ async def create_satsdice_withdraw(data: CreateSatsDiceWithdraw) -> satsdiceWith VALUES (?, ?, ?, ?, ?, ?, ?) """, ( - data["payment_hash"], - data["satsdice_pay"], - data["value"], + data.payment_hash, + data.satsdice_pay, + data.value, urlsafe_short_hash(), urlsafe_short_hash(), int(datetime.now().timestamp()), - data["used"], + data.used, ), ) - withdraw = await get_satsdice_withdraw(data["payment_hash"], 0) + withdraw = await get_satsdice_withdraw(data.payment_hash, 0) assert withdraw, "Newly created withdraw couldn't be retrieved" return withdraw @@ -247,7 +250,7 @@ async def delete_satsdice_withdraw(withdraw_id: str) -> None: ) -async def create_withdraw_hash_check(the_hash: str, lnurl_id: str) -> HashCheck: +async def create_withdraw_hash_check(the_hash: str, lnurl_id: str): await db.execute( """ INSERT INTO satsdice.hash_checkw ( @@ -262,19 +265,15 @@ async def create_withdraw_hash_check(the_hash: str, lnurl_id: str) -> HashCheck: return hashCheck -async def get_withdraw_hash_checkw(the_hash: str, lnurl_id: str) -> Optional[HashCheck]: +async def get_withdraw_hash_checkw(the_hash: str, lnurl_id: str): rowid = await db.fetchone( "SELECT * FROM satsdice.hash_checkw WHERE id = ?", (the_hash,) ) rowlnurl = await db.fetchone( "SELECT * FROM satsdice.hash_checkw WHERE lnurl_id = ?", (lnurl_id,) ) - if not rowlnurl: + if not rowlnurl or not rowid: await create_withdraw_hash_check(the_hash, lnurl_id) return {"lnurl": True, "hash": False} else: - if not rowid: - await create_withdraw_hash_check(the_hash, lnurl_id) - return {"lnurl": True, "hash": False} - else: - return {"lnurl": True, "hash": True} + return {"lnurl": True, "hash": True} diff --git a/lnbits/extensions/satsdice/lnurl.py b/lnbits/extensions/satsdice/lnurl.py index caafc3a4..a9b3cf08 100644 --- a/lnbits/extensions/satsdice/lnurl.py +++ b/lnbits/extensions/satsdice/lnurl.py @@ -1,4 +1,3 @@ -import hashlib import json import math from http import HTTPStatus @@ -83,15 +82,18 @@ async def api_lnurlp_callback( success_action = link.success_action(payment_hash=payment_hash, req=req) - data: CreateSatsDicePayment = { - "satsdice_pay": link.id, - "value": amount_received / 1000, - "payment_hash": payment_hash, - } + data = CreateSatsDicePayment( + satsdice_pay=link.id, + value=amount_received / 1000, + payment_hash=payment_hash, + ) await create_satsdice_payment(data) - payResponse = {"pr": payment_request, "successAction": success_action, "routes": []} - + payResponse: dict = { + "pr": payment_request, + "successAction": success_action, + "routes": [], + } return json.dumps(payResponse) @@ -133,9 +135,7 @@ async def api_lnurlw_response(req: Request, unique_hash: str = Query(None)): name="satsdice.api_lnurlw_callback", ) async def api_lnurlw_callback( - req: Request, unique_hash: str = Query(None), - k1: str = Query(None), pr: str = Query(None), ): @@ -146,12 +146,13 @@ async def api_lnurlw_callback( return {"status": "ERROR", "reason": "spent"} paylink = await get_satsdice_pay(link.satsdice_pay) - await update_satsdice_withdraw(link.id, used=1) - await pay_invoice( - wallet_id=paylink.wallet, - payment_request=pr, - max_sat=link.value, - extra={"tag": "withdraw"}, - ) + if paylink: + await update_satsdice_withdraw(link.id, used=1) + await pay_invoice( + wallet_id=paylink.wallet, + payment_request=pr, + max_sat=link.value, + extra={"tag": "withdraw"}, + ) - return {"status": "OK"} + return {"status": "OK"} diff --git a/lnbits/extensions/satsdice/models.py b/lnbits/extensions/satsdice/models.py index fd9af74f..2537f8d7 100644 --- a/lnbits/extensions/satsdice/models.py +++ b/lnbits/extensions/satsdice/models.py @@ -4,7 +4,7 @@ from typing import Dict, Optional from fastapi import Request from fastapi.param_functions import Query -from lnurl import Lnurl, LnurlWithdrawResponse +from lnurl import Lnurl from lnurl import encode as lnurl_encode # type: ignore from lnurl.types import LnurlPayMetadata # type: ignore from pydantic import BaseModel @@ -80,8 +80,7 @@ class satsdiceWithdraw(BaseModel): def is_spent(self) -> bool: return self.used >= 1 - @property - def lnurl_response(self, req: Request) -> LnurlWithdrawResponse: + def lnurl_response(self, req: Request): url = req.url_for("satsdice.api_lnurlw_callback", unique_hash=self.unique_hash) withdrawResponse = { "tag": "withdrawRequest", @@ -99,7 +98,7 @@ class HashCheck(BaseModel): lnurl_id: str @classmethod - def from_row(cls, row: Row) -> "Hash": + def from_row(cls, row: Row): return cls(**dict(row)) diff --git a/lnbits/extensions/satsdice/views.py b/lnbits/extensions/satsdice/views.py index 72e24867..d2b5e601 100644 --- a/lnbits/extensions/satsdice/views.py +++ b/lnbits/extensions/satsdice/views.py @@ -1,6 +1,8 @@ import random from http import HTTPStatus +from io import BytesIO +import pyqrcode from fastapi import Request from fastapi.param_functions import Query from fastapi.params import Depends @@ -20,13 +22,15 @@ from .crud import ( get_satsdice_withdraw, update_satsdice_payment, ) -from .models import CreateSatsDiceWithdraw, satsdiceLink +from .models import CreateSatsDiceWithdraw templates = Jinja2Templates(directory="templates") @satsdice_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, user: User = Depends(check_user_exists) # type: ignore +): return satsdice_renderer().TemplateResponse( "satsdice/index.html", {"request": request, "user": user.dict()} ) @@ -67,7 +71,7 @@ async def displaywin( ) withdrawLink = await get_satsdice_withdraw(payment_hash) payment = await get_satsdice_payment(payment_hash) - if payment.lost: + if not payment or payment.lost: return satsdice_renderer().TemplateResponse( "satsdice/error.html", {"request": request, "link": satsdicelink.id, "paid": False, "lost": True}, @@ -96,13 +100,18 @@ async def displaywin( ) await update_satsdice_payment(payment_hash, paid=1) paylink = await get_satsdice_payment(payment_hash) + if not paylink: + return satsdice_renderer().TemplateResponse( + "satsdice/error.html", + {"request": request, "link": satsdicelink.id, "paid": False, "lost": True}, + ) - data: CreateSatsDiceWithdraw = { - "satsdice_pay": satsdicelink.id, - "value": paylink.value * satsdicelink.multiplier, - "payment_hash": payment_hash, - "used": 0, - } + data = CreateSatsDiceWithdraw( + satsdice_pay=satsdicelink.id, + value=paylink.value * satsdicelink.multiplier, + payment_hash=payment_hash, + used=0, + ) withdrawLink = await create_satsdice_withdraw(data) return satsdice_renderer().TemplateResponse( @@ -121,9 +130,12 @@ async def displaywin( @satsdice_ext.get("/img/{link_id}", response_class=HTMLResponse) async def img(link_id): - link = await get_satsdice_pay(link_id) or abort( - HTTPStatus.NOT_FOUND, "satsdice link does not exist." - ) + link = await get_satsdice_pay(link_id) + if not link: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="satsdice link does not exist." + ) + qr = pyqrcode.create(link.lnurl) stream = BytesIO() qr.svg(stream, scale=3) diff --git a/lnbits/extensions/satsdice/views_api.py b/lnbits/extensions/satsdice/views_api.py index bccaa5ff..d33b76b8 100644 --- a/lnbits/extensions/satsdice/views_api.py +++ b/lnbits/extensions/satsdice/views_api.py @@ -15,9 +15,10 @@ from .crud import ( delete_satsdice_pay, get_satsdice_pay, get_satsdice_pays, + get_withdraw_hash_checkw, update_satsdice_pay, ) -from .models import CreateSatsDiceLink, CreateSatsDiceWithdraws, satsdiceLink +from .models import CreateSatsDiceLink ################LNURL pay @@ -25,13 +26,15 @@ from .models import CreateSatsDiceLink, CreateSatsDiceWithdraws, satsdiceLink @satsdice_ext.get("/api/v1/links") async def api_links( request: Request, - wallet: WalletTypeInfo = Depends(get_key_type), + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore all_wallets: bool = Query(False), ): wallet_ids = [wallet.wallet.id] if all_wallets: - wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids + user = await get_user(wallet.wallet.user) + if user: + wallet_ids = user.wallet_ids try: links = await get_satsdice_pays(wallet_ids) @@ -46,7 +49,7 @@ async def api_links( @satsdice_ext.get("/api/v1/links/{link_id}") async def api_link_retrieve( - link_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type) + link_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): link = await get_satsdice_pay(link_id) @@ -67,7 +70,7 @@ async def api_link_retrieve( @satsdice_ext.put("/api/v1/links/{link_id}", status_code=HTTPStatus.OK) async def api_link_create_or_update( data: CreateSatsDiceLink, - wallet: WalletTypeInfo = Depends(get_key_type), + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore link_id: str = Query(None), ): if data.min_bet > data.max_bet: @@ -95,10 +98,10 @@ async def api_link_create_or_update( @satsdice_ext.delete("/api/v1/links/{link_id}") async def api_link_delete( - wallet: WalletTypeInfo = Depends(get_key_type), link_id: str = Query(None) + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore + link_id: str = Query(None), ): link = await get_satsdice_pay(link_id) - if not link: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist." @@ -117,11 +120,12 @@ async def api_link_delete( ##########LNURL withdraw -@satsdice_ext.get("/api/v1/withdraws/{the_hash}/{lnurl_id}") +@satsdice_ext.get( + "/api/v1/withdraws/{the_hash}/{lnurl_id}", dependencies=[Depends(get_key_type)] +) async def api_withdraw_hash_retrieve( - wallet: WalletTypeInfo = Depends(get_key_type), lnurl_id: str = Query(None), the_hash: str = Query(None), ): - hashCheck = await get_withdraw_hash_check(the_hash, lnurl_id) + hashCheck = await get_withdraw_hash_checkw(the_hash, lnurl_id) return hashCheck diff --git a/pyproject.toml b/pyproject.toml index 7418de27..e66073c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,7 +95,6 @@ exclude = """(?x)( | ^lnbits/extensions/events. | ^lnbits/extensions/hivemind. | ^lnbits/extensions/invoices. - | ^lnbits/extensions/jukebox. | ^lnbits/extensions/livestream. | ^lnbits/extensions/lnaddress. | ^lnbits/extensions/lndhub. @@ -103,10 +102,8 @@ exclude = """(?x)( | ^lnbits/extensions/lnurldevice. | ^lnbits/extensions/lnurlp. | ^lnbits/extensions/lnurlpayout. - | ^lnbits/extensions/ngrok. | ^lnbits/extensions/offlineshop. | ^lnbits/extensions/paywall. - | ^lnbits/extensions/satsdice. | ^lnbits/extensions/satspay. | ^lnbits/extensions/scrub. | ^lnbits/extensions/splitpayments. From 41aa0743507b5ef62517e48afefa6c0c25e37fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Sat, 19 Nov 2022 21:44:21 +0100 Subject: [PATCH 83/96] Add webhook_api_key and webhook_custom_data to lnurlp webhook (#1065) * add webhook_api_key and webhook_custom_data to lnurlp * formatting * add json validation to lnurlp custom_data * motorina0 improvements --- lnbits/extensions/lnurlp/crud.py | 6 +++++- lnbits/extensions/lnurlp/migrations.py | 8 ++++++++ lnbits/extensions/lnurlp/models.py | 4 ++++ lnbits/extensions/lnurlp/tasks.py | 15 ++++++++++----- .../lnurlp/templates/lnurlp/index.html | 18 ++++++++++++++++++ lnbits/extensions/lnurlp/views_api.py | 19 +++++++++++++++++++ 6 files changed, 64 insertions(+), 6 deletions(-) diff --git a/lnbits/extensions/lnurlp/crud.py b/lnbits/extensions/lnurlp/crud.py index 9cb01fde..d02ae80e 100644 --- a/lnbits/extensions/lnurlp/crud.py +++ b/lnbits/extensions/lnurlp/crud.py @@ -21,13 +21,15 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink: served_meta, served_pr, webhook_url, + webhook_headers, + webhook_body, success_text, success_url, comment_chars, currency, fiat_base_multiplier ) - VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?) {returning} """, ( @@ -36,6 +38,8 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink: data.min, data.max, data.webhook_url, + data.webhook_headers, + data.webhook_body, data.success_text, data.success_url, data.comment_chars, diff --git a/lnbits/extensions/lnurlp/migrations.py b/lnbits/extensions/lnurlp/migrations.py index 81dd62f8..5258471d 100644 --- a/lnbits/extensions/lnurlp/migrations.py +++ b/lnbits/extensions/lnurlp/migrations.py @@ -60,3 +60,11 @@ async def m004_fiat_base_multiplier(db): await db.execute( "ALTER TABLE lnurlp.pay_links ADD COLUMN fiat_base_multiplier INTEGER DEFAULT 1;" ) + + +async def m005_webhook_headers_and_body(db): + """ + Add headers and body to webhooks + """ + await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN webhook_headers TEXT;") + await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN webhook_body TEXT;") diff --git a/lnbits/extensions/lnurlp/models.py b/lnbits/extensions/lnurlp/models.py index 4bd438a4..2cb4d0ab 100644 --- a/lnbits/extensions/lnurlp/models.py +++ b/lnbits/extensions/lnurlp/models.py @@ -18,6 +18,8 @@ class CreatePayLinkData(BaseModel): currency: str = Query(None) comment_chars: int = Query(0, ge=0, lt=800) webhook_url: str = Query(None) + webhook_headers: str = Query(None) + webhook_body: str = Query(None) success_text: str = Query(None) success_url: str = Query(None) fiat_base_multiplier: int = Query(100, ge=1) @@ -31,6 +33,8 @@ class PayLink(BaseModel): served_meta: int served_pr: int webhook_url: Optional[str] + webhook_headers: Optional[str] + webhook_body: Optional[str] success_text: Optional[str] success_url: Optional[str] currency: Optional[str] diff --git a/lnbits/extensions/lnurlp/tasks.py b/lnbits/extensions/lnurlp/tasks.py index 86f1579a..23f312cb 100644 --- a/lnbits/extensions/lnurlp/tasks.py +++ b/lnbits/extensions/lnurlp/tasks.py @@ -33,17 +33,22 @@ async def on_invoice_paid(payment: Payment) -> None: if pay_link and pay_link.webhook_url: async with httpx.AsyncClient() as client: try: - r = await client.post( - pay_link.webhook_url, - json={ + kwargs = { + "json": { "payment_hash": payment.payment_hash, "payment_request": payment.bolt11, "amount": payment.amount, "comment": payment.extra.get("comment"), "lnurlp": pay_link.id, }, - timeout=40, - ) + "timeout": 40, + } + if pay_link.webhook_body: + kwargs["json"]["body"] = json.loads(pay_link.webhook_body) + if pay_link.webhook_headers: + kwargs["headers"] = json.loads(pay_link.webhook_headers) + + r = await client.post(pay_link.webhook_url, **kwargs) await mark_webhook_sent(payment, r.status_code) except (httpx.ConnectError, httpx.RequestError): await mark_webhook_sent(payment, -1) diff --git a/lnbits/extensions/lnurlp/templates/lnurlp/index.html b/lnbits/extensions/lnurlp/templates/lnurlp/index.html index de90f5af..eb594cec 100644 --- a/lnbits/extensions/lnurlp/templates/lnurlp/index.html +++ b/lnbits/extensions/lnurlp/templates/lnurlp/index.html @@ -213,6 +213,24 @@ label="Webhook URL (optional)" hint="A URL to be called whenever this link receives a payment." > + + Date: Sun, 20 Nov 2022 17:29:07 +0000 Subject: [PATCH 84/96] Improved ad space function --- .env.example | 5 +++-- lnbits/core/templates/core/wallet.html | 13 ++++++++++--- lnbits/helpers.py | 1 + lnbits/settings.py | 3 +-- lnbits/static/images/lnbits-shop-dark.png | Bin 0 -> 12184 bytes lnbits/static/images/lnbits-shop-light.png | Bin 0 -> 14426 bytes lnbits/static/images/lnbits-shop.png | Bin 0 -> 14426 bytes 7 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 lnbits/static/images/lnbits-shop-dark.png create mode 100644 lnbits/static/images/lnbits-shop-light.png create mode 100644 lnbits/static/images/lnbits-shop.png diff --git a/.env.example b/.env.example index e76296ab..f90c10a6 100644 --- a/.env.example +++ b/.env.example @@ -6,14 +6,15 @@ PORT=5000 DEBUG=false -# Find "usr" string in wallet url to explicit allow users or set admins (comma separated list) LNBITS_ALLOWED_USERS="" LNBITS_ADMIN_USERS="" # Extensions only admin can access LNBITS_ADMIN_EXTENSIONS="ngrok" LNBITS_DEFAULT_WALLET_NAME="LNbits wallet" -# csv ad image filepaths or urls, extensions can choose to honor +# Ad space description +LNBITS_AD_SPACE_TITE"" +# csv ad space, format ";;, ;;", extensions can choose to honor LNBITS_AD_SPACE="" # Hides wallet api, extensions can choose to honor diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index 4bf6067c..813ae767 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -388,9 +388,16 @@ {% endif %} {% if AD_SPACE %} {% for ADS in AD_SPACE %} {% set AD = ADS.split(';') %} - +
+ {{ AD_TITLE }} +
+ + + + + + {% endfor %} {% endif %}
diff --git a/lnbits/helpers.py b/lnbits/helpers.py index e213240c..83876160 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -163,6 +163,7 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates: ) if settings.LNBITS_AD_SPACE: + t.env.globals["AD_TITLE"] = settings.LNBITS_AD_SPACE_TITLE t.env.globals["AD_SPACE"] = settings.LNBITS_AD_SPACE t.env.globals["HIDE_API"] = settings.LNBITS_HIDE_API t.env.globals["SITE_TITLE"] = settings.LNBITS_SITE_TITLE diff --git a/lnbits/settings.py b/lnbits/settings.py index 73b0d6c9..0f4064d5 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -18,8 +18,6 @@ DEBUG = env.bool("DEBUG", default=False) HOST = env.str("HOST", default="127.0.0.1") PORT = env.int("PORT", default=5000) -FORWARDED_ALLOW_IPS = env.str("FORWARDED_ALLOW_IPS", default="127.0.0.1") - LNBITS_PATH = path.dirname(path.realpath(__file__)) LNBITS_DATA_FOLDER = env.str( "LNBITS_DATA_FOLDER", default=path.join(LNBITS_PATH, "data") @@ -40,6 +38,7 @@ LNBITS_DISABLED_EXTENSIONS: List[str] = [ for x in env.list("LNBITS_DISABLED_EXTENSIONS", default=[], subcast=str) ] +LNBITS_AD_SPACE_TITLE = env.str("LNBITS_AD_SPACE_TITLE", default="Optional Advert Space") LNBITS_AD_SPACE = [x.strip(" ") for x in env.list("LNBITS_AD_SPACE", default=[])] LNBITS_HIDE_API = env.bool("LNBITS_HIDE_API", default=False) LNBITS_SITE_TITLE = env.str("LNBITS_SITE_TITLE", default="LNbits") diff --git a/lnbits/static/images/lnbits-shop-dark.png b/lnbits/static/images/lnbits-shop-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..3dd677dc9a7564eecb173f7d6f281cd3fae11bf2 GIT binary patch literal 12184 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 HoD!MM^RC*<`0wMdzwVW1 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 HoD!M<*T1#i literal 0 HcmV?d00001 From 304812060916dc2646d35cf74e53417cbb213720 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 20 Nov 2022 17:36:00 +0000 Subject: [PATCH 85/96] img not needed --- lnbits/static/images/lnbits-shop.png | Bin 14426 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lnbits/static/images/lnbits-shop.png diff --git a/lnbits/static/images/lnbits-shop.png b/lnbits/static/images/lnbits-shop.png deleted file mode 100644 index b2320f3b663aea9728cd5c56e692cdc74f596cfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14426 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 HoD!M<*T1#i From 3f475bf1ea18bf2180896362159f9c0e5d5a5464 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 20 Nov 2022 17:41:57 +0000 Subject: [PATCH 86/96] Defaulted to null in env.example --- .env.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index f90c10a6..de9cb3f7 100644 --- a/.env.example +++ b/.env.example @@ -13,9 +13,9 @@ LNBITS_ADMIN_EXTENSIONS="ngrok" LNBITS_DEFAULT_WALLET_NAME="LNbits wallet" # Ad space description -LNBITS_AD_SPACE_TITE"" +# LNBITS_AD_SPACE_TITLE="Supported by" # csv ad space, format ";;, ;;", extensions can choose to honor -LNBITS_AD_SPACE="" +# LNBITS_AD_SPACE="" # Hides wallet api, extensions can choose to honor LNBITS_HIDE_API=false From 0836ba6c77a02ed229bf63505296d5967cb9d77f Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 20 Nov 2022 17:59:43 +0000 Subject: [PATCH 87/96] Added to homescreen --- lnbits/core/templates/core/index.html | 15 +++++++++++++++ lnbits/core/templates/core/wallet.html | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html index 68a7b7ed..63d8668e 100644 --- a/lnbits/core/templates/core/index.html +++ b/lnbits/core/templates/core/index.html @@ -183,6 +183,21 @@
 
+ + {% if AD_SPACE %} {% for ADS in AD_SPACE %} {% set AD = ADS.split(';') %} + + {% endfor %} {% endif %}
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index 813ae767..fb392c32 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -394,7 +394,7 @@ - + Date: Sun, 20 Nov 2022 18:06:29 +0000 Subject: [PATCH 88/96] slight ui tweak for mobile --- lnbits/core/templates/core/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html index 63d8668e..8c3c5b0f 100644 --- a/lnbits/core/templates/core/index.html +++ b/lnbits/core/templates/core/index.html @@ -192,9 +192,9 @@ - +
{% endfor %} {% endif %} From f458fc416c974b95328b1f7a721b1d94639d4737 Mon Sep 17 00:00:00 2001 From: ben Date: Sun, 20 Nov 2022 18:23:18 +0000 Subject: [PATCH 89/96] black --- lnbits/settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lnbits/settings.py b/lnbits/settings.py index 0f4064d5..c50e0a51 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -38,7 +38,9 @@ LNBITS_DISABLED_EXTENSIONS: List[str] = [ for x in env.list("LNBITS_DISABLED_EXTENSIONS", default=[], subcast=str) ] -LNBITS_AD_SPACE_TITLE = env.str("LNBITS_AD_SPACE_TITLE", default="Optional Advert Space") +LNBITS_AD_SPACE_TITLE = env.str( + "LNBITS_AD_SPACE_TITLE", default="Optional Advert Space" +) LNBITS_AD_SPACE = [x.strip(" ") for x in env.list("LNBITS_AD_SPACE", default=[])] LNBITS_HIDE_API = env.bool("LNBITS_HIDE_API", default=False) LNBITS_SITE_TITLE = env.str("LNBITS_SITE_TITLE", default="LNbits") From d9a153d43d5aca6a3d62962c1c4184e81801186a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 22 Nov 2022 10:37:13 +0100 Subject: [PATCH 90/96] fix tests to low balance --- tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 1e719c76..458ce2b9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -60,7 +60,7 @@ async def from_wallet(from_user): wallet = await create_wallet(user_id=user.id, wallet_name="test_wallet_from") await credit_wallet( wallet_id=wallet.id, - amount=99999999, + amount=999999999, ) yield wallet @@ -77,7 +77,7 @@ async def to_wallet(to_user): wallet = await create_wallet(user_id=user.id, wallet_name="test_wallet_to") await credit_wallet( wallet_id=wallet.id, - amount=99999999, + amount=999999999, ) yield wallet From 3edbe1b89557210d05db9c755b56efd0539122bb Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Tue, 22 Nov 2022 09:35:15 +0200 Subject: [PATCH 91/96] fix: allow both `description_hash` and `unhashed_description` to be present --- lnbits/core/views/api.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 983d5a26..11075370 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -155,30 +155,29 @@ class CreateInvoiceData(BaseModel): async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet): - if data.description_hash: + if data.description_hash or data.unhashed_description: try: - description_hash = binascii.unhexlify(data.description_hash) + description_hash = ( + binascii.unhexlify(data.description_hash) + if data.description_hash + else b"" + ) + unhashed_description = ( + binascii.unhexlify(data.unhashed_description) + if data.unhashed_description + else b"" + ) except binascii.Error: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, - detail="'description_hash' must be a valid hex string", + detail="'description_hash' and 'unhashed_description' must be a valid hex strings", ) - unhashed_description = b"" - memo = "" - elif data.unhashed_description: - try: - unhashed_description = binascii.unhexlify(data.unhashed_description) - except binascii.Error: - raise HTTPException( - status_code=HTTPStatus.BAD_REQUEST, - detail="'unhashed_description' must be a valid hex string", - ) - description_hash = b"" memo = "" else: description_hash = b"" unhashed_description = b"" memo = data.memo or LNBITS_SITE_TITLE + if data.unit == "sat": amount = int(data.amount) else: From 50dc6f9c556dd57c09063767548d0586eb226fff Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Wed, 23 Nov 2022 13:43:04 +0000 Subject: [PATCH 92/96] add select users wallet --- .../splitpayments/templates/splitpayments/index.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/splitpayments/templates/splitpayments/index.html b/lnbits/extensions/splitpayments/templates/splitpayments/index.html index 5862abc1..1cceb7ba 100644 --- a/lnbits/extensions/splitpayments/templates/splitpayments/index.html +++ b/lnbits/extensions/splitpayments/templates/splitpayments/index.html @@ -31,14 +31,20 @@ style="flex-wrap: nowrap" v-for="(target, t) in targets" > - + option-label="name" + style="width: 1000px" + new-value-mode="add-unique" + use-input + input-debounce="0" + emit-value + > Date: Wed, 23 Nov 2022 13:48:36 +0000 Subject: [PATCH 93/96] fix typo in events --- lnbits/extensions/events/templates/events/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/events/templates/events/index.html b/lnbits/extensions/events/templates/events/index.html index 7f34a3e2..21258930 100644 --- a/lnbits/extensions/events/templates/events/index.html +++ b/lnbits/extensions/events/templates/events/index.html @@ -260,7 +260,7 @@ dense v-model.number="formDialog.data.price_per_ticket" type="number" - label="Price per ticket " + label="Sats per ticket " >
From 390d74eaf6d128c5c24b339902646d6ec4b5fa17 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 23 Nov 2022 16:57:27 +0000 Subject: [PATCH 94/96] prettier --- lnbits/core/templates/core/index.html | 8 +++++--- lnbits/core/templates/core/wallet.html | 4 +--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html index 8c3c5b0f..5f26cb03 100644 --- a/lnbits/core/templates/core/index.html +++ b/lnbits/core/templates/core/index.html @@ -186,15 +186,17 @@ {% if AD_SPACE %} {% for ADS in AD_SPACE %} {% set AD = ADS.split(';') %}
- {{ AD_TITLE }} + {{ AD_TITLE }} - +
{% endfor %} {% endif %} diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index fb392c32..22fbd05d 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -389,9 +389,7 @@ ADS.split(';') %} -
- {{ AD_TITLE }} -
+
{{ AD_TITLE }}
From 71d105b5d57d4214412fe85200ed0efde4461107 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 23 Nov 2022 17:06:45 +0000 Subject: [PATCH 95/96] Accidental delete --- lnbits/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lnbits/settings.py b/lnbits/settings.py index c50e0a51..17fce293 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -18,6 +18,8 @@ DEBUG = env.bool("DEBUG", default=False) HOST = env.str("HOST", default="127.0.0.1") PORT = env.int("PORT", default=5000) +FORWARDED_ALLOW_IPS = env.str("FORWARDED_ALLOW_IPS", default="127.0.0.1") + LNBITS_PATH = path.dirname(path.realpath(__file__)) LNBITS_DATA_FOLDER = env.str( "LNBITS_DATA_FOLDER", default=path.join(LNBITS_PATH, "data") From f6047d9ea52e140ff5d2fea43ed6bdd427f57640 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Thu, 24 Nov 2022 15:43:39 +0100 Subject: [PATCH 96/96] revert to PR #1125 --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index de9cb3f7..b7fb9f06 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,7 @@ PORT=5000 DEBUG=false +# Allow users and admins by user IDs (comma separated list) LNBITS_ALLOWED_USERS="" LNBITS_ADMIN_USERS="" # Extensions only admin can access