From 947dc50d2e6598e52e36d86741d6c12a9cd6b67d Mon Sep 17 00:00:00 2001 From: Gene Takavic <80261724+iWarpBTC@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:14:24 +0200 Subject: [PATCH 001/614] readme update --- lnbits/extensions/boltcards/README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lnbits/extensions/boltcards/README.md b/lnbits/extensions/boltcards/README.md index f9c594093..a73029062 100644 --- a/lnbits/extensions/boltcards/README.md +++ b/lnbits/extensions/boltcards/README.md @@ -10,18 +10,17 @@ This extension allows you to link your Bolt Card (or other compatible NXP NTAG d ## About the keys -Up to five 16-byte keys can be stored on the card, numbered from 00 to 04. In the empty state they all should be set to zeros (00000000000000000000000000000000). For this extension only two keys need to be set: +Up to five 16-byte keys can be stored on the card, numbered from 00 to 04. In the empty state they all should be set to zeros (00000000000000000000000000000000). For this extension only two keys need to be set, but for the security reasons all five keys should be changed from default (empty) state. The keys directly needed by this extension are: -One for encrypting the card UID and the counter (p parameter), let's called it meta key, key #01 or K1. +- One for encrypting the card UID and the counter (p parameter), let's called it meta key, key #01 or K1. -One for calculating CMAC (c parameter), let's called it file key, key #02 or K2. +- One for calculating CMAC (c parameter), let's called it file key, key #02 or K2. -The key #00, K0 (also know as auth key) is skipped to be use as authentification key. Is not needed by this extension, but can be filled in order to write the keys in cooperation with bolt-nfc-android-app. +The key #00, K0 (also know as auth key) is skipped to be used as authentification key. It is not needed by this extension, but should be filled in order to write the keys in cooperation with bolt-nfc-android-app. In this case also K3 is set to same value as K1 and K4 as K2, so all keys are changed from default values. Keep that in your mind in case you need to reset the keys manually. ***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!*** ## Setting the card - bolt-nfc-android-app (easy way) -So far, regarding the keys, the app can only write a new key set on an empty card (with zero keys). **When you write non zero (and 'non debug') keys, they can't be rewrite with this app.** You have to do it on your computer. - Read the card with the app. Note UID so you can fill it in the extension later. - Write the link on the card. It shoud be like `YOUR_LNBITS_DOMAIN/boltcards/api/v1/scan/{external_id}` @@ -35,10 +34,10 @@ So far, regarding the keys, the app can only write a new key set on an empty car - If on an Android device with a newish version of Chrome, you can click the icon next to the input and tap your card to autofill this field. - Advanced Options - Card Keys (k0, k1, k2) will be automatically generated if not explicitly set. - - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in debug mode. - - GENERATE KEY button fill the keys randomly. If there is "debug" in the card name, a debug set of keys is filled instead. + - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default state. + - GENERATE KEY button fill the keys randomly. If there is "debug" in the card name, a debug set of keys is filled instead. - Click CREATE CARD button -- Click the QR code button next to a card to view its details. You can scan the QR code with the Android app to import the keys. +- Click the QR code button next to a card to view its details. Backup the keys! You can scan the QR code with the Android app to import the keys. - Click the "KEYS / AUTH LINK" button to copy the auth URL to the clipboard. You can then paste this into the Android app to import the keys. - Tap the NFC card to write the keys to the card. @@ -48,7 +47,7 @@ Follow the guide. The URI should be `lnurlw://YOUR-DOMAIN.COM/boltcards/api/v1/scan/{YOUR_card_external_id}?p=00000000000000000000000000000000&c=0000000000000000` -Then fill up the card parameters in the extension. Card Auth key (K0) can be omitted. Initical counter can be 0. +Then fill up the card parameters in the extension. Card Auth key (K0) can be filled just for the record. Initical counter can be 0. ## Setting the card - android NXP app (hard way) - If you don't know the card ID, use NXP TagInfo app to find it out. From e1eb0895890c068a29ee32f4fe7910698522da0e Mon Sep 17 00:00:00 2001 From: Gene Takavic <80261724+iWarpBTC@users.noreply.github.com> Date: Mon, 12 Sep 2022 22:44:22 +0200 Subject: [PATCH 002/614] readme minor changes --- lnbits/extensions/boltcards/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/boltcards/README.md b/lnbits/extensions/boltcards/README.md index a73029062..8e093f3e6 100644 --- a/lnbits/extensions/boltcards/README.md +++ b/lnbits/extensions/boltcards/README.md @@ -34,7 +34,7 @@ The key #00, K0 (also know as auth key) is skipped to be used as authentificatio - If on an Android device with a newish version of Chrome, you can click the icon next to the input and tap your card to autofill this field. - Advanced Options - Card Keys (k0, k1, k2) will be automatically generated if not explicitly set. - - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default state. + - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default (empty) state. - GENERATE KEY button fill the keys randomly. If there is "debug" in the card name, a debug set of keys is filled instead. - Click CREATE CARD button - Click the QR code button next to a card to view its details. Backup the keys! You can scan the QR code with the Android app to import the keys. @@ -47,7 +47,7 @@ Follow the guide. The URI should be `lnurlw://YOUR-DOMAIN.COM/boltcards/api/v1/scan/{YOUR_card_external_id}?p=00000000000000000000000000000000&c=0000000000000000` -Then fill up the card parameters in the extension. Card Auth key (K0) can be filled just for the record. Initical counter can be 0. +Then fill up the card parameters in the extension. Card Auth key (K0) can be filled in the extension just for the record. Initical counter can be 0. ## Setting the card - android NXP app (hard way) - If you don't know the card ID, use NXP TagInfo app to find it out. From 3660d96295ea75be08cd1e55b7899da60cd66eb9 Mon Sep 17 00:00:00 2001 From: Gene Takavic Date: Tue, 20 Sep 2022 15:53:57 +0200 Subject: [PATCH 003/614] rename setting app + delete msg --- lnbits/extensions/boltcards/README.md | 6 +++--- lnbits/extensions/boltcards/static/js/index.js | 2 +- lnbits/extensions/boltcards/templates/boltcards/index.html | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/boltcards/README.md b/lnbits/extensions/boltcards/README.md index 8e093f3e6..c6d12311f 100644 --- a/lnbits/extensions/boltcards/README.md +++ b/lnbits/extensions/boltcards/README.md @@ -6,7 +6,7 @@ This extension allows you to link your Bolt Card (or other compatible NXP NTAG d **Disclaimer:** ***Use this only if you either know what you are doing or are a reckless lightning pioneer. Only you are responsible for all your sats, cards and other devices. Always backup all your card keys!*** -***In order to use this extension you need to be able to setup your own card.*** That means writing a URL template pointing to your LNBits instance, configuring some SUN (SDM) settings and optionally changing the card's keys. There's a [guide](https://www.whitewolftech.com/articles/payment-card/) to set it up with a card reader connected to your computer. It can be done (without setting the keys) with [TagWriter app by NXP](https://play.google.com/store/apps/details?id=com.nxp.nfc.tagwriter) Android app. Last but not least, an OSS android app by name [bolt-nfc-android-app](https://github.com/boltcard/bolt-nfc-android-app) is being developed for these purposes. It's available from Google Play [here](https://play.google.com/store/apps/details?id=com.lightningnfcapp). +***In order to use this extension you need to be able to setup your own card.*** That means writing a URL template pointing to your LNBits instance, configuring some SUN (SDM) settings and optionally changing the card's keys. There's a [guide](https://www.whitewolftech.com/articles/payment-card/) to set it up with a card reader connected to your computer. It can be done (without setting the keys) with [TagWriter app by NXP](https://play.google.com/store/apps/details?id=com.nxp.nfc.tagwriter) Android app. Last but not least, an OSS android app by name [Boltcard NFC Card Creator](https://github.com/boltcard/bolt-nfc-android-app) is being developed for these purposes. It's available from Google Play [here](https://play.google.com/store/apps/details?id=com.lightningnfcapp). ## About the keys @@ -16,11 +16,11 @@ Up to five 16-byte keys can be stored on the card, numbered from 00 to 04. In th - One for calculating CMAC (c parameter), let's called it file key, key #02 or K2. -The key #00, K0 (also know as auth key) is skipped to be used as authentification key. It is not needed by this extension, but should be filled in order to write the keys in cooperation with bolt-nfc-android-app. In this case also K3 is set to same value as K1 and K4 as K2, so all keys are changed from default values. Keep that in your mind in case you need to reset the keys manually. +The key #00, K0 (also know as auth key) is skipped to be used as authentification key. It is not needed by this extension, but should be filled in order to write the keys in cooperation with Boltcard NFC Card Creator. In this case also K3 is set to same value as K1 and K4 as K2, so all keys are changed from default values. Keep that in your mind in case you need to reset the keys manually. ***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!*** -## Setting the card - bolt-nfc-android-app (easy way) +## Setting the card - Boltcard NFC Card Creator (easy way) - Read the card with the app. Note UID so you can fill it in the extension later. - Write the link on the card. It shoud be like `YOUR_LNBITS_DOMAIN/boltcards/api/v1/scan/{external_id}` diff --git a/lnbits/extensions/boltcards/static/js/index.js b/lnbits/extensions/boltcards/static/js/index.js index 11df222a8..2ecde39d7 100644 --- a/lnbits/extensions/boltcards/static/js/index.js +++ b/lnbits/extensions/boltcards/static/js/index.js @@ -398,7 +398,7 @@ new Vue({ let cards = _.findWhere(this.cards, {id: cardId}) LNbits.utils - .confirmDialog('Are you sure you want to delete this card') + .confirmDialog('Are you sure you want to delete this card? Without access to the card keys you won\'t be able to reset them in the future!') .onOk(function () { LNbits.api .request( diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html index 55cc1e5e2..3e07024cb 100644 --- a/lnbits/extensions/boltcards/templates/boltcards/index.html +++ b/lnbits/extensions/boltcards/templates/boltcards/index.html @@ -370,7 +370,7 @@ bolt-nfc-android-appBoltcard NFC Card Creator)

From 632d35682d84c981b926ef47f8e6fc9d274b948e Mon Sep 17 00:00:00 2001 From: Gene Takavic Date: Fri, 23 Sep 2022 15:17:21 +0200 Subject: [PATCH 004/614] payment notification webhook --- lnbits/extensions/boltcards/crud.py | 6 +++-- lnbits/extensions/boltcards/lnurl.py | 23 ++++++++++++++++++- lnbits/extensions/boltcards/migrations.py | 8 +++++++ lnbits/extensions/boltcards/models.py | 2 ++ .../extensions/boltcards/static/js/index.js | 8 +++++-- .../boltcards/templates/boltcards/index.html | 12 +++++++++- 6 files changed, 53 insertions(+), 6 deletions(-) diff --git a/lnbits/extensions/boltcards/crud.py b/lnbits/extensions/boltcards/crud.py index c541346e3..39ee3f40d 100644 --- a/lnbits/extensions/boltcards/crud.py +++ b/lnbits/extensions/boltcards/crud.py @@ -27,9 +27,10 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card: k0, k1, k2, - otp + otp, + webhook_url ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( card_id, @@ -45,6 +46,7 @@ async def create_card(data: CreateCardData, wallet_id: str) -> Card: data.k1, data.k2, secrets.token_hex(16), + data.webhook_url, ), ) card = await get_card(card_id) diff --git a/lnbits/extensions/boltcards/lnurl.py b/lnbits/extensions/boltcards/lnurl.py index 6fb9ad8dc..be3e09d81 100644 --- a/lnbits/extensions/boltcards/lnurl.py +++ b/lnbits/extensions/boltcards/lnurl.py @@ -8,6 +8,7 @@ from io import BytesIO from typing import Optional from urllib.parse import urlparse +import httpx from embit import bech32, compact from fastapi import Request from fastapi.param_functions import Query @@ -119,12 +120,32 @@ async def lnurl_callback( invoice = bolt11.decode(pr) hit = await spend_hit(id=hit.id, amount=int(invoice.amount_msat / 1000)) try: - await pay_invoice( + payment_hash = await pay_invoice( wallet_id=card.wallet, payment_request=pr, max_sat=card.tx_limit, extra={"tag": "boltcard", "tag": hit.id}, ) + + if card.webhook_url: + async with httpx.AsyncClient() as client: + try: + r = await client.post( + card.webhook_url, + json={ + "notification": "card_payment", + "payment_hash": payment_hash, + "payment_request": pr, + "card_external_id": card.external_id, + "card_name": card.card_name, + "amount": int(invoice.amount_msat / 1000), + }, + timeout=40, + ) + except Exception as exc: + # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid + logger.error("Caught exception when dispatching webhook url:", exc) + return {"status": "OK"} except: return {"status": "ERROR", "reason": f"Payment failed"} diff --git a/lnbits/extensions/boltcards/migrations.py b/lnbits/extensions/boltcards/migrations.py index 081260139..25a59fdb0 100644 --- a/lnbits/extensions/boltcards/migrations.py +++ b/lnbits/extensions/boltcards/migrations.py @@ -58,3 +58,11 @@ async def m001_initial(db): ); """ ) + + +async def m002_add_webhook(db): + await db.execute( + """ + ALTER TABLE boltcards.cards ADD COLUMN webhook_url TEXT NOT NULL DEFAULT ''; + """ + ) diff --git a/lnbits/extensions/boltcards/models.py b/lnbits/extensions/boltcards/models.py index 47ca1df09..8e6f77c96 100644 --- a/lnbits/extensions/boltcards/models.py +++ b/lnbits/extensions/boltcards/models.py @@ -30,6 +30,7 @@ class Card(BaseModel): prev_k1: str prev_k2: str otp: str + webhook_url: str time: int def from_row(cls, row: Row) -> "Card": @@ -56,6 +57,7 @@ class CreateCardData(BaseModel): prev_k0: str = Query(ZERO_KEY) prev_k1: str = Query(ZERO_KEY) prev_k2: str = Query(ZERO_KEY) + webhook_url: str = Query(...) class Hit(BaseModel): diff --git a/lnbits/extensions/boltcards/static/js/index.js b/lnbits/extensions/boltcards/static/js/index.js index 2ecde39d7..e13c14fb6 100644 --- a/lnbits/extensions/boltcards/static/js/index.js +++ b/lnbits/extensions/boltcards/static/js/index.js @@ -23,6 +23,7 @@ new Vue({ cardDialog: { show: false, data: { + webhook_url: '', counter: 1, k0: '', k1: '', @@ -270,7 +271,8 @@ new Vue({ k1: card.k1, k2: card.k2, k3: card.k1, - k4: card.k2 + k4: card.k2, + webhook_url: card.webhook_url } this.qrCodeDialog.show = true }, @@ -398,7 +400,9 @@ new Vue({ let cards = _.findWhere(this.cards, {id: cardId}) LNbits.utils - .confirmDialog('Are you sure you want to delete this card? Without access to the card keys you won\'t be able to reset them in the future!') + .confirmDialog( + "Are you sure you want to delete this card? Without access to the card keys you won't be able to reset them in the future!" + ) .onOk(function () { LNbits.api .request( diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html index 3e07024cb..f795e4545 100644 --- a/lnbits/extensions/boltcards/templates/boltcards/index.html +++ b/lnbits/extensions/boltcards/templates/boltcards/index.html @@ -283,7 +283,7 @@ v-model="toggleAdvanced" label="Show advanced options" > -

+
Zero if you don't know. + + Lock key: {{ qrCodeDialog.data.k0 }}
Meta key: {{ qrCodeDialog.data.k1 }}
File key: {{ qrCodeDialog.data.k2 }}
+ Notification webhook: {{ qrCodeDialog.data.webhook_url + }}


Date: Fri, 23 Sep 2022 16:19:58 +0200 Subject: [PATCH 005/614] Update README.md Updated for Boltcard NFC Card Creator v0.1.1 --- lnbits/extensions/boltcards/README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lnbits/extensions/boltcards/README.md b/lnbits/extensions/boltcards/README.md index c6d12311f..5140ecc2a 100644 --- a/lnbits/extensions/boltcards/README.md +++ b/lnbits/extensions/boltcards/README.md @@ -21,10 +21,7 @@ The key #00, K0 (also know as auth key) is skipped to be used as authentificatio ***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!*** ## Setting the card - Boltcard NFC Card Creator (easy way) - -- Read the card with the app. Note UID so you can fill it in the extension later. -- Write the link on the card. It shoud be like `YOUR_LNBITS_DOMAIN/boltcards/api/v1/scan/{external_id}` - - `{external_id}` should be replaced with the External ID found in the LNBits dialog. +Updated for v0.1.1 - Add new card in the extension. - Set a max sats per transaction. Any transaction greater than this amount will be rejected. @@ -32,14 +29,16 @@ The key #00, K0 (also know as auth key) is skipped to be used as authentificatio - Set a card name. This is just for your reference inside LNBits. - Set the card UID. This is the unique identifier on your NFC card and is 7 bytes. - If on an Android device with a newish version of Chrome, you can click the icon next to the input and tap your card to autofill this field. + - Otherwise read it with the Android app (Advanced -> Read NFC) and paste it to the field. - Advanced Options - Card Keys (k0, k1, k2) will be automatically generated if not explicitly set. - - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default (empty) state. - - GENERATE KEY button fill the keys randomly. If there is "debug" in the card name, a debug set of keys is filled instead. + - Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default (empty) state (this is unsecure). + - GENERATE KEY button fill the keys randomly. - Click CREATE CARD button -- Click the QR code button next to a card to view its details. Backup the keys! You can scan the QR code with the Android app to import the keys. -- Click the "KEYS / AUTH LINK" button to copy the auth URL to the clipboard. You can then paste this into the Android app to import the keys. -- Tap the NFC card to write the keys to the card. +- Click the QR code button next to a card to view its details. Backup the keys now! They'll be comfortable in your password manager. + - Now you can scan the QR code with the Android app (Create Bolt Card -> SCAN QR CODE). + - Or you can Click the "KEYS / AUTH LINK" button to copy the auth URL to the clipboard. Then paste it into the Android app (Create Bolt Card -> PASTE AUTH URL). +- Click WRITE CARD NOW and tap the NFC card to set it up. DO NOT REMOVE THE CARD PREMATURELY! ## Setting the card - computer (hard way) @@ -69,4 +68,4 @@ Then fill up the card parameters in the extension. Card Auth key (K0) can be fil - Save & Write - Scan with compatible Wallet -This app afaik cannot change the keys. If you cannot change them any other way, leave them empty in the extension dialog and remember you're not secure. Card Auth key (K0) can be omitted anyway. Initical counter can be 0. +This app afaik cannot change the keys. If you cannot change them any other way, leave them empty in the extension dialog and remember you're not secured. Card Auth key (K0) can be omitted anyway. Initical counter can be 0. From ea2d66fbd628681618dbd411f544f63ee8692a5f Mon Sep 17 00:00:00 2001 From: Gene Takavic Date: Fri, 23 Sep 2022 17:11:19 +0200 Subject: [PATCH 006/614] refund notification --- lnbits/extensions/boltcards/tasks.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/boltcards/tasks.py b/lnbits/extensions/boltcards/tasks.py index 1b51c98ba..27929efc7 100644 --- a/lnbits/extensions/boltcards/tasks.py +++ b/lnbits/extensions/boltcards/tasks.py @@ -2,12 +2,13 @@ import asyncio import json import httpx +from loguru import logger from lnbits.core import db as core_db from lnbits.core.models import Payment from lnbits.tasks import register_invoice_listener -from .crud import create_refund, get_hit +from .crud import create_refund, get_card, get_hit async def wait_for_paid_invoices(): @@ -34,6 +35,25 @@ async def on_invoice_paid(payment: Payment) -> None: ) await mark_webhook_sent(payment, 1) + card = await get_card(hit.card_id) + if card.webhook_url: + async with httpx.AsyncClient() as client: + try: + r = await client.post( + card.webhook_url, + json={ + "notification": "card_refund", + "payment_hash": payment.payment_hash, + "payment_request": payment.bolt11, + "card_external_id": card.external_id, + "card_name": card.card_name, + "amount": int(payment.amount / 1000), + }, + timeout=40, + ) + except Exception as exc: + logger.error("Caught exception when dispatching webhook url:", exc) + async def mark_webhook_sent(payment: Payment, status: int) -> None: payment.extra["wh_status"] = status From 8b3af03519c06437325e32e78a71febf9f91d3a1 Mon Sep 17 00:00:00 2001 From: Gene Takavic Date: Sun, 25 Sep 2022 17:01:22 +0200 Subject: [PATCH 007/614] webhook to pay_invoice --- lnbits/core/services.py | 31 ++++++++++++++++- lnbits/extensions/boltcards/lnurl.py | 42 ++++++++---------------- lnbits/extensions/boltcards/views_api.py | 5 --- lnbits/extensions/withdraw/lnurl.py | 30 +++++++---------- 4 files changed, 56 insertions(+), 52 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 10693f4b9..aeb4f9382 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -2,7 +2,7 @@ import asyncio import json from binascii import unhexlify from io import BytesIO -from typing import Dict, Optional, Tuple +from typing import Dict, Optional, Tuple, Union from urllib.parse import parse_qs, urlparse import httpx @@ -102,6 +102,7 @@ async def pay_invoice( extra: Optional[Dict] = None, description: str = "", conn: Optional[Connection] = None, + webhook: Optional[Union[str, tuple]] = None, ) -> str: """ Pay a Lightning invoice. @@ -231,6 +232,34 @@ async def pay_invoice( f"didn't receive checking_id from backend, payment may be stuck in database: {temp_id}" ) + if type(webhook) is str: + webhook_url = webhook + elif type(webhook) is tuple: + webhook_url = webhook[0] + additionals = webhook[1] + else: + webhook_url = None + + if webhook_url: + async with httpx.AsyncClient() as client: + try: + json = { + "payment_hash": invoice.payment_hash, + "payment_request": payment_request, + "amount": int(invoice.amount_msat / 1000), + } + if type(additionals) is dict: + json.update(additionals) + + r = await client.post( + webhook_url, + json=json, + timeout=40, + ) + except Exception as exc: + # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid + logger.error("Caught exception when dispatching webhook url:", exc) + return invoice.payment_hash diff --git a/lnbits/extensions/boltcards/lnurl.py b/lnbits/extensions/boltcards/lnurl.py index be3e09d81..320b16661 100644 --- a/lnbits/extensions/boltcards/lnurl.py +++ b/lnbits/extensions/boltcards/lnurl.py @@ -1,19 +1,12 @@ -import base64 -import hashlib -import hmac import json import secrets from http import HTTPStatus -from io import BytesIO -from typing import Optional from urllib.parse import urlparse -import httpx from embit import bech32, compact from fastapi import Request from fastapi.param_functions import Query from fastapi.params import Depends, Query -from lnurl import Lnurl, LnurlWithdrawResponse from lnurl import encode as lnurl_encode # type: ignore from lnurl.types import LnurlPayMetadata # type: ignore from loguru import logger @@ -34,7 +27,6 @@ from .crud import ( get_hit, get_hits_today, spend_hit, - update_card, update_card_counter, update_card_otp, ) @@ -120,32 +112,26 @@ async def lnurl_callback( invoice = bolt11.decode(pr) hit = await spend_hit(id=hit.id, amount=int(invoice.amount_msat / 1000)) try: - payment_hash = await pay_invoice( + webhook = ( + ( + card.webhook_url, + { + "notification": "card_payment", + "card_external_id": card.external_id, + "card_name": card.card_name, + }, + ) + if card.webhook_url + else None + ) + await pay_invoice( wallet_id=card.wallet, payment_request=pr, max_sat=card.tx_limit, extra={"tag": "boltcard", "tag": hit.id}, + webhook=webhook, ) - if card.webhook_url: - async with httpx.AsyncClient() as client: - try: - r = await client.post( - card.webhook_url, - json={ - "notification": "card_payment", - "payment_hash": payment_hash, - "payment_request": pr, - "card_external_id": card.external_id, - "card_name": card.card_name, - "amount": int(invoice.amount_msat / 1000), - }, - timeout=40, - ) - except Exception as exc: - # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid - logger.error("Caught exception when dispatching webhook url:", exc) - return {"status": "OK"} except: return {"status": "ERROR", "reason": f"Payment failed"} diff --git a/lnbits/extensions/boltcards/views_api.py b/lnbits/extensions/boltcards/views_api.py index 7b8357cf8..2fc11dbc8 100644 --- a/lnbits/extensions/boltcards/views_api.py +++ b/lnbits/extensions/boltcards/views_api.py @@ -12,21 +12,16 @@ from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key from . import boltcards_ext from .crud import ( create_card, - create_hit, delete_card, enable_disable_card, get_card, - get_card_by_otp, get_card_by_uid, get_cards, get_hits, get_refunds, update_card, - update_card_counter, - update_card_otp, ) from .models import CreateCardData -from .nxp424 import decryptSUN, getSunMAC @boltcards_ext.get("/api/v1/cards") diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index 18a99599c..3379fd17d 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -3,7 +3,6 @@ import traceback from datetime import datetime from http import HTTPStatus -import httpx import shortuuid # type: ignore from fastapi import HTTPException from fastapi.param_functions import Query @@ -115,29 +114,24 @@ async def api_lnurl_callback( payment_request = pr - payment_hash = await pay_invoice( + webhook = ( + ( + link.webhook_url, + { + "lnurlw": link.id, + }, + ) + if link.webhook_url + else None + ) + await pay_invoice( wallet_id=link.wallet, payment_request=payment_request, max_sat=link.max_withdrawable, extra={"tag": "withdraw"}, + webhook=webhook, ) - if link.webhook_url: - async with httpx.AsyncClient() as client: - try: - r = await client.post( - link.webhook_url, - json={ - "payment_hash": payment_hash, - "payment_request": payment_request, - "lnurlw": link.id, - }, - timeout=40, - ) - except Exception as exc: - # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid - logger.error("Caught exception when dispatching webhook url:", exc) - return {"status": "OK"} except Exception as e: From 0bede387f5ed5620781c50b689fc7016ad5d15af Mon Sep 17 00:00:00 2001 From: Gene Takavic <80261724+iWarpBTC@users.noreply.github.com> Date: Sun, 25 Sep 2022 18:11:25 +0200 Subject: [PATCH 008/614] webhook to pay_invoice/fix --- lnbits/core/services.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index aeb4f9382..4f937ec3a 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -232,6 +232,7 @@ async def pay_invoice( f"didn't receive checking_id from backend, payment may be stuck in database: {temp_id}" ) + additionals = None if type(webhook) is str: webhook_url = webhook elif type(webhook) is tuple: From 5c6cd70d3bf08720a094c4aac263f53c583664e9 Mon Sep 17 00:00:00 2001 From: Gene Takavic Date: Mon, 26 Sep 2022 11:47:00 +0200 Subject: [PATCH 009/614] disabling card with just otp which is also part of notify --- lnbits/extensions/boltcards/crud.py | 10 ++++++++-- lnbits/extensions/boltcards/lnurl.py | 3 +++ lnbits/extensions/boltcards/views_api.py | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/boltcards/crud.py b/lnbits/extensions/boltcards/crud.py index 39ee3f40d..0724ea046 100644 --- a/lnbits/extensions/boltcards/crud.py +++ b/lnbits/extensions/boltcards/crud.py @@ -114,8 +114,14 @@ async def get_card_by_external_id(external_id: str) -> Optional[Card]: return Card.parse_obj(card) -async def get_card_by_otp(otp: str) -> Optional[Card]: - row = await db.fetchone("SELECT * FROM boltcards.cards WHERE otp = ?", (otp,)) +async def get_card_by_otp(otp: str, half: bool = False) -> Optional[Card]: + if half and len(otp) == 16: + otp = "%" + otp + row = await db.fetchone( + "SELECT * FROM boltcards.cards WHERE otp LIKE ?", (otp,) + ) + else: + row = await db.fetchone("SELECT * FROM boltcards.cards WHERE otp = ?", (otp,)) if not row: return None diff --git a/lnbits/extensions/boltcards/lnurl.py b/lnbits/extensions/boltcards/lnurl.py index 320b16661..064bde2c6 100644 --- a/lnbits/extensions/boltcards/lnurl.py +++ b/lnbits/extensions/boltcards/lnurl.py @@ -119,6 +119,9 @@ async def lnurl_callback( "notification": "card_payment", "card_external_id": card.external_id, "card_name": card.card_name, + "card_otp": card.otp[ + -16: + ], # actually only half of the OTP is sent (full otp reveals the keys) }, ) if card.webhook_url diff --git a/lnbits/extensions/boltcards/views_api.py b/lnbits/extensions/boltcards/views_api.py index 2fc11dbc8..d7f5cf7c0 100644 --- a/lnbits/extensions/boltcards/views_api.py +++ b/lnbits/extensions/boltcards/views_api.py @@ -15,11 +15,13 @@ from .crud import ( delete_card, enable_disable_card, get_card, + get_card_by_otp, get_card_by_uid, get_cards, get_hits, get_refunds, update_card, + update_card_otp, ) from .models import CreateCardData @@ -111,6 +113,22 @@ async def enable_card( return card.dict() +@boltcards_ext.post("/api/v1/disablecard") +async def disble_card_with_otp(a): + if len(a) < 16: + raise HTTPException(detail="Invalid OTP.", status_code=HTTPStatus.BAD_REQUEST) + card = await get_card_by_otp(a, half=True) + if not card: + raise HTTPException(detail="No card found.", status_code=HTTPStatus.NOT_FOUND) + + new_otp = secrets.token_hex(16) + await update_card_otp(new_otp, card.id) + + card = await enable_disable_card(enable=False, id=card.id) + + return {"status": "OK"} + + @boltcards_ext.delete("/api/v1/cards/{card_id}") async def api_card_delete(card_id, wallet: WalletTypeInfo = Depends(require_admin_key)): card = await get_card(card_id) From 166530eb0c985575a140124fb4c9e5a23ee9e5a7 Mon Sep 17 00:00:00 2001 From: benarc Date: Mon, 7 Mar 2022 05:03:32 +0000 Subject: [PATCH 010/614] Added old admin extension --- .env.example | 12 +- lnbits/extensions/admin/README.md | 11 + lnbits/extensions/admin/__init__.py | 10 + lnbits/extensions/admin/config.json | 6 + lnbits/extensions/admin/crud.py | 59 ++ lnbits/extensions/admin/migrations.py | 256 ++++++++ lnbits/extensions/admin/models.py | 38 ++ .../admin/templates/admin/index.html | 565 ++++++++++++++++++ lnbits/extensions/admin/views.py | 20 + lnbits/extensions/admin/views_api.py | 41 ++ 10 files changed, 1013 insertions(+), 5 deletions(-) create mode 100644 lnbits/extensions/admin/README.md create mode 100644 lnbits/extensions/admin/__init__.py create mode 100644 lnbits/extensions/admin/config.json create mode 100644 lnbits/extensions/admin/crud.py create mode 100644 lnbits/extensions/admin/migrations.py create mode 100644 lnbits/extensions/admin/models.py create mode 100644 lnbits/extensions/admin/templates/admin/index.html create mode 100644 lnbits/extensions/admin/views.py create mode 100644 lnbits/extensions/admin/views_api.py diff --git a/.env.example b/.env.example index 93b823250..68e25ad15 100644 --- a/.env.example +++ b/.env.example @@ -3,10 +3,12 @@ PORT=5000 DEBUG=false -LNBITS_ALLOWED_USERS="" -LNBITS_ADMIN_USERS="" -# Extensions only admin can access -LNBITS_ADMIN_EXTENSIONS="ngrok" +LNBITS_ADMIN_USERS="" # User IDs seperated by comma +LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access +LNBITS_ADMIN_UI=false # Extensions only admin can access + +LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma + LNBITS_DEFAULT_WALLET_NAME="LNbits wallet" # csv ad image filepaths or urls, extensions can choose to honor @@ -91,4 +93,4 @@ LNBITS_DENOMINATION=sats # EclairWallet ECLAIR_URL=http://127.0.0.1:8283 -ECLAIR_PASS=eclairpw \ No newline at end of file +ECLAIR_PASS=eclairpw diff --git a/lnbits/extensions/admin/README.md b/lnbits/extensions/admin/README.md new file mode 100644 index 000000000..277294592 --- /dev/null +++ b/lnbits/extensions/admin/README.md @@ -0,0 +1,11 @@ +

Example Extension

+

*tagline*

+This is an example extension to help you organise and build you own. + +Try to include an image + + + +

If your extension has API endpoints, include useful ones here

+ +curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY" diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py new file mode 100644 index 000000000..d5f26c90d --- /dev/null +++ b/lnbits/extensions/admin/__init__.py @@ -0,0 +1,10 @@ +from quart import Blueprint +from lnbits.db import Database + +db = Database("ext_admin") + +admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates") + + +from .views_api import * # noqa +from .views import * # noqa diff --git a/lnbits/extensions/admin/config.json b/lnbits/extensions/admin/config.json new file mode 100644 index 000000000..696617335 --- /dev/null +++ b/lnbits/extensions/admin/config.json @@ -0,0 +1,6 @@ +{ + "name": "Admin", + "short_description": "Manage your LNbits install", + "icon": "build", + "contributors": ["benarc"] +} diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py new file mode 100644 index 000000000..cb8f9b5be --- /dev/null +++ b/lnbits/extensions/admin/crud.py @@ -0,0 +1,59 @@ +from typing import List, Optional + +from . import db +from .models import Admin, Funding +from lnbits.settings import * +from lnbits.helpers import urlsafe_short_hash +from lnbits.core.crud import create_payment +from lnbits.db import Connection + + +def update_wallet_balance(wallet_id: str, amount: int) -> str: + temp_id = f"temp_{urlsafe_short_hash()}" + internal_id = f"internal_{urlsafe_short_hash()}" + create_payment( + wallet_id=wallet_id, + checking_id=internal_id, + payment_request="admin_internal", + payment_hash="admin_internal", + amount=amount * 1000, + memo="Admin top up", + pending=False, + ) + return "success" + + +async def update_admin( +) -> Optional[Admin]: + if not CLightningWallet: + print("poo") + await db.execute( + """ + UPDATE admin + SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ? + WHERE 1 + """, + ( + + ), + ) + row = await db.fetchone("SELECT * FROM admin WHERE 1") + return Admin.from_row(row) if row else None + +async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]: + q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) + await db.execute( + f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id) + ) + row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,)) + return Jukebox(**row) if row else None + +async def get_admin() -> List[Admin]: + row = await db.fetchone("SELECT * FROM admin WHERE 1") + return Admin.from_row(row) if row else None + + +async def get_funding() -> List[Funding]: + rows = await db.fetchall("SELECT * FROM funding") + + return [Funding.from_row(row) for row in rows] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py new file mode 100644 index 000000000..82d934cb6 --- /dev/null +++ b/lnbits/extensions/admin/migrations.py @@ -0,0 +1,256 @@ +from sqlalchemy.exc import OperationalError # type: ignore +from os import getenv +from lnbits.helpers import urlsafe_short_hash + + +async def m001_create_admin_table(db): + user = None + site_title = None + site_tagline = None + site_description = None + allowed_users = None + admin_user = None + default_wallet_name = None + data_folder = None + disabled_ext = None + force_https = True + service_fee = 0 + funding_source = "" + + if getenv("LNBITS_SITE_TITLE"): + site_title = getenv("LNBITS_SITE_TITLE") + + if getenv("LNBITS_SITE_TAGLINE"): + site_tagline = getenv("LNBITS_SITE_TAGLINE") + + if getenv("LNBITS_SITE_DESCRIPTION"): + site_description = getenv("LNBITS_SITE_DESCRIPTION") + + if getenv("LNBITS_ALLOWED_USERS"): + allowed_users = getenv("LNBITS_ALLOWED_USERS") + + if getenv("LNBITS_ADMIN_USER"): + admin_user = getenv("LNBITS_ADMIN_USER") + + if getenv("LNBITS_DEFAULT_WALLET_NAME"): + default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") + + if getenv("LNBITS_DATA_FOLDER"): + data_folder = getenv("LNBITS_DATA_FOLDER") + + if getenv("LNBITS_DISABLED_EXTENSIONS"): + disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS") + + if getenv("LNBITS_FORCE_HTTPS"): + force_https = getenv("LNBITS_FORCE_HTTPS") + + if getenv("LNBITS_SERVICE_FEE"): + service_fee = getenv("LNBITS_SERVICE_FEE") + + if getenv("LNBITS_BACKEND_WALLET_CLASS"): + funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS") + + await db.execute( + """ + CREATE TABLE IF NOT EXISTS admin ( + user TEXT, + site_title TEXT, + site_tagline TEXT, + site_description TEXT, + admin_user TEXT, + allowed_users TEXT, + default_wallet_name TEXT, + data_folder TEXT, + disabled_ext TEXT, + force_https BOOLEAN, + service_fee INT, + funding_source TEXT + ); + """ + ) + await db.execute( + """ + INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + user, + site_title, + site_tagline, + site_description, + admin_user, + allowed_users, + default_wallet_name, + data_folder, + disabled_ext, + force_https, + service_fee, + funding_source, + ), + ) + + +async def m001_create_funding_table(db): + + funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS") + + # Make the funding table, if it does not already exist + await db.execute( + """ + CREATE TABLE IF NOT EXISTS funding ( + id TEXT PRIMARY KEY, + backend_wallet TEXT, + endpoint TEXT, + port INT, + read_key TEXT, + invoice_key TEXT, + admin_key TEXT, + cert TEXT, + balance INT, + selected INT + ); + """ + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, selected) + VALUES (?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "CLightningWallet", + getenv("CLIGHTNING_RPC"), + 1 if funding_wallet == "CLightningWallet" else 0, + ), + ) + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "SparkWallet", + getenv("SPARK_URL"), + getenv("SPARK_TOKEN"), + 1 if funding_wallet == "SparkWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LnbitsWallet", + getenv("LNBITS_ENDPOINT"), + getenv("LNBITS_KEY"), + 1 if funding_wallet == "LnbitsWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, port, admin_key, cert, selected) + VALUES (?, ?, ?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LndWallet", + getenv("LND_GRPC_ENDPOINT"), + getenv("LND_GRPC_PORT"), + getenv("LND_GRPC_MACAROON"), + getenv("LND_GRPC_CERT"), + 1 if funding_wallet == "LndWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected) + VALUES (?, ?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LndRestWallet", + getenv("LND_REST_ENDPOINT"), + getenv("LND_REST_MACAROON"), + getenv("LND_REST_CERT"), + 1 if funding_wallet == "LndWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected) + VALUES (?, ?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LNPayWallet", + getenv("LNPAY_API_ENDPOINT"), + getenv("LNPAY_WALLET_KEY"), + getenv("LNPAY_API_KEY"), # this is going in as the cert + 1 if funding_wallet == "LNPayWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LntxbotWallet", + getenv("LNTXBOT_API_ENDPOINT"), + getenv("LNTXBOT_KEY"), + 1 if funding_wallet == "LntxbotWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "OpenNodeWallet", + getenv("OPENNODE_API_ENDPOINT"), + getenv("OPENNODE_KEY"), + 1 if funding_wallet == "OpenNodeWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "SparkWallet", + getenv("SPARK_URL"), + getenv("SPARK_TOKEN"), + 1 if funding_wallet == "SparkWallet" else 0, + ), + ) + + ## PLACEHOLDER FOR ECLAIR WALLET + # await db.execute( + # """ + # INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + # VALUES (?, ?, ?, ?, ?) + # """, + # ( + # urlsafe_short_hash(), + # "EclairWallet", + # getenv("ECLAIR_URL"), + # getenv("ECLAIR_PASS"), + # 1 if funding_wallet == "EclairWallet" else 0, + # ), + # ) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py new file mode 100644 index 000000000..c38f17f48 --- /dev/null +++ b/lnbits/extensions/admin/models.py @@ -0,0 +1,38 @@ +from typing import NamedTuple +from sqlite3 import Row + +class Admin(NamedTuple): + user: str + site_title: str + site_tagline: str + site_description:str + allowed_users: str + admin_user: str + default_wallet_name: str + data_folder: str + disabled_ext: str + force_https: str + service_fee: str + funding_source: str + + @classmethod + def from_row(cls, row: Row) -> "Admin": + data = dict(row) + return cls(**data) + +class Funding(NamedTuple): + id: str + backend_wallet: str + endpoint: str + port: str + read_key: str + invoice_key: str + admin_key: str + cert: str + balance: int + selected: int + + @classmethod + def from_row(cls, row: Row) -> "Funding": + data = dict(row) + return cls(**data) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html new file mode 100644 index 000000000..87cf09efa --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -0,0 +1,565 @@ +{% extends "base.html" %} {% from "macros.jinja" import window_vars with context +%} {% block page %} + +

Admin

+

+ +
+
+ + +
Settings
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+

+
+
+ +
+
+
+
+ +
+ + +
Wallet topup
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+ +{% endblock %} {% block scripts %} {{ window_vars(user) }} + +{% endblock %} diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py new file mode 100644 index 000000000..5e17919c5 --- /dev/null +++ b/lnbits/extensions/admin/views.py @@ -0,0 +1,20 @@ +from quart import g, render_template, request, jsonify +import json + +from lnbits.decorators import check_user_exists, validate_uuids +from lnbits.extensions.admin import admin_ext +from lnbits.core.crud import get_user, create_account +from .crud import get_admin, get_funding +from lnbits.settings import WALLET + + +@admin_ext.route("/") +@validate_uuids(["usr"], required=True) +@check_user_exists() +async def index(): + user_id = g.user + admin = await get_admin() + + funding = [{**funding._asdict()} for funding in await get_funding()] + + return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py new file mode 100644 index 000000000..2a61b6f55 --- /dev/null +++ b/lnbits/extensions/admin/views_api.py @@ -0,0 +1,41 @@ +from quart import jsonify, g, request +from http import HTTPStatus +from .crud import update_wallet_balance +from lnbits.extensions.admin import admin_ext +from lnbits.decorators import api_check_wallet_key, api_validate_post_request +from lnbits.core.crud import get_wallet +from .crud import get_admin,update_admin +import json + +@admin_ext.route("/api/v1/admin//", methods=["GET"]) +@api_check_wallet_key("admin") +async def api_update_balance(wallet_id, topup_amount): + print(g.data.wallet) + try: + wallet = await get_wallet(wallet_id) + except: + return ( + jsonify({"error": "Not allowed: not an admin"}), + HTTPStatus.FORBIDDEN, + ) + print(wallet) + print(topup_amount) + return jsonify({"status": "Success"}), HTTPStatus.OK + + +@admin_ext.route("/api/v1/admin/", methods=["POST"]) +@api_check_wallet_key("admin") +@api_validate_post_request(schema={}) +async def api_update_admin(): + body = await request.get_json() + admin = await get_admin() + print(g.wallet[2]) + print(body["admin_user"]) + if not admin.admin_user == g.wallet[2] and admin.admin_user != None: + return ( + jsonify({"error": "Not allowed: not an admin"}), + HTTPStatus.FORBIDDEN, + ) + updated = await update_admin(body) + print(updated) + return jsonify({"status": "Success"}), HTTPStatus.OK \ No newline at end of file From 68eee00b45170db4e35fa6fdafba85373958e9fa Mon Sep 17 00:00:00 2001 From: benarc Date: Mon, 7 Mar 2022 05:11:55 +0000 Subject: [PATCH 011/614] old admin setup UI --- lnbits/core/templates/core/admin.html | 717 ++++++++++++++++++++++++++ 1 file changed, 717 insertions(+) create mode 100644 lnbits/core/templates/core/admin.html diff --git a/lnbits/core/templates/core/admin.html b/lnbits/core/templates/core/admin.html new file mode 100644 index 000000000..e81765550 --- /dev/null +++ b/lnbits/core/templates/core/admin.html @@ -0,0 +1,717 @@ +{% extends "public.html" %} {% from "macros.jinja" import window_vars with +context %} {% block page %} +
+
+ + +

+
Welcome to LNbits
+

+
+ Fill in the information below to setup your LNbits instance. Details + can be changed later. +
+

+ + +
+ +
Branding
+
+
+ +
+
+ +
+
+
+
+ + + +
+
+ + + +
+
+ +
Service settings
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ Funding source information (at least one required)
*if installed through RaspiBlitz, MyNode, etc, details + should be filled in for you
+
+ + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+ View project in GitHub + Donate +
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(funding) }} + +{% endblock %} From 65e1f19ed1124340d2a9ba97b4420c099896488c Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:18:09 +0000 Subject: [PATCH 012/614] convert to FastAPI --- lnbits/extensions/admin/__init__.py | 11 +++- lnbits/extensions/admin/crud.py | 50 +++++++-------- lnbits/extensions/admin/migrations.py | 23 ++++--- lnbits/extensions/admin/models.py | 51 ++++++++++------ .../admin/templates/admin/index.html | 23 +++++-- lnbits/extensions/admin/views.py | 39 ++++++++---- lnbits/extensions/admin/views_api.py | 61 ++++++++++--------- 7 files changed, 151 insertions(+), 107 deletions(-) diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py index d5f26c90d..6a56b2bb1 100644 --- a/lnbits/extensions/admin/__init__.py +++ b/lnbits/extensions/admin/__init__.py @@ -1,10 +1,15 @@ -from quart import Blueprint +from fastapi import APIRouter + from lnbits.db import Database +from lnbits.helpers import template_renderer db = Database("ext_admin") -admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates") +admin_ext: APIRouter = APIRouter(prefix="/admin", tags=["admin"]) + +def admin_renderer(): + return template_renderer(["lnbits/extensions/admin/templates"]) -from .views_api import * # noqa from .views import * # noqa +from .views_api import * # noqa diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index cb8f9b5be..872d6c97b 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -1,11 +1,11 @@ from typing import List, Optional +from lnbits.core.crud import create_payment +from lnbits.helpers import urlsafe_short_hash +from lnbits.settings import * + from . import db from .models import Admin, Funding -from lnbits.settings import * -from lnbits.helpers import urlsafe_short_hash -from lnbits.core.crud import create_payment -from lnbits.db import Connection def update_wallet_balance(wallet_id: str, amount: int) -> str: @@ -22,38 +22,30 @@ def update_wallet_balance(wallet_id: str, amount: int) -> str: ) return "success" - -async def update_admin( -) -> Optional[Admin]: - if not CLightningWallet: - print("poo") - await db.execute( - """ - UPDATE admin - SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ? - WHERE 1 - """, - ( - - ), - ) - row = await db.fetchone("SELECT * FROM admin WHERE 1") - return Admin.from_row(row) if row else None - -async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]: +async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) + print("UPDATE", q) await db.execute( - f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id) + f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) ) - row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,)) - return Jukebox(**row) if row else None + row = await db.fetchone('SELECT * FROM admin WHERE "user" = ?', (user,)) + assert row, "Newly updated settings couldn't be retrieved" + return Admin(**row) if row else None + +# async def update_admin(user: str, **kwargs) -> Optional[Admin]: +# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) +# await db.execute( +# f"UPDATE admin SET {q} WHERE user = ?", (*kwargs.values(), user) +# ) +# new_settings = await get_admin() +# return new_settings async def get_admin() -> List[Admin]: - row = await db.fetchone("SELECT * FROM admin WHERE 1") - return Admin.from_row(row) if row else None + row = await db.fetchone("SELECT * FROM admin") + return Admin(**row) if row else None async def get_funding() -> List[Funding]: rows = await db.fetchall("SELECT * FROM funding") - return [Funding.from_row(row) for row in rows] + return [Funding(**row) for row in rows] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 82d934cb6..13b769232 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -1,5 +1,7 @@ -from sqlalchemy.exc import OperationalError # type: ignore from os import getenv + +from sqlalchemy.exc import OperationalError # type: ignore + from lnbits.helpers import urlsafe_short_hash @@ -9,7 +11,7 @@ async def m001_create_admin_table(db): site_tagline = None site_description = None allowed_users = None - admin_user = None + admin_users = None default_wallet_name = None data_folder = None disabled_ext = None @@ -29,8 +31,9 @@ async def m001_create_admin_table(db): if getenv("LNBITS_ALLOWED_USERS"): allowed_users = getenv("LNBITS_ALLOWED_USERS") - if getenv("LNBITS_ADMIN_USER"): - admin_user = getenv("LNBITS_ADMIN_USER") + if getenv("LNBITS_ADMIN_USERS"): + admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split()) + user = admin_users.split(',')[0] if getenv("LNBITS_DEFAULT_WALLET_NAME"): default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") @@ -53,32 +56,32 @@ async def m001_create_admin_table(db): await db.execute( """ CREATE TABLE IF NOT EXISTS admin ( - user TEXT, + "user" TEXT, site_title TEXT, site_tagline TEXT, site_description TEXT, - admin_user TEXT, + admin_users TEXT, allowed_users TEXT, default_wallet_name TEXT, data_folder TEXT, disabled_ext TEXT, force_https BOOLEAN, - service_fee INT, + service_fee REAL, funding_source TEXT ); """ ) await db.execute( """ - INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) + INSERT INTO admin ("user", site_title, site_tagline, site_description, admin_users, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( - user, + user.strip(), site_title, site_tagline, site_description, - admin_user, + admin_users[1:], allowed_users, default_wallet_name, data_folder, diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index c38f17f48..4080ff018 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -1,18 +1,35 @@ -from typing import NamedTuple from sqlite3 import Row +from typing import List, Optional -class Admin(NamedTuple): +from fastapi import Query +from pydantic import BaseModel + + +class UpdateAdminSettings(BaseModel): + site_title: Optional[str] + site_tagline: Optional[str] + site_description: Optional[str] + allowed_users: Optional[str] + admin_users: Optional[str] + default_wallet_name: Optional[str] + data_folder: Optional[str] + disabled_ext: Optional[str] + force_https: Optional[bool] + service_fee: Optional[float] + funding_source: Optional[str] + +class Admin(BaseModel): user: str - site_title: str - site_tagline: str - site_description:str - allowed_users: str - admin_user: str + site_title: Optional[str] + site_tagline: Optional[str] + site_description: Optional[str] + allowed_users: Optional[str] + admin_users: str default_wallet_name: str data_folder: str disabled_ext: str - force_https: str - service_fee: str + force_https: Optional[bool] = Query(True) + service_fee: float funding_source: str @classmethod @@ -20,16 +37,16 @@ class Admin(NamedTuple): data = dict(row) return cls(**data) -class Funding(NamedTuple): +class Funding(BaseModel): id: str backend_wallet: str - endpoint: str - port: str - read_key: str - invoice_key: str - admin_key: str - cert: str - balance: int + endpoint: str = Query(None) + port: str = Query(None) + read_key: str = Query(None) + invoice_key: str = Query(None) + admin_key: str = Query(None) + cert: str = Query(None) + balance: int = Query(None) selected: int @classmethod diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 87cf09efa..a6b456259 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -87,7 +87,7 @@ @@ -442,13 +442,14 @@ site_title: '{{admin.site_title}}', tagline: '{{admin.site_tagline}}', description: '{{admin.site_description}}', - admin_user: '{{admin.admin_user}}', - service_fee: parseInt('{{admin.service_fee}}'), + admin_users: '{{admin.admin_users}}', + service_fee: parseFloat('{{admin.service_fee}}'), default_wallet_name: '{{admin.default_wallet_name}}', data_folder: '{{admin.data_folder}}', funding_source_primary: '{{admin.funding_source}}', disabled_ext: '{{admin.disabled_ext}}'.split(','), edited: [], + funding: {}, senddata: {} } }, @@ -528,15 +529,27 @@ }, UpdateLNbits: function () { var self = this - console.log(self.data.admin) + let {site_title, admin_users, default_wallet_name, data_folder, disabled_ext, service_fee, funding_source_primary} = this.data.admin + let data = { + site_title, + site_tagline: this.data.admin.tagline, + site_description: this.data.admin.description, + admin_users: admin_users.toString(), + default_wallet_name, + data_folder, + disabled_ext: disabled_ext.toString(), + service_fee, + funding_source: funding_source_primary} + console.log(data) LNbits.api .request( 'POST', '/admin/api/v1/admin/', self.g.user.wallets[0].adminkey, - self.data.admin + data ) .then(function (response) { + console.log(response.data) self.$q.notify({ type: 'positive', message: diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py index 5e17919c5..00a0c99fc 100644 --- a/lnbits/extensions/admin/views.py +++ b/lnbits/extensions/admin/views.py @@ -1,20 +1,33 @@ -from quart import g, render_template, request, jsonify -import json +from email.policy import default +from os import getenv -from lnbits.decorators import check_user_exists, validate_uuids +from fastapi import Request +from fastapi.params import Depends +from fastapi.templating import Jinja2Templates +from starlette.responses import HTMLResponse + +from lnbits.core.models import User +from lnbits.decorators import check_user_exists from lnbits.extensions.admin import admin_ext -from lnbits.core.crud import get_user, create_account +from lnbits.requestvars import g + +from . import admin_ext, admin_renderer from .crud import get_admin, get_funding -from lnbits.settings import WALLET +templates = Jinja2Templates(directory="templates") -@admin_ext.route("/") -@validate_uuids(["usr"], required=True) -@check_user_exists() -async def index(): - user_id = g.user +@admin_ext.get("/", response_class=HTMLResponse) +async def index(request: Request, user: User = Depends(check_user_exists)): admin = await get_admin() + print(g()) + funding = [f.dict() for f in await get_funding()] - funding = [{**funding._asdict()} for funding in await get_funding()] - - return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding) + print("ADMIN", admin.dict()) + return admin_renderer().TemplateResponse( + "admin/index.html", { + "request": request, + "user": user.dict(), + "admin": admin.dict(), + "funding": funding + } + ) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 2a61b6f55..b2c65be25 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -1,41 +1,42 @@ -from quart import jsonify, g, request from http import HTTPStatus -from .crud import update_wallet_balance -from lnbits.extensions.admin import admin_ext -from lnbits.decorators import api_check_wallet_key, api_validate_post_request -from lnbits.core.crud import get_wallet -from .crud import get_admin,update_admin -import json -@admin_ext.route("/api/v1/admin//", methods=["GET"]) -@api_check_wallet_key("admin") -async def api_update_balance(wallet_id, topup_amount): - print(g.data.wallet) +from fastapi import Body, Depends, Request +from starlette.exceptions import HTTPException + +from lnbits.core.crud import get_wallet +from lnbits.decorators import WalletTypeInfo, require_admin_key +from lnbits.extensions.admin import admin_ext +from lnbits.extensions.admin.models import Admin, UpdateAdminSettings + +from .crud import get_admin, update_admin, update_wallet_balance + + +@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK) +async def api_update_balance(wallet_id, topup_amount, g: WalletTypeInfo = Depends(require_admin_key)): + print(g.wallet) try: wallet = await get_wallet(wallet_id) except: - return ( - jsonify({"error": "Not allowed: not an admin"}), - HTTPStatus.FORBIDDEN, - ) + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) print(wallet) print(topup_amount) - return jsonify({"status": "Success"}), HTTPStatus.OK + return {"status": "Success"} -@admin_ext.route("/api/v1/admin/", methods=["POST"]) -@api_check_wallet_key("admin") -@api_validate_post_request(schema={}) -async def api_update_admin(): - body = await request.get_json() +@admin_ext.post("/api/v1/admin/", status_code=HTTPStatus.OK) +async def api_update_admin( + request: Request, + data: UpdateAdminSettings = Body(...), + g: WalletTypeInfo = Depends(require_admin_key) + ): admin = await get_admin() - print(g.wallet[2]) - print(body["admin_user"]) - if not admin.admin_user == g.wallet[2] and admin.admin_user != None: - return ( - jsonify({"error": "Not allowed: not an admin"}), - HTTPStatus.FORBIDDEN, - ) - updated = await update_admin(body) + print(data) + if not admin.user == g.wallet.user: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) + updated = await update_admin(user=g.wallet.user, **data.dict()) print(updated) - return jsonify({"status": "Success"}), HTTPStatus.OK \ No newline at end of file + return {"status": "Success"} From 23d770a07489c3d74cc0c4c4d3b4a3b4b00fc393 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:18:58 +0000 Subject: [PATCH 013/614] remove core admin html (renamed for now) --- lnbits/core/templates/core/core_admin.html | 717 +++++++++++++++++++++ 1 file changed, 717 insertions(+) create mode 100644 lnbits/core/templates/core/core_admin.html diff --git a/lnbits/core/templates/core/core_admin.html b/lnbits/core/templates/core/core_admin.html new file mode 100644 index 000000000..835fc00a3 --- /dev/null +++ b/lnbits/core/templates/core/core_admin.html @@ -0,0 +1,717 @@ +{% extends "public.html" %} {% from "macros.jinja" import window_vars with +context %} {% block page %} +
+
+ + +

+
Welcome to LNbits
+

+
+ Fill in the information below to setup your LNbits instance. Details + can be changed later. +
+

+ + +
+ +
Branding
+
+
+ +
+
+ +
+
+
+
+ + + +
+
+ + + +
+
+ +
Service settings
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ Funding source information (at least one required)
*if installed through RaspiBlitz, MyNode, etc, details + should be filled in for you
+
+ + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+ View project in GitHub + Donate +
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(funding) }} + +{% endblock %} From 165ab6d0b50b49cad69b46100bce0f87fd6f7257 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:21:38 +0000 Subject: [PATCH 014/614] typo --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 68e25ad15..bd1894843 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,7 @@ DEBUG=false LNBITS_ADMIN_USERS="" # User IDs seperated by comma LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access -LNBITS_ADMIN_UI=false # Extensions only admin can access +LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma From 844e11edeb441fd2e7c7b40d11c25cc4019471bf Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:22:23 +0000 Subject: [PATCH 015/614] add admin_ui env --- lnbits/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lnbits/settings.py b/lnbits/settings.py index 3f4e31cc5..43cb87cbe 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,5 +1,6 @@ import importlib import subprocess +from email.policy import default from os import path from typing import List @@ -27,6 +28,7 @@ LNBITS_DATABASE_URL = env.str("LNBITS_DATABASE_URL", default=None) LNBITS_ALLOWED_USERS: List[str] = [ x.strip(" ") for x in env.list("LNBITS_ALLOWED_USERS", default=[], subcast=str) ] +LNBITS_ADMIN_UI = env.bool("LNBITS_ADMIN_UI", default=False) LNBITS_ADMIN_USERS: List[str] = [ x.strip(" ") for x in env.list("LNBITS_ADMIN_USERS", default=[], subcast=str) ] From 4336613028e05dae5b6adf3b543903f6fc365a44 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:23:16 +0000 Subject: [PATCH 016/614] add db config at startup --- lnbits/commands.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lnbits/commands.py b/lnbits/commands.py index 0f7454f23..8c39c338f 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -52,6 +52,25 @@ def bundle_vendored(): with open(outputpath, "w") as f: f.write(output) +async def get_admin_settings(): + from lnbits.extensions.admin.models import Admin + + async with core_db.connect() as conn: + + if conn.type == SQLITE: + exists = await conn.fetchone( + "SELECT * FROM sqlite_master WHERE type='table' AND name='admin'" + ) + elif conn.type in {POSTGRES, COCKROACH}: + exists = await conn.fetchone( + "SELECT * FROM information_schema.tables WHERE table_name = 'admin'" + ) + if not exists: + return False + + row = await conn.fetchone("SELECT * from admin") + + return Admin(**row) if row else None async def migrate_databases(): """Creates the necessary databases if they don't exist already; or migrates them.""" From 32a6a6ae2fb9fa3ba01c24f31067a5115feca4e3 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:23:53 +0000 Subject: [PATCH 017/614] get admin settings at startup --- lnbits/app.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lnbits/app.py b/lnbits/app.py index 514825380..6a66b99e8 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -18,6 +18,7 @@ from loguru import logger import lnbits.settings from lnbits.core.tasks import register_task_listeners +from .commands import get_admin_settings from .core import core_app from .core.views.generic import core_html_routes from .helpers import ( @@ -42,6 +43,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI: """Create application factory. :param config_object: The configuration object to use. """ +<<<<<<< HEAD configure_logger() app = FastAPI( @@ -53,6 +55,14 @@ def create_app(config_object="lnbits.settings") -> FastAPI: }, ) app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static") +======= + app = FastAPI() + + if lnbits.settings.LNBITS_ADMIN_UI: + check_settings(app) + + app.mount("/static", StaticFiles(directory="lnbits/static"), name="static") +>>>>>>> e3a1b3ae (get admin settings at startup) app.mount( "/core/static", StaticFiles(packages=[("lnbits.core", "static")]), @@ -64,7 +74,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: app.add_middleware( CORSMiddleware, allow_origins=origins, allow_methods=["*"], allow_headers=["*"] ) - g().config = lnbits.settings g().base_url = f"http://{lnbits.settings.HOST}:{lnbits.settings.PORT}" @@ -102,6 +111,18 @@ def create_app(config_object="lnbits.settings") -> FastAPI: return app +def check_settings(app: FastAPI): + @app.on_event("startup") + async def check_settings_admin(): + while True: + admin_set = await get_admin_settings() + if admin_set : + break + print("ERROR:", admin_set) + await asyncio.sleep(5) + # admin_set = await get_admin_settings() + g().admin_conf = admin_set + def check_funding_source(app: FastAPI) -> None: @app.on_event("startup") async def check_wallet_status(): From f245f3188b7f35b6f9388e9caa12d3d6a0b20c3e Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:24:11 +0000 Subject: [PATCH 018/614] remove core admin.html --- lnbits/core/templates/core/admin.html | 717 -------------------------- 1 file changed, 717 deletions(-) delete mode 100644 lnbits/core/templates/core/admin.html diff --git a/lnbits/core/templates/core/admin.html b/lnbits/core/templates/core/admin.html deleted file mode 100644 index e81765550..000000000 --- a/lnbits/core/templates/core/admin.html +++ /dev/null @@ -1,717 +0,0 @@ -{% extends "public.html" %} {% from "macros.jinja" import window_vars with -context %} {% block page %} -
-
- - -

-
Welcome to LNbits
-

-
- Fill in the information below to setup your LNbits instance. Details - can be changed later. -
-

- - -
- -
Branding
-
-
- -
-
- -
-
-
-
- - - -
-
- - - -
-
- -
Service settings
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
- Funding source information (at least one required)
*if installed through RaspiBlitz, MyNode, etc, details - should be filled in for you
-
- - - - - - - - - - - - - -
-
- -
-
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
-
-
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
- -
-
-
-
-
-
- -
- - -
-
-
-
- View project in GitHub - Donate -
-
-
-
-
-{% endblock %} {% block scripts %} {{ window_vars(funding) }} - -{% endblock %} From de21f0216161ac9f45cf17f42f5be708404156d0 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Fri, 18 Mar 2022 16:55:31 +0000 Subject: [PATCH 019/614] refactor ui --- .../admin/templates/admin/index.html | 727 +++++++++++++++++- 1 file changed, 712 insertions(+), 15 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index a6b456259..65ac9f336 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1,6 +1,670 @@ {% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block page %} +
+
+ +
+
+ + + + + + +
+
+ + + + +
Wallets Management
+
+
+
+
+

Funding Source Info

+
    + {%raw%} +
  • Funding Source: {{data.admin.funding_source}}
  • +
  • Balance: {{data.admin.balance / 1000}} sats
  • + {%endraw%} +
+
+
+
+
+
+

Active Funding

+ +
+
+
+ +

TopUp a wallet

+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+

Funding Sources

+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+
+
+ +
+ Save +
+
+
+ + +
User Management
+
+

+ Super Admin: {% raw + %}{{this.data.admin.user}}{% endraw %} +

+
+
+

Admin Users

+ + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+

Allowed Users

+ + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+
+

Admin Extensions

+ +
+
+
+

Disabled Extensions

+ +
+
+
+
+ Save +
+
+
+ + +
Server Management
+
+
+
+
+

Server Info

+
    + {%raw%} +
  • SQlite: {{data.admin.data_folder}}
  • +
  • Postgres: {{data.admin.database_url}}
  • + {%endraw%} +
+
+
+
+
+
+

Service Fee

+ +
+
+
+

Miscelaneous

+ + + Force HTTPS + Prefer secure URLs + + + + + + + + Hide API + Hides wallet api, extensions can choose to honor + + + + + +
+
+
+
+ +
+ Save +
+
+
+ + +
UI Management
+
+
+
+
+

Site Title

+ +
+
+
+

Site Tagline

+ +
+
+
+
+

Site Description

+ +
+
+
+
+

Default Wallet Name

+ +
+
+
+

Denomination

+ +
+
+
+
+
+

Themes

+ +
+
+
+

Advertisement Slots

+ + + +
+ {% raw %} + + {{ space.slice(0, 8) + " ... " + space.slice(-8) }} + + {% endraw %} +
+
+
+
+
+ +
+ Save +
+
+
+
+
+
+
+
+

Admin

-
+
@@ -426,6 +1090,7 @@ return { wallet: {data: {}}, cancel: {}, + tab: 'funding', data: { funding_source: [ 'CLightningWallet', @@ -436,24 +1101,14 @@ 'LnbitsWallet', 'OpenNodeWallet' ], - + admin: { - user: '{{ user.id }}', - site_title: '{{admin.site_title}}', - tagline: '{{admin.site_tagline}}', - description: '{{admin.site_description}}', - admin_users: '{{admin.admin_users}}', - service_fee: parseFloat('{{admin.service_fee}}'), - default_wallet_name: '{{admin.default_wallet_name}}', - data_folder: '{{admin.data_folder}}', - funding_source_primary: '{{admin.funding_source}}', - disabled_ext: '{{admin.disabled_ext}}'.split(','), edited: [], funding: {}, senddata: {} } }, - + themes: ['classic', 'bitcoin', 'flamingo', 'mint', 'autumn', 'monochrome', 'salvador'], options: [ 'bleskomat', 'captcha', @@ -489,9 +1144,51 @@ for (i = 0; i < funding.length; i++) { self.data.admin.funding[funding[i].backend_wallet] = funding[i] } - console.log(self.data.admin) + let settings = JSON.parse('{{ settings | tojson|safe }}') + settings.balance = '{{ balance }}' + this.data.admin = {...this.data.admin, ...settings} + console.log(this.g.user) }, methods: { + addAdminUser(){ + let addUser = this.data.admin_users_add + let admin_users = this.data.admin.admin_users + if(addUser.length && !admin_users.includes(addUser)){ + admin_users.push(addUser) + this.data.admin.admin_users = admin_users + this.data.admin_users_add = "" + } + }, + removeAdminUser(user){ + let admin_users = this.data.admin.admin_users + this.data.admin.admin_users = admin_users.filter(u => u !== user) + }, + addAllowedUser(){ + let addUser = this.data.allowed_users_add + let allowed_users = this.data.admin.allowed_users + if(addUser.length && !allowed_users.includes(addUser)){ + allowed_users.push(addUser) + this.data.admin.allowed_users = allowed_users + this.data.allowed_users_add = "" + } + }, + removeAllowedUser(user){ + let allowed_users = this.data.admin.allowed_users + this.data.admin.allowed_users = allowed_users.filter(u => u !== user) + }, + addAdSpace(){ + let adSpace = this.data.ad_space_add + let spaces = this.data.admin.ad_space + if(adSpace.length && !spaces.includes(adSpace)){ + spaces.push(adSpace) + this.data.admin.ad_space = spaces + this.data.ad_space_add = "" + } + }, + removeAdSpace(ad){ + let spaces = this.data.admin.ad_space + this.data.admin.ad_space = spaces.filter(s => s !== ad) + }, topupWallet: function () { var self = this LNbits.api From 582cc52ac61236ca7ae219b49e20d0954e983411 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Fri, 18 Mar 2022 16:59:06 +0000 Subject: [PATCH 020/614] make it work from g() --- lnbits/app.py | 34 +++--- lnbits/config.py | 62 ++++++++++ lnbits/extensions/admin/crud.py | 2 +- lnbits/extensions/admin/migrations.py | 162 +++++++++++++++++--------- lnbits/extensions/admin/models.py | 27 +++-- lnbits/extensions/admin/views.py | 8 +- lnbits/settings.py | 2 +- 7 files changed, 218 insertions(+), 79 deletions(-) create mode 100644 lnbits/config.py diff --git a/lnbits/app.py b/lnbits/app.py index 6a66b99e8..ccac1e002 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -19,6 +19,7 @@ import lnbits.settings from lnbits.core.tasks import register_task_listeners from .commands import get_admin_settings +from .config import WALLET, conf from .core import core_app from .core.views.generic import core_html_routes from .helpers import ( @@ -29,7 +30,8 @@ from .helpers import ( url_for_vendored, ) from .requestvars import g -from .settings import WALLET + +# from .settings import WALLET from .tasks import ( catch_everything_and_restart, check_pending_payments, @@ -43,7 +45,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: """Create application factory. :param config_object: The configuration object to use. """ -<<<<<<< HEAD configure_logger() app = FastAPI( @@ -55,20 +56,18 @@ def create_app(config_object="lnbits.settings") -> FastAPI: }, ) app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static") -======= - app = FastAPI() - - if lnbits.settings.LNBITS_ADMIN_UI: - check_settings(app) - - app.mount("/static", StaticFiles(directory="lnbits/static"), name="static") ->>>>>>> e3a1b3ae (get admin settings at startup) app.mount( "/core/static", StaticFiles(packages=[("lnbits.core", "static")]), name="core_static", ) + if lnbits.settings.LNBITS_ADMIN_UI: + g().admin_conf = conf + check_settings(app) + + g().WALLET = WALLET + origins = ["*"] app.add_middleware( @@ -110,18 +109,27 @@ def create_app(config_object="lnbits.settings") -> FastAPI: return app - def check_settings(app: FastAPI): @app.on_event("startup") async def check_settings_admin(): + + def removeEmptyString(arr): + return list(filter(None, arr)) + while True: admin_set = await get_admin_settings() if admin_set : break print("ERROR:", admin_set) await asyncio.sleep(5) - # admin_set = await get_admin_settings() - g().admin_conf = admin_set + + admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(',')) + admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(',')) + admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(',')) + admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(',')) + admin_set.theme = removeEmptyString(admin_set.theme.split(',')) + admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(',')) + g().admin_conf = conf.copy(update=admin_set.dict()) def check_funding_source(app: FastAPI) -> None: @app.on_event("startup") diff --git a/lnbits/config.py b/lnbits/config.py new file mode 100644 index 000000000..02e8cf537 --- /dev/null +++ b/lnbits/config.py @@ -0,0 +1,62 @@ +import importlib +import json +from os import getenv, path +from typing import List, Optional + +from pydantic import BaseSettings, Field, validator + +wallets_module = importlib.import_module("lnbits.wallets") +wallet_class = getattr( + wallets_module, getenv("LNBITS_BACKEND_WALLET_CLASS", "VoidWallet") +) + +WALLET = wallet_class() + +def list_parse_fallback(v): + try: + return json.loads(v) + except Exception as e: + return v.replace(' ','').split(',') + +class Settings(BaseSettings): + # users + admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS") + allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS") + admin_ext: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_EXTENSIONS") + disabled_ext: List[str] = Field(default_factory=list, env="LNBITS_DISABLED_EXTENSIONS") + funding_source: str = Field(default="VoidWallet", env="LNBITS_BACKEND_WALLET_CLASS") + # ops + data_folder: str = Field(default=None, env="LNBITS_DATA_FOLDER") + database_url: str = Field(default=None, env="LNBITS_DATABASE_URL") + force_https: bool = Field(default=True, env="LNBITS_FORCE_HTTPS") + service_fee: float = Field(default=0, env="LNBITS_SERVICE_FEE") + hide_api: bool = Field(default=False, env="LNBITS_HIDE_API") + denomination: str = Field(default="sats", env="LNBITS_DENOMINATION") + # Change theme + site_title: str = Field(default=None, env="LNBITS_SITE_TITLE") + site_tagline: str = Field(default=None, env="LNBITS_SITE_TAGLINE") + site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") + default_wallet_name: str = Field(default=None, env="LNBITS_DEFAULT_WALLET_NAME") + theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS") + ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") + # .env + env: Optional[str] + debug: Optional[str] + host: Optional[str] + port: Optional[str] + lnbits_path: Optional[str] = path.dirname(path.realpath(__file__)) + + # @validator('admin_users', 'allowed_users', 'admin_ext', 'disabled_ext', pre=True) + # def validate(cls, val): + # print(val) + # return val.split(',') + + class Config: + env_file = ".env" + env_file_encoding = "utf-8" + case_sensitive = False + json_loads = list_parse_fallback + + +conf = Settings() +WALLET = wallet_class() diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 872d6c97b..6fccb8ee6 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -40,7 +40,7 @@ async def update_admin(user: str, **kwargs) -> Admin: # new_settings = await get_admin() # return new_settings -async def get_admin() -> List[Admin]: +async def get_admin() -> Admin: row = await db.fetchone("SELECT * FROM admin") return Admin(**row) if row else None diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 13b769232..574f772d1 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -2,93 +2,151 @@ from os import getenv from sqlalchemy.exc import OperationalError # type: ignore +from lnbits.config import conf from lnbits.helpers import urlsafe_short_hash async def m001_create_admin_table(db): - user = None - site_title = None - site_tagline = None - site_description = None - allowed_users = None - admin_users = None - default_wallet_name = None - data_folder = None - disabled_ext = None - force_https = True - service_fee = 0 - funding_source = "" + # users/server + user = conf.admin_users[0] + admin_users = ",".join(conf.admin_users) + allowed_users = ",".join(conf.allowed_users) + admin_ext = ",".join(conf.admin_ext) + disabled_ext = ",".join(conf.disabled_ext) + funding_source = conf.funding_source + #operational + data_folder = conf.data_folder + database_url = conf.database_url + force_https = conf.force_https + service_fee = conf.service_fee + hide_api = conf.hide_api + denomination = conf.denomination + # Theme'ing + site_title = conf.site_title + site_tagline = conf.site_tagline + site_description = conf.site_description + default_wallet_name = conf.default_wallet_name + theme = ",".join(conf.theme) + ad_space = ",".join(conf.ad_space) - if getenv("LNBITS_SITE_TITLE"): - site_title = getenv("LNBITS_SITE_TITLE") + # if getenv("LNBITS_ADMIN_EXTENSIONS"): + # admin_ext = getenv("LNBITS_ADMIN_EXTENSIONS") - if getenv("LNBITS_SITE_TAGLINE"): - site_tagline = getenv("LNBITS_SITE_TAGLINE") + # if getenv("LNBITS_DATABASE_URL"): + # database_url = getenv("LNBITS_DATABASE_URL") - if getenv("LNBITS_SITE_DESCRIPTION"): - site_description = getenv("LNBITS_SITE_DESCRIPTION") + # if getenv("LNBITS_HIDE_API"): + # hide_api = getenv("LNBITS_HIDE_API") - if getenv("LNBITS_ALLOWED_USERS"): - allowed_users = getenv("LNBITS_ALLOWED_USERS") + # if getenv("LNBITS_THEME_OPTIONS"): + # theme = getenv("LNBITS_THEME_OPTIONS") - if getenv("LNBITS_ADMIN_USERS"): - admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split()) - user = admin_users.split(',')[0] + # if getenv("LNBITS_AD_SPACE"): + # ad_space = getenv("LNBITS_AD_SPACE") - if getenv("LNBITS_DEFAULT_WALLET_NAME"): - default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") + # if getenv("LNBITS_SITE_TITLE"): + # site_title = getenv("LNBITS_SITE_TITLE") - if getenv("LNBITS_DATA_FOLDER"): - data_folder = getenv("LNBITS_DATA_FOLDER") + # if getenv("LNBITS_SITE_TAGLINE"): + # site_tagline = getenv("LNBITS_SITE_TAGLINE") - if getenv("LNBITS_DISABLED_EXTENSIONS"): - disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS") + # if getenv("LNBITS_SITE_DESCRIPTION"): + # site_description = getenv("LNBITS_SITE_DESCRIPTION") - if getenv("LNBITS_FORCE_HTTPS"): - force_https = getenv("LNBITS_FORCE_HTTPS") + # if getenv("LNBITS_ALLOWED_USERS"): + # allowed_users = getenv("LNBITS_ALLOWED_USERS") - if getenv("LNBITS_SERVICE_FEE"): - service_fee = getenv("LNBITS_SERVICE_FEE") + # if getenv("LNBITS_ADMIN_USERS"): + # admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split()) + # user = admin_users.split(',')[0] + + # if getenv("LNBITS_DEFAULT_WALLET_NAME"): + # default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") - if getenv("LNBITS_BACKEND_WALLET_CLASS"): - funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS") + # if getenv("LNBITS_DATA_FOLDER"): + # data_folder = getenv("LNBITS_DATA_FOLDER") + + # if getenv("LNBITS_DISABLED_EXTENSIONS"): + # disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS") + + # if getenv("LNBITS_FORCE_HTTPS"): + # force_https = getenv("LNBITS_FORCE_HTTPS") + + # if getenv("LNBITS_SERVICE_FEE"): + # service_fee = getenv("LNBITS_SERVICE_FEE") + + # if getenv("LNBITS_DENOMINATION"): + # denomination = getenv("LNBITS_DENOMINATION", "sats") + + # if getenv("LNBITS_BACKEND_WALLET_CLASS"): + # funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS") await db.execute( """ CREATE TABLE IF NOT EXISTS admin ( - "user" TEXT, + "user" TEXT PRIMARY KEY, + admin_users TEXT, + allowed_users TEXT, + admin_ext TEXT, + disabled_ext TEXT, + funding_source TEXT, + data_folder TEXT, + database_url TEXT, + force_https BOOLEAN, + service_fee REAL, + hide_api BOOLEAN, + denomination TEXT, site_title TEXT, site_tagline TEXT, site_description TEXT, - admin_users TEXT, - allowed_users TEXT, default_wallet_name TEXT, - data_folder TEXT, - disabled_ext TEXT, - force_https BOOLEAN, - service_fee REAL, - funding_source TEXT + theme TEXT, + ad_space TEXT ); """ ) await db.execute( """ - INSERT INTO admin ("user", site_title, site_tagline, site_description, admin_users, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - user.strip(), + INSERT INTO admin ( + "user", + admin_users, + allowed_users, + admin_ext, + disabled_ext, + funding_source, + data_folder, + database_url, + force_https, + service_fee, + hide_api, + denomination, site_title, site_tagline, site_description, - admin_users[1:], - allowed_users, default_wallet_name, - data_folder, + theme, + ad_space) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + user, + admin_users, + allowed_users, + admin_ext, disabled_ext, + funding_source, + data_folder, + database_url, force_https, service_fee, - funding_source, + hide_api, + denomination, + site_title, + site_tagline, + site_description, + default_wallet_name, + theme, + ad_space, ), ) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 4080ff018..f7c64de55 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -2,7 +2,7 @@ from sqlite3 import Row from typing import List, Optional from fastapi import Query -from pydantic import BaseModel +from pydantic import BaseModel, Field class UpdateAdminSettings(BaseModel): @@ -19,18 +19,27 @@ class UpdateAdminSettings(BaseModel): funding_source: Optional[str] class Admin(BaseModel): + # users user: str + admin_users: Optional[str] + allowed_users: Optional[str] + admin_ext: Optional[str] + disabled_ext: Optional[str] + funding_source: Optional[str] + # ops + data_folder: Optional[str] + database_url: Optional[str] + force_https: bool = Field(default=True) + service_fee: float = Field(default=0) + hide_api: bool = Field(default=False) + # Change theme site_title: Optional[str] site_tagline: Optional[str] site_description: Optional[str] - allowed_users: Optional[str] - admin_users: str - default_wallet_name: str - data_folder: str - disabled_ext: str - force_https: Optional[bool] = Query(True) - service_fee: float - funding_source: str + default_wallet_name: Optional[str] + denomination: str = Field(default="sats") + theme: Optional[str] + ad_space: Optional[str] @classmethod def from_row(cls, row: Row) -> "Admin": diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py index 00a0c99fc..105f05a10 100644 --- a/lnbits/extensions/admin/views.py +++ b/lnbits/extensions/admin/views.py @@ -19,15 +19,17 @@ templates = Jinja2Templates(directory="templates") @admin_ext.get("/", response_class=HTMLResponse) async def index(request: Request, user: User = Depends(check_user_exists)): admin = await get_admin() - print(g()) funding = [f.dict() for f in await get_funding()] - + error, balance = await g().WALLET.status() print("ADMIN", admin.dict()) + print(g().admin_conf) return admin_renderer().TemplateResponse( "admin/index.html", { "request": request, "user": user.dict(), "admin": admin.dict(), - "funding": funding + "funding": funding, + "settings": g().admin_conf.dict(), + "balance": balance } ) diff --git a/lnbits/settings.py b/lnbits/settings.py index 43cb87cbe..ed5c77f7e 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -4,7 +4,7 @@ from email.policy import default from os import path from typing import List -from environs import Env # type: ignore +from environs import Env env = Env() env.read_env() From 66a7f53b976ae98a1e18cff8305da1299e524b69 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 10:28:07 +0000 Subject: [PATCH 021/614] topup wallet endpoint --- lnbits/extensions/admin/crud.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 6fccb8ee6..683558f94 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -1,26 +1,31 @@ +import json from typing import List, Optional from lnbits.core.crud import create_payment from lnbits.helpers import urlsafe_short_hash from lnbits.settings import * +from lnbits.tasks import internal_invoice_queue from . import db from .models import Admin, Funding -def update_wallet_balance(wallet_id: str, amount: int) -> str: +async def update_wallet_balance(wallet_id: str, amount: int) -> str: temp_id = f"temp_{urlsafe_short_hash()}" internal_id = f"internal_{urlsafe_short_hash()}" - create_payment( + + payment = await create_payment( wallet_id=wallet_id, checking_id=internal_id, payment_request="admin_internal", payment_hash="admin_internal", - amount=amount * 1000, + amount=amount*1000, memo="Admin top up", pending=False, ) - return "success" + # manually send this for now + await internal_invoice_queue.put(internal_id) + return payment async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) From 663c7ebd2f52c4cc0ea57839d921e3730d4decef Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 10:28:44 +0000 Subject: [PATCH 022/614] update admin settings in db --- lnbits/extensions/admin/models.py | 29 +++++++++++++++++----------- lnbits/extensions/admin/views_api.py | 8 ++++---- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index f7c64de55..36d9b8152 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -6,17 +6,24 @@ from pydantic import BaseModel, Field class UpdateAdminSettings(BaseModel): - site_title: Optional[str] - site_tagline: Optional[str] - site_description: Optional[str] - allowed_users: Optional[str] - admin_users: Optional[str] - default_wallet_name: Optional[str] - data_folder: Optional[str] - disabled_ext: Optional[str] - force_https: Optional[bool] - service_fee: Optional[float] - funding_source: Optional[str] + # users + admin_users: str = Query(None) + allowed_users: str = Query(None) + admin_ext: str = Query(None) + disabled_ext: str = Query(None) + funding_source: str = Query(None) + # ops + force_https: bool = Query(None) + service_fee: float = Query(None, ge=0) + hide_api: bool = Query(None) + # Change theme + site_title: str = Query(None) + site_tagline: str = Query(None) + site_description: str = Query(None) + default_wallet_name: str = Query(None) + denomination: str = Query(None) + theme: str = Query(None) + ad_space: str = Query(None) class Admin(BaseModel): # users diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index b2c65be25..cb526aa54 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -12,16 +12,16 @@ from .crud import get_admin, update_admin, update_wallet_balance @admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK) -async def api_update_balance(wallet_id, topup_amount, g: WalletTypeInfo = Depends(require_admin_key)): - print(g.wallet) +async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)): try: wallet = await get_wallet(wallet_id) except: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" ) - print(wallet) - print(topup_amount) + + await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount)) + return {"status": "Success"} From bc090190fca9a170566e1d605bdbdbd3536c70f5 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 10:29:18 +0000 Subject: [PATCH 023/614] update settings and topup logic --- .../admin/templates/admin/index.html | 91 ++++++++++++------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 65ac9f336..e9ddc7c47 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -30,7 +30,7 @@
- + @@ -61,7 +61,7 @@
- +

TopUp a wallet

@@ -87,13 +87,13 @@
- +
@@ -577,7 +577,6 @@

Site Description

s !== ad) }, - topupWallet: function () { - var self = this + topupWallet() { LNbits.api .request( 'GET', '/admin/api/v1/admin/' + - self.wallet.id + + this.wallet.data.id + '/' + - self.wallet.data.amount, - self.g.user.wallets[0].adminkey + this.wallet.data.amount, + this.g.user.wallets[0].adminkey ) - .then(function (response) { - self.$q.notify({ + .then((response) => { + this.$q.notify({ type: 'positive', message: - 'Success! Added ' + - self.wallet.amount + - ' to ' + - self.wallet.id, + 'Success! Added ' + + this.wallet.data.amount + + ' to ' + + this.wallet.data.id, icon: null }) + this.wallet.data = {} }) .catch(function (error) { LNbits.utils.notifyApiError(error) @@ -1224,36 +1224,59 @@ self.data.admin.edited.push(source) console.log(self.data.admin.edited) }, - UpdateLNbits: function () { - var self = this - let {site_title, admin_users, default_wallet_name, data_folder, disabled_ext, service_fee, funding_source_primary} = this.data.admin + UpdateLNbits() { + let { + admin_users, + allowed_users, + admin_ext, + disabled_ext, + funding_source, + force_https, + service_fee, + hide_api, + site_title, + site_tagline, + site_description, + default_wallet_name, + denomination, + theme, + ad_space + } = this.data.admin + //console.log("this", this.data.admin) let data = { - site_title, - site_tagline: this.data.admin.tagline, - site_description: this.data.admin.description, - admin_users: admin_users.toString(), - default_wallet_name, - data_folder, + admin_users: admin_users.toString(), + allowed_users: allowed_users.toString(), + admin_ext: admin_ext.toString(), disabled_ext: disabled_ext.toString(), - service_fee, - funding_source: funding_source_primary} + funding_source, + force_https, + service_fee, + hide_api, + site_title, + site_tagline, + site_description, + default_wallet_name, + denomination, + theme: theme.toString(), + ad_space: ad_space.toString() + } console.log(data) LNbits.api .request( 'POST', '/admin/api/v1/admin/', - self.g.user.wallets[0].adminkey, + this.g.user.wallets[0].adminkey, data ) - .then(function (response) { + .then(response => { console.log(response.data) - self.$q.notify({ + this.$q.notify({ type: 'positive', message: 'Success! Added ' + - self.wallet.amount + + this.wallet.amount + ' to ' + - self.wallet.id, + this.wallet.id, icon: null }) }) From 313574df1991c47b6a0dbc390f8d839278e200d4 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:34:47 +0000 Subject: [PATCH 024/614] make removeEmptyString fn as helper fn --- lnbits/app.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index ccac1e002..5df439dc8 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -26,6 +26,7 @@ from .helpers import ( get_css_vendored, get_js_vendored, get_valid_extensions, + removeEmptyString, template_renderer, url_for_vendored, ) @@ -113,9 +114,6 @@ def check_settings(app: FastAPI): @app.on_event("startup") async def check_settings_admin(): - def removeEmptyString(arr): - return list(filter(None, arr)) - while True: admin_set = await get_admin_settings() if admin_set : From edfa98f00e7111096321d259fca4cd2eb36ac3d5 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:35:15 +0000 Subject: [PATCH 025/614] add some defaults --- lnbits/config.py | 6 +++--- lnbits/extensions/admin/models.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lnbits/config.py b/lnbits/config.py index 02e8cf537..b2fbfff12 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -33,10 +33,10 @@ class Settings(BaseSettings): hide_api: bool = Field(default=False, env="LNBITS_HIDE_API") denomination: str = Field(default="sats", env="LNBITS_DENOMINATION") # Change theme - site_title: str = Field(default=None, env="LNBITS_SITE_TITLE") - site_tagline: str = Field(default=None, env="LNBITS_SITE_TAGLINE") + site_title: str = Field(default="LNbits", env="LNBITS_SITE_TITLE") + site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE") site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") - default_wallet_name: str = Field(default=None, env="LNBITS_DEFAULT_WALLET_NAME") + default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME") theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 36d9b8152..0f25679dc 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -17,11 +17,11 @@ class UpdateAdminSettings(BaseModel): service_fee: float = Query(None, ge=0) hide_api: bool = Query(None) # Change theme - site_title: str = Query(None) - site_tagline: str = Query(None) + site_title: str = Query("LNbits") + site_tagline: str = Query("free and open-source lightning wallet") site_description: str = Query(None) - default_wallet_name: str = Query(None) - denomination: str = Query(None) + default_wallet_name: str = Query("LNbits wallet") + denomination: str = Query("sats") theme: str = Query(None) ad_space: str = Query(None) From ba6bda39ab4a6c50631658d9bee85329c4784950 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:36:04 +0000 Subject: [PATCH 026/614] removeEmtpy sting as helper fn --- lnbits/helpers.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lnbits/helpers.py b/lnbits/helpers.py index e213240cd..e456f7150 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -154,8 +154,20 @@ def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> s url = f"{base}{endpoint}{url_params}" return url +def removeEmptyString(arr): + return list(filter(None, arr)) def template_renderer(additional_folders: List = []) -> Jinja2Templates: + if(settings.LNBITS_ADMIN_UI): + _ = g().admin_conf + settings.LNBITS_AD_SPACE = _.ad_space + settings.LNBITS_HIDE_API = _.hide_api + settings.LNBITS_SITE_TITLE = _.site_title + settings.LNBITS_DENOMINATION = _.denomination + settings.LNBITS_SITE_TAGLINE = _.site_tagline + settings.LNBITS_SITE_DESCRIPTION = _.site_description + settings.LNBITS_THEME_OPTIONS = _.theme + t = Jinja2Templates( loader=jinja2.FileSystemLoader( ["lnbits/templates", "lnbits/core/templates", *additional_folders] From 0a211a2fb2d1fdf7c135cf637b768c8fc2855a64 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:41:11 +0000 Subject: [PATCH 027/614] cleanup --- lnbits/extensions/admin/crud.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 683558f94..e14ad1949 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -1,9 +1,7 @@ -import json -from typing import List, Optional +from typing import List from lnbits.core.crud import create_payment from lnbits.helpers import urlsafe_short_hash -from lnbits.settings import * from lnbits.tasks import internal_invoice_queue from . import db @@ -37,14 +35,6 @@ async def update_admin(user: str, **kwargs) -> Admin: assert row, "Newly updated settings couldn't be retrieved" return Admin(**row) if row else None -# async def update_admin(user: str, **kwargs) -> Optional[Admin]: -# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) -# await db.execute( -# f"UPDATE admin SET {q} WHERE user = ?", (*kwargs.values(), user) -# ) -# new_settings = await get_admin() -# return new_settings - async def get_admin() -> Admin: row = await db.fetchone("SELECT * FROM admin") return Admin(**row) if row else None From 5ec7f21650897699f39723bbe93edd3d53da1fd2 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:42:28 +0000 Subject: [PATCH 028/614] make string to list --- lnbits/extensions/admin/views_api.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index cb526aa54..1d4e6a9c7 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -1,5 +1,6 @@ from http import HTTPStatus +# from config import conf from fastapi import Body, Depends, Request from starlette.exceptions import HTTPException @@ -7,6 +8,8 @@ from lnbits.core.crud import get_wallet from lnbits.decorators import WalletTypeInfo, require_admin_key from lnbits.extensions.admin import admin_ext from lnbits.extensions.admin.models import Admin, UpdateAdminSettings +from lnbits.helpers import removeEmptyString +from lnbits.requestvars import g from .crud import get_admin, update_admin, update_wallet_balance @@ -19,7 +22,7 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" ) - + await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount)) return {"status": "Success"} @@ -29,14 +32,24 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D async def api_update_admin( request: Request, data: UpdateAdminSettings = Body(...), - g: WalletTypeInfo = Depends(require_admin_key) + w: WalletTypeInfo = Depends(require_admin_key) ): admin = await get_admin() print(data) - if not admin.user == g.wallet.user: + if not admin.user == w.wallet.user: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" ) - updated = await update_admin(user=g.wallet.user, **data.dict()) - print(updated) + updated = await update_admin(user=w.wallet.user, **data.dict()) + + updated.admin_users = removeEmptyString(updated.admin_users.split(',')) + updated.allowed_users = removeEmptyString(updated.allowed_users.split(',')) + updated.admin_ext = removeEmptyString(updated.admin_ext.split(',')) + updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(',')) + updated.theme = removeEmptyString(updated.theme.split(',')) + updated.ad_space = removeEmptyString(updated.ad_space.split(',')) + + g().admin_conf = g().admin_conf.copy(update=updated.dict()) + + print(g().admin_conf) return {"status": "Success"} From 4d16c296aa1d8b8375e3f131e459a2f2f80f0d3b Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:42:47 +0000 Subject: [PATCH 029/614] success message --- lnbits/extensions/admin/templates/admin/index.html | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index e9ddc7c47..9aa4f12a8 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1273,10 +1273,7 @@ this.$q.notify({ type: 'positive', message: - 'Success! Added ' + - this.wallet.amount + - ' to ' + - this.wallet.id, + 'Success! Settings changed!', icon: null }) }) From 2c48e3aa5f1e561e4106c60bfe0e4266a86711b3 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Thu, 14 Apr 2022 10:42:26 +0100 Subject: [PATCH 030/614] allow html to be passed to description --- lnbits/core/templates/core/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html index f769b44f6..03cf706f5 100644 --- a/lnbits/core/templates/core/index.html +++ b/lnbits/core/templates/core/index.html @@ -82,7 +82,7 @@ >
-

{{SITE_DESCRIPTION}}

+

{{SITE_DESCRIPTION | safe}}

From f16ead4f7303787d945ab9dc39550ae3bc0ec611 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Thu, 14 Apr 2022 16:37:13 +0100 Subject: [PATCH 031/614] update funding wallets --- lnbits/extensions/admin/crud.py | 12 + .../admin/templates/admin/index.html | 523 ++++++++++-------- lnbits/extensions/admin/views.py | 3 +- lnbits/extensions/admin/views_api.py | 19 +- 4 files changed, 315 insertions(+), 242 deletions(-) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index e14ad1949..dd39e8e42 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -39,6 +39,18 @@ async def get_admin() -> Admin: row = await db.fetchone("SELECT * FROM admin") return Admin(**row) if row else None +async def update_funding(data: Funding) -> Funding: + await db.execute( + """ + UPDATE funding + SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ? + WHERE id = ? + """, + (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,), + ) + row = await db.fetchone('SELECT * FROM funding WHERE "id" = ?', (data.id,)) + assert row, "Newly updated settings couldn't be retrieved" + return Funding(**row) if row else None async def get_funding() -> List[Funding]: rows = await db.fetchall("SELECT * FROM funding") diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 9aa4f12a8..d56b3d793 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -31,11 +31,11 @@ - - - -
Wallets Management
-
+ + + +
Wallets Management
+
@@ -62,43 +62,96 @@
-

TopUp a wallet

-
-
- -
-
-
- -
+

TopUp a wallet

+
+
+ +
-
- +
+
+
+
+ +

Funding Sources

- + {% raw %} + + + + + + + + + + + + + + {% endraw %} + +
+ + + + +
User Management
+
+

+ Super Admin: {% raw %}{{this.data.admin.user}}{% endraw %} +

+

Admin Users

+ hint="Users with admin privileges" + >
{% raw %} -
-
-
-

Allowed Users

- - - +
- {% raw %} - Allowed Users

+ - {{ user }} -
- {% endraw %} + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+

Admin Extensions

+ +
+
+
+

Disabled Extensions

+ +
+
+
+
+ Save +
+ + + + +
Server Management

-
-
-
-

Admin Extensions

- -
-
-
-

Disabled Extensions

- -
-
-
-
- Save -
-
-
- - -
Server Management
-

Server Info

    {%raw%} -
  • SQlite: {{data.admin.data_folder}}
  • -
  • Postgres: {{data.admin.database_url}}
  • +
  • + SQlite: {{data.admin.data_folder}} +
  • +
  • + Postgres: {{data.admin.database_url}} +
  • {%endraw%}

@@ -520,7 +576,10 @@ Hide API - Hides wallet api, extensions can choose to honor + Hides wallet api, extensions can choose to + honor
-
- -
- Save -
-
-
- - -
UI Management
-
+
+ +
+ Save +
+ + + + +
UI Management
+
@@ -575,15 +629,15 @@
-

Site Description

- -
-
+

Site Description

+ +
+

Default Wallet Name

@@ -628,12 +682,15 @@ @keydown.enter="addAdSpace" type="text" label="Ad image URL" - hint="Ad image filepaths or urls, extensions can choose to honor"> + hint="Ad image filepaths or urls, extensions can choose to honor" + >
{% raw %} -
-
- -
- Save -
-
-
- - +
+ +
+ Save +
+
+
+
+
- - -

Admin

-

+ + - -
- - -
Wallet topup
-
-
- -
-
- -
-
-
- -
-
-
-
{% endblock %} {% block scripts %} {{ window_vars(user) }} @@ -1100,14 +1114,22 @@ 'LnbitsWallet', 'OpenNodeWallet' ], - + admin: { edited: [], - funding: {}, + funding: [], senddata: {} } }, - themes: ['classic', 'bitcoin', 'flamingo', 'mint', 'autumn', 'monochrome', 'salvador'], + themes: [ + 'classic', + 'bitcoin', + 'flamingo', + 'mint', + 'autumn', + 'monochrome', + 'salvador' + ], options: [ 'bleskomat', 'captcha', @@ -1139,10 +1161,13 @@ self.cancel.on = true } funding = JSON.parse(String('{{ funding | tojson|safe }}')) - var i + funding.map(f => { + this.data.admin.funding.push(f) + }) + /*var i for (i = 0; i < funding.length; i++) { self.data.admin.funding[funding[i].backend_wallet] = funding[i] - } + }*/ let settings = JSON.parse('{{ settings | tojson|safe }}') settings.balance = '{{ balance }}' this.data.admin = {...this.data.admin, ...settings} @@ -1150,42 +1175,42 @@ console.log(settings) }, methods: { - addAdminUser(){ + addAdminUser() { let addUser = this.data.admin_users_add let admin_users = this.data.admin.admin_users - if(addUser.length && !admin_users.includes(addUser)){ + if (addUser.length && !admin_users.includes(addUser)) { admin_users.push(addUser) this.data.admin.admin_users = admin_users - this.data.admin_users_add = "" + this.data.admin_users_add = '' } }, - removeAdminUser(user){ + removeAdminUser(user) { let admin_users = this.data.admin.admin_users this.data.admin.admin_users = admin_users.filter(u => u !== user) }, - addAllowedUser(){ + addAllowedUser() { let addUser = this.data.allowed_users_add let allowed_users = this.data.admin.allowed_users - if(addUser.length && !allowed_users.includes(addUser)){ + if (addUser.length && !allowed_users.includes(addUser)) { allowed_users.push(addUser) this.data.admin.allowed_users = allowed_users - this.data.allowed_users_add = "" + this.data.allowed_users_add = '' } }, - removeAllowedUser(user){ + removeAllowedUser(user) { let allowed_users = this.data.admin.allowed_users this.data.admin.allowed_users = allowed_users.filter(u => u !== user) }, - addAdSpace(){ + addAdSpace() { let adSpace = this.data.ad_space_add let spaces = this.data.admin.ad_space - if(adSpace.length && !spaces.includes(adSpace)){ + if (adSpace.length && !spaces.includes(adSpace)) { spaces.push(adSpace) this.data.admin.ad_space = spaces - this.data.ad_space_add = "" + this.data.ad_space_add = '' } }, - removeAdSpace(ad){ + removeAdSpace(ad) { let spaces = this.data.admin.ad_space this.data.admin.ad_space = spaces.filter(s => s !== ad) }, @@ -1199,14 +1224,14 @@ this.wallet.data.amount, this.g.user.wallets[0].adminkey ) - .then((response) => { + .then(response => { this.$q.notify({ type: 'positive', message: - 'Success! Added ' + - this.wallet.data.amount + - ' to ' + - this.wallet.data.id, + 'Success! Added ' + + this.wallet.data.amount + + ' to ' + + this.wallet.data.id, icon: null }) this.wallet.data = {} @@ -1224,6 +1249,29 @@ self.data.admin.edited.push(source) console.log(self.data.admin.edited) }, + updateFunding(fund) { + let data = this.data.admin.funding.find(v => v.backend_wallet == fund) + + LNbits.api + .request( + 'POST', + '/admin/api/v1/admin/funding', + this.g.user.wallets[0].adminkey, + data + ) + .then(response => { + //let wallet = response.data.backend_wallet + //this.data.admin.funding[wallet] = response.data + //this.data.admin.funding[wallet].endpoint = response.data.endpoint + //console.log(this.data.admin.funding) + //console.log(this.data.admin) + this.$q.notify({ + type: 'positive', + message: `Success! ${response.data.backend_wallet} changed!`, + icon: null + }) + }) + }, UpdateLNbits() { let { admin_users, @@ -1272,8 +1320,7 @@ console.log(response.data) this.$q.notify({ type: 'positive', - message: - 'Success! Settings changed!', + message: 'Success! Settings changed!', icon: null }) }) diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py index 105f05a10..24b8ca859 100644 --- a/lnbits/extensions/admin/views.py +++ b/lnbits/extensions/admin/views.py @@ -21,8 +21,7 @@ async def index(request: Request, user: User = Depends(check_user_exists)): admin = await get_admin() funding = [f.dict() for f in await get_funding()] error, balance = await g().WALLET.status() - print("ADMIN", admin.dict()) - print(g().admin_conf) + return admin_renderer().TemplateResponse( "admin/index.html", { "request": request, diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 1d4e6a9c7..b797dc2d1 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -7,11 +7,11 @@ from starlette.exceptions import HTTPException from lnbits.core.crud import get_wallet from lnbits.decorators import WalletTypeInfo, require_admin_key from lnbits.extensions.admin import admin_ext -from lnbits.extensions.admin.models import Admin, UpdateAdminSettings +from lnbits.extensions.admin.models import Admin, Funding, UpdateAdminSettings from lnbits.helpers import removeEmptyString from lnbits.requestvars import g -from .crud import get_admin, update_admin, update_wallet_balance +from .crud import get_admin, update_admin, update_funding, update_wallet_balance @admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK) @@ -53,3 +53,18 @@ async def api_update_admin( print(g().admin_conf) return {"status": "Success"} + +@admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK) +async def api_update_funding( + request: Request, + data: Funding = Body(...), + w: WalletTypeInfo = Depends(require_admin_key) + ): + admin = await get_admin() + + if not admin.user == w.wallet.user: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) + funding = await update_funding(data=data) + return funding From 5a3ad81c315f42a7b482714e943a1b0a72028e93 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 18 Apr 2022 14:25:06 +0100 Subject: [PATCH 032/614] allow user settings without restart --- lnbits/core/views/generic.py | 7 ++++++- lnbits/decorators.py | 8 ++++++++ lnbits/helpers.py | 3 +++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 31a7b0300..83648c444 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -15,7 +15,9 @@ from lnbits.core import db from lnbits.core.models import User from lnbits.decorators import check_user_exists from lnbits.helpers import template_renderer, url_for +from lnbits.requestvars import g from lnbits.settings import ( + LNBITS_ADMIN_UI, LNBITS_ADMIN_USERS, LNBITS_ALLOWED_USERS, LNBITS_CUSTOM_LOGO, @@ -37,7 +39,6 @@ from ..services import pay_invoice, redeem_lnurl_withdraw core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"]) - @core_html_routes.get("/favicon.ico", response_class=FileResponse) async def favicon(): return FileResponse("lnbits/core/static/favicon.ico") @@ -119,6 +120,10 @@ async def wallet( wallet_name = nme service_fee = int(SERVICE_FEE) if int(SERVICE_FEE) == SERVICE_FEE else SERVICE_FEE + if LNBITS_ADMIN_UI: + LNBITS_ADMIN_USERS = g().admin_conf.admin_users + LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users + if not user_id: user = await get_user((await create_account()).id) logger.info(f"Create user {user.id}") # type: ignore diff --git a/lnbits/decorators.py b/lnbits/decorators.py index d4aa63aea..f951163f8 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -16,6 +16,7 @@ from lnbits.core.models import User, Wallet from lnbits.requestvars import g from lnbits.settings import ( LNBITS_ADMIN_EXTENSIONS, + LNBITS_ADMIN_UI, LNBITS_ADMIN_USERS, LNBITS_ALLOWED_USERS, ) @@ -138,6 +139,9 @@ async def get_key_type( detail="Invoice (or Admin) key required.", ) + if LNBITS_ADMIN_UI: + LNBITS_ADMIN_USERS = g().admin_conf.admin_users + for typenr, WalletChecker in zip( [0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker] ): @@ -231,6 +235,10 @@ async def check_user_exists(usr: UUID4) -> User: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="User does not exist." ) + + if LNBITS_ADMIN_UI: + LNBITS_ADMIN_USERS = g().admin_conf.admin_users + LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: raise HTTPException( diff --git a/lnbits/helpers.py b/lnbits/helpers.py index e456f7150..1167143f8 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -24,6 +24,9 @@ class Extension(NamedTuple): class ExtensionManager: def __init__(self): + if settings.LNBITS_ADMIN_UI: + settings.LNBITS_DISABLED_EXTENSIONS = g().admin_conf.disabled_ext + settings.LNBITS_ADMIN_EXTENSIONS = g().admin_conf.admin_ext self._disabled: List[str] = settings.LNBITS_DISABLED_EXTENSIONS self._admin_only: List[str] = [ x.strip(" ") for x in settings.LNBITS_ADMIN_EXTENSIONS From 1ff8a9fce5c15d421a8c37a3f8bc908a4b249510 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 18 Apr 2022 14:25:26 +0100 Subject: [PATCH 033/614] advert for server restart option --- lnbits/extensions/admin/templates/admin/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index d56b3d793..089c5f1ce 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -51,7 +51,9 @@
-

Active Funding

+

+ Active Funding (Requires server restart) +

Date: Thu, 21 Apr 2022 11:08:26 +0100 Subject: [PATCH 034/614] cleanup prints and console logs --- lnbits/extensions/admin/crud.py | 2 +- lnbits/extensions/admin/templates/admin/index.html | 4 ++-- lnbits/extensions/admin/views_api.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index dd39e8e42..f866bc1aa 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -27,7 +27,7 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str: async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) - print("UPDATE", q) + # print("UPDATE", q) await db.execute( f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) ) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 089c5f1ce..584d3a33f 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1310,7 +1310,7 @@ theme: theme.toString(), ad_space: ad_space.toString() } - console.log(data) + //console.log(data) LNbits.api .request( 'POST', @@ -1319,7 +1319,7 @@ data ) .then(response => { - console.log(response.data) + //console.log(response.data) this.$q.notify({ type: 'positive', message: 'Success! Settings changed!', diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index b797dc2d1..c0650c8a9 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -35,7 +35,7 @@ async def api_update_admin( w: WalletTypeInfo = Depends(require_admin_key) ): admin = await get_admin() - print(data) + # print(data) if not admin.user == w.wallet.user: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" @@ -51,7 +51,7 @@ async def api_update_admin( g().admin_conf = g().admin_conf.copy(update=updated.dict()) - print(g().admin_conf) + # print(g().admin_conf) return {"status": "Success"} @admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK) From b0f9c82e1b0b16e3e1d499f5173a0368e5d9c93e Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 16 May 2022 10:49:21 +0100 Subject: [PATCH 035/614] create first user on fresh install --- .env.example | 4 ++-- lnbits/config.py | 3 ++- lnbits/extensions/admin/README.md | 15 ++++++++------- lnbits/extensions/admin/migrations.py | 15 ++++++++++++++- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index bd1894843..7a49d5c5d 100644 --- a/.env.example +++ b/.env.example @@ -4,8 +4,8 @@ PORT=5000 DEBUG=false LNBITS_ADMIN_USERS="" # User IDs seperated by comma -LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access -LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS +LNBITS_ADMIN_EXTENSIONS="ngrok, admin" # Extensions only admin can access +LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma diff --git a/lnbits/config.py b/lnbits/config.py index b2fbfff12..3ce51c3cb 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -19,6 +19,7 @@ def list_parse_fallback(v): return v.replace(' ','').split(',') class Settings(BaseSettings): + admin_ui: bool = Field(default=True, env="LNBITS_ADMIN_UI") # users admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS") allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS") @@ -37,7 +38,7 @@ class Settings(BaseSettings): site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE") site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME") - theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS") + theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env env: Optional[str] diff --git a/lnbits/extensions/admin/README.md b/lnbits/extensions/admin/README.md index 277294592..6cf073a11 100644 --- a/lnbits/extensions/admin/README.md +++ b/lnbits/extensions/admin/README.md @@ -1,11 +1,12 @@ -

Example Extension

-

*tagline*

-This is an example extension to help you organise and build you own. +# Admin Extension -Try to include an image - +## Dashboard to manage LNbits from the UI +With AdminUI you can manage your LNbits from the UI -

If your extension has API endpoints, include useful ones here

+![AdminUI](https://i.imgur.com/BIyLkyG.png) -curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY" +## Before you start + +**This extension doesn't discard the need for the `.env` file!** +In the .env file, set the `LNBITS_ADMIN_USERS` variable to include at least your user id. diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 574f772d1..0e22e6677 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -6,9 +6,22 @@ from lnbits.config import conf from lnbits.helpers import urlsafe_short_hash +async def get_admin_user(): + if(conf.admin_users[0]): + return conf.admin_users[0] + from lnbits.core.crud import create_account, get_user + print("Seems like there's no admin users yet. Let's create an account for you!") + account = await create_account() + user = account.id + assert user, "Newly created user couldn't be retrieved" + print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.") + return user + + + async def m001_create_admin_table(db): # users/server - user = conf.admin_users[0] + user = await get_admin_user() admin_users = ",".join(conf.admin_users) allowed_users = ",".join(conf.allowed_users) admin_ext = ",".join(conf.admin_ext) From 2f2d70f9a8cdd3edc59cb3c71b841b6823309dcf Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 16 May 2022 12:29:58 +0100 Subject: [PATCH 036/614] fix schemas for admin --- lnbits/commands.py | 11 ++++++++--- lnbits/extensions/admin/crud.py | 12 ++++++------ lnbits/extensions/admin/migrations.py | 26 +++++++++++++------------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/lnbits/commands.py b/lnbits/commands.py index 8c39c338f..7d9b49e21 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -55,8 +55,12 @@ def bundle_vendored(): async def get_admin_settings(): from lnbits.extensions.admin.models import Admin - async with core_db.connect() as conn: + try: + ext_db = importlib.import_module(f"lnbits.extensions.admin").db + except: + return False + async with ext_db.connect() as conn: if conn.type == SQLITE: exists = await conn.fetchone( "SELECT * FROM sqlite_master WHERE type='table' AND name='admin'" @@ -65,11 +69,12 @@ async def get_admin_settings(): exists = await conn.fetchone( "SELECT * FROM information_schema.tables WHERE table_name = 'admin'" ) + print("EXISTS", exists) if not exists: return False - row = await conn.fetchone("SELECT * from admin") - + row = await conn.fetchone("SELECT * from admin.admin") + return Admin(**row) if row else None async def migrate_databases(): diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index f866bc1aa..67fbc6143 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -29,30 +29,30 @@ async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) # print("UPDATE", q) await db.execute( - f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) + f'UPDATE admin.admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) ) - row = await db.fetchone('SELECT * FROM admin WHERE "user" = ?', (user,)) + row = await db.fetchone('SELECT * FROM admin.admin WHERE "user" = ?', (user,)) assert row, "Newly updated settings couldn't be retrieved" return Admin(**row) if row else None async def get_admin() -> Admin: - row = await db.fetchone("SELECT * FROM admin") + row = await db.fetchone("SELECT * FROM admin.admin") return Admin(**row) if row else None async def update_funding(data: Funding) -> Funding: await db.execute( """ - UPDATE funding + UPDATE admin.funding SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ? WHERE id = ? """, (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,), ) - row = await db.fetchone('SELECT * FROM funding WHERE "id" = ?', (data.id,)) + row = await db.fetchone('SELECT * FROM admin.funding WHERE "id" = ?', (data.id,)) assert row, "Newly updated settings couldn't be retrieved" return Funding(**row) if row else None async def get_funding() -> List[Funding]: - rows = await db.fetchall("SELECT * FROM funding") + rows = await db.fetchall("SELECT * FROM admin.funding") return [Funding(**row) for row in rows] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 0e22e6677..c94d140b7 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -96,7 +96,7 @@ async def m001_create_admin_table(db): await db.execute( """ - CREATE TABLE IF NOT EXISTS admin ( + CREATE TABLE IF NOT EXISTS admin.admin ( "user" TEXT PRIMARY KEY, admin_users TEXT, allowed_users TEXT, @@ -120,7 +120,7 @@ async def m001_create_admin_table(db): ) await db.execute( """ - INSERT INTO admin ( + INSERT INTO admin.admin ( "user", admin_users, allowed_users, @@ -171,7 +171,7 @@ async def m001_create_funding_table(db): # Make the funding table, if it does not already exist await db.execute( """ - CREATE TABLE IF NOT EXISTS funding ( + CREATE TABLE IF NOT EXISTS admin.funding ( id TEXT PRIMARY KEY, backend_wallet TEXT, endpoint TEXT, @@ -188,7 +188,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, selected) VALUES (?, ?, ?, ?) """, ( @@ -200,7 +200,7 @@ async def m001_create_funding_table(db): ) await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -214,7 +214,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -228,7 +228,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, port, admin_key, cert, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, port, admin_key, cert, selected) VALUES (?, ?, ?, ?, ?, ?, ?) """, ( @@ -244,7 +244,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected) VALUES (?, ?, ?, ?, ?, ?) """, ( @@ -259,7 +259,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected) VALUES (?, ?, ?, ?, ?, ?) """, ( @@ -274,7 +274,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -288,7 +288,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -302,7 +302,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -317,7 +317,7 @@ async def m001_create_funding_table(db): ## PLACEHOLDER FOR ECLAIR WALLET # await db.execute( # """ - # INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + # INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) # VALUES (?, ?, ?, ?, ?) # """, # ( From 08e54de99b72eec2c8f7850ef0d346c15bf64705 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 16 May 2022 12:53:47 +0100 Subject: [PATCH 037/614] fix sqlite and show user account --- lnbits/app.py | 1 + lnbits/commands.py | 2 +- lnbits/extensions/admin/migrations.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lnbits/app.py b/lnbits/app.py index 5df439dc8..f066163f7 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -117,6 +117,7 @@ def check_settings(app: FastAPI): while True: admin_set = await get_admin_settings() if admin_set : + print(f"Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}") break print("ERROR:", admin_set) await asyncio.sleep(5) diff --git a/lnbits/commands.py b/lnbits/commands.py index 7d9b49e21..763a5b90b 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -69,7 +69,7 @@ async def get_admin_settings(): exists = await conn.fetchone( "SELECT * FROM information_schema.tables WHERE table_name = 'admin'" ) - print("EXISTS", exists) + if not exists: return False diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index c94d140b7..6c5b507d1 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -15,6 +15,7 @@ async def get_admin_user(): user = account.id assert user, "Newly created user couldn't be retrieved" print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.") + conf.admin_users.insert(0, user) return user From 1adfb674ccb6bf44ad6e3680c795ed085bd20d8e Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 16 May 2022 15:35:04 +0100 Subject: [PATCH 038/614] cleanup and info to user on startup --- lnbits/app.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index f066163f7..950b61407 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -113,13 +113,11 @@ def create_app(config_object="lnbits.settings") -> FastAPI: def check_settings(app: FastAPI): @app.on_event("startup") async def check_settings_admin(): - while True: admin_set = await get_admin_settings() if admin_set : - print(f"Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}") break - print("ERROR:", admin_set) + print("Waiting for admin settings... retrying in 5 seconds!") await asyncio.sleep(5) admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(',')) @@ -129,6 +127,7 @@ def check_settings(app: FastAPI): admin_set.theme = removeEmptyString(admin_set.theme.split(',')) admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(',')) g().admin_conf = conf.copy(update=admin_set.dict()) + print(f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}") def check_funding_source(app: FastAPI) -> None: @app.on_event("startup") From 363bc85e3b73967c26b9809f1d71664ec8719d8d Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 8 Jun 2022 11:00:43 +0100 Subject: [PATCH 039/614] add custom logo --- lnbits/config.py | 1 + lnbits/extensions/admin/migrations.py | 58 ++----------------- lnbits/extensions/admin/models.py | 2 + .../admin/templates/admin/index.html | 20 +++++-- lnbits/helpers.py | 3 +- 5 files changed, 26 insertions(+), 58 deletions(-) diff --git a/lnbits/config.py b/lnbits/config.py index 3ce51c3cb..d07ca044e 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -39,6 +39,7 @@ class Settings(BaseSettings): site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME") theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS") + custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env env: Optional[str] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 6c5b507d1..aad66f02a 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -41,60 +41,9 @@ async def m001_create_admin_table(db): site_description = conf.site_description default_wallet_name = conf.default_wallet_name theme = ",".join(conf.theme) + custom_logo = conf.custom_logo ad_space = ",".join(conf.ad_space) - # if getenv("LNBITS_ADMIN_EXTENSIONS"): - # admin_ext = getenv("LNBITS_ADMIN_EXTENSIONS") - - # if getenv("LNBITS_DATABASE_URL"): - # database_url = getenv("LNBITS_DATABASE_URL") - - # if getenv("LNBITS_HIDE_API"): - # hide_api = getenv("LNBITS_HIDE_API") - - # if getenv("LNBITS_THEME_OPTIONS"): - # theme = getenv("LNBITS_THEME_OPTIONS") - - # if getenv("LNBITS_AD_SPACE"): - # ad_space = getenv("LNBITS_AD_SPACE") - - # if getenv("LNBITS_SITE_TITLE"): - # site_title = getenv("LNBITS_SITE_TITLE") - - # if getenv("LNBITS_SITE_TAGLINE"): - # site_tagline = getenv("LNBITS_SITE_TAGLINE") - - # if getenv("LNBITS_SITE_DESCRIPTION"): - # site_description = getenv("LNBITS_SITE_DESCRIPTION") - - # if getenv("LNBITS_ALLOWED_USERS"): - # allowed_users = getenv("LNBITS_ALLOWED_USERS") - - # if getenv("LNBITS_ADMIN_USERS"): - # admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split()) - # user = admin_users.split(',')[0] - - # if getenv("LNBITS_DEFAULT_WALLET_NAME"): - # default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") - - # if getenv("LNBITS_DATA_FOLDER"): - # data_folder = getenv("LNBITS_DATA_FOLDER") - - # if getenv("LNBITS_DISABLED_EXTENSIONS"): - # disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS") - - # if getenv("LNBITS_FORCE_HTTPS"): - # force_https = getenv("LNBITS_FORCE_HTTPS") - - # if getenv("LNBITS_SERVICE_FEE"): - # service_fee = getenv("LNBITS_SERVICE_FEE") - - # if getenv("LNBITS_DENOMINATION"): - # denomination = getenv("LNBITS_DENOMINATION", "sats") - - # if getenv("LNBITS_BACKEND_WALLET_CLASS"): - # funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS") - await db.execute( """ CREATE TABLE IF NOT EXISTS admin.admin ( @@ -115,6 +64,7 @@ async def m001_create_admin_table(db): site_description TEXT, default_wallet_name TEXT, theme TEXT, + custom_logo TEXT, ad_space TEXT ); """ @@ -139,8 +89,9 @@ async def m001_create_admin_table(db): site_description, default_wallet_name, theme, + custom_logo, ad_space) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( user, @@ -160,6 +111,7 @@ async def m001_create_admin_table(db): site_description, default_wallet_name, theme, + custom_logo, ad_space, ), ) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 0f25679dc..3b17e720c 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -23,6 +23,7 @@ class UpdateAdminSettings(BaseModel): default_wallet_name: str = Query("LNbits wallet") denomination: str = Query("sats") theme: str = Query(None) + custom_logo: str = Query(None) ad_space: str = Query(None) class Admin(BaseModel): @@ -46,6 +47,7 @@ class Admin(BaseModel): default_wallet_name: Optional[str] denomination: str = Field(default="sats") theme: Optional[str] + custom_logo: Optional[str] ad_space: Optional[str] @classmethod diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 584d3a33f..d9790051e 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -705,6 +705,19 @@
+
+
+

Custom Logo

+ +
+
+
@@ -718,10 +731,7 @@
- +
@@ -1292,6 +1323,8 @@ disabled_ext, funding_source, force_https, + reserve_fee_min, + reserve_fee_pct, service_fee, hide_api, site_title, @@ -1311,6 +1344,8 @@ disabled_ext: disabled_ext.toString(), funding_source, force_https, + reserve_fee_min, + reserve_fee_pct, service_fee, hide_api, site_title, diff --git a/lnbits/settings.py b/lnbits/settings.py index ed5c77f7e..8e5c321a2 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,6 +1,5 @@ import importlib import subprocess -from email.policy import default from os import path from typing import List From fcf05a7dd19b831ee986578efad9b2d9094e69bd Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 8 Jun 2022 15:38:28 +0100 Subject: [PATCH 041/614] calle's semantics --- lnbits/extensions/admin/templates/admin/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 832629bcf..d34b90683 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -67,7 +67,7 @@
-

Minimum wallet reserve

+

Fee reserve

From faab389e3fa21671313b66236e66fccbf1f70a1d Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 5 Jul 2022 16:25:02 +0100 Subject: [PATCH 042/614] blacked --- lnbits/extensions/admin/__init__.py | 1 + lnbits/extensions/admin/crud.py | 23 ++++++++++++--- lnbits/extensions/admin/migrations.py | 10 ++++--- lnbits/extensions/admin/models.py | 2 ++ lnbits/extensions/admin/views.py | 10 ++++--- lnbits/extensions/admin/views_api.py | 41 ++++++++++++++------------- 6 files changed, 56 insertions(+), 31 deletions(-) diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py index 6a56b2bb1..24b91fe22 100644 --- a/lnbits/extensions/admin/__init__.py +++ b/lnbits/extensions/admin/__init__.py @@ -7,6 +7,7 @@ db = Database("ext_admin") admin_ext: APIRouter = APIRouter(prefix="/admin", tags=["admin"]) + def admin_renderer(): return template_renderer(["lnbits/extensions/admin/templates"]) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 67fbc6143..0d7019ccf 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -11,13 +11,13 @@ from .models import Admin, Funding async def update_wallet_balance(wallet_id: str, amount: int) -> str: temp_id = f"temp_{urlsafe_short_hash()}" internal_id = f"internal_{urlsafe_short_hash()}" - + payment = await create_payment( wallet_id=wallet_id, checking_id=internal_id, payment_request="admin_internal", payment_hash="admin_internal", - amount=amount*1000, + amount=amount * 1000, memo="Admin top up", pending=False, ) @@ -25,6 +25,7 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str: await internal_invoice_queue.put(internal_id) return payment + async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) # print("UPDATE", q) @@ -35,23 +36,37 @@ async def update_admin(user: str, **kwargs) -> Admin: assert row, "Newly updated settings couldn't be retrieved" return Admin(**row) if row else None + async def get_admin() -> Admin: row = await db.fetchone("SELECT * FROM admin.admin") return Admin(**row) if row else None + async def update_funding(data: Funding) -> Funding: await db.execute( """ UPDATE admin.funding SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ? WHERE id = ? - """, - (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,), + """, + ( + data.backend_wallet, + data.endpoint, + data.port, + data.read_key, + data.invoice_key, + data.admin_key, + data.cert, + data.balance, + data.selected, + data.id, + ), ) row = await db.fetchone('SELECT * FROM admin.funding WHERE "id" = ?', (data.id,)) assert row, "Newly updated settings couldn't be retrieved" return Funding(**row) if row else None + async def get_funding() -> List[Funding]: rows = await db.fetchall("SELECT * FROM admin.funding") diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index f3663435c..388f5ec64 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -7,19 +7,21 @@ from lnbits.helpers import urlsafe_short_hash async def get_admin_user(): - if(conf.admin_users[0]): + if conf.admin_users[0]: return conf.admin_users[0] from lnbits.core.crud import create_account, get_user + print("Seems like there's no admin users yet. Let's create an account for you!") account = await create_account() user = account.id assert user, "Newly created user couldn't be retrieved" - print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.") + print( + f"Your newly created account/user id is: {user}. This will be the Super Admin user." + ) conf.admin_users.insert(0, user) return user - async def m001_create_admin_table(db): # users/server user = await get_admin_user() @@ -28,7 +30,7 @@ async def m001_create_admin_table(db): admin_ext = ",".join(conf.admin_ext) disabled_ext = ",".join(conf.disabled_ext) funding_source = conf.funding_source - #operational + # operational data_folder = conf.data_folder database_url = conf.database_url force_https = conf.force_https diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 3d8efdcd8..6e95d68fb 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -28,6 +28,7 @@ class UpdateAdminSettings(BaseModel): custom_logo: str = Query(None) ad_space: str = Query(None) + class Admin(BaseModel): # users user: str @@ -59,6 +60,7 @@ class Admin(BaseModel): data = dict(row) return cls(**data) + class Funding(BaseModel): id: str backend_wallet: str diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py index 24b8ca859..ceda5192a 100644 --- a/lnbits/extensions/admin/views.py +++ b/lnbits/extensions/admin/views.py @@ -16,19 +16,21 @@ from .crud import get_admin, get_funding templates = Jinja2Templates(directory="templates") + @admin_ext.get("/", response_class=HTMLResponse) async def index(request: Request, user: User = Depends(check_user_exists)): admin = await get_admin() funding = [f.dict() for f in await get_funding()] error, balance = await g().WALLET.status() - + return admin_renderer().TemplateResponse( - "admin/index.html", { + "admin/index.html", + { "request": request, "user": user.dict(), "admin": admin.dict(), "funding": funding, "settings": g().admin_conf.dict(), - "balance": balance - } + "balance": balance, + }, ) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index c0650c8a9..784ad97fa 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -15,16 +15,18 @@ from .crud import get_admin, update_admin, update_funding, update_wallet_balance @admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK) -async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)): +async def api_update_balance( + wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key) +): try: wallet = await get_wallet(wallet_id) except: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" - ) + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount)) - + return {"status": "Success"} @@ -32,39 +34,40 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D async def api_update_admin( request: Request, data: UpdateAdminSettings = Body(...), - w: WalletTypeInfo = Depends(require_admin_key) - ): + w: WalletTypeInfo = Depends(require_admin_key), +): admin = await get_admin() # print(data) if not admin.user == w.wallet.user: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" - ) + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) updated = await update_admin(user=w.wallet.user, **data.dict()) - updated.admin_users = removeEmptyString(updated.admin_users.split(',')) - updated.allowed_users = removeEmptyString(updated.allowed_users.split(',')) - updated.admin_ext = removeEmptyString(updated.admin_ext.split(',')) - updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(',')) - updated.theme = removeEmptyString(updated.theme.split(',')) - updated.ad_space = removeEmptyString(updated.ad_space.split(',')) + updated.admin_users = removeEmptyString(updated.admin_users.split(",")) + updated.allowed_users = removeEmptyString(updated.allowed_users.split(",")) + updated.admin_ext = removeEmptyString(updated.admin_ext.split(",")) + updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(",")) + updated.theme = removeEmptyString(updated.theme.split(",")) + updated.ad_space = removeEmptyString(updated.ad_space.split(",")) g().admin_conf = g().admin_conf.copy(update=updated.dict()) - + # print(g().admin_conf) return {"status": "Success"} + @admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK) async def api_update_funding( request: Request, data: Funding = Body(...), - w: WalletTypeInfo = Depends(require_admin_key) - ): + w: WalletTypeInfo = Depends(require_admin_key), +): admin = await get_admin() if not admin.user == w.wallet.user: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" - ) + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) funding = await update_funding(data=data) return funding From c319b84d7299eb5f7d942214fab2f21deffb38f6 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 21 Sep 2022 15:28:13 +0100 Subject: [PATCH 043/614] Had to add a couple of tries --- lnbits/core/views/generic.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 83648c444..63f7af681 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -133,12 +133,19 @@ async def wallet( return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "User does not exist."} ) - if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS: - return template_renderer().TemplateResponse( - "error.html", {"request": request, "err": "User not authorized."} - ) - if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS: - user.admin = True + try: + if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS: + return template_renderer().TemplateResponse( + "error.html", {"request": request, "err": "User not authorized."} + ) + except: + pass + + try: + if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS: + user.admin = True + except: + pass if not wallet_id: if user.wallets and not wallet_name: # type: ignore wallet = user.wallets[0] # type: ignore From d8a13ed29d323f5233c6d9ba5cb59f9ef352d90c Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 21 Sep 2022 15:31:31 +0100 Subject: [PATCH 044/614] Added couple more tries --- lnbits/decorators.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index f951163f8..a810892d9 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -239,13 +239,16 @@ async def check_user_exists(usr: UUID4) -> User: if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users - - if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: - raise HTTPException( - status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." - ) - - if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: - g().user.admin = True - + try: + if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: + raise HTTPException( + status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." + ) + except: + pass + try: + if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: + g().user.admin = True + except: + pass return g().user From 55a44030283b34b6d61c0d441b779b135b449c3a Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 21 Sep 2022 15:35:06 +0100 Subject: [PATCH 045/614] Reverted try --- lnbits/decorators.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index a810892d9..904ca1c25 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -239,16 +239,12 @@ async def check_user_exists(usr: UUID4) -> User: if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users - try: - if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: - raise HTTPException( - status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." - ) - except: - pass - try: - if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: - g().user.admin = True + if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: + raise HTTPException( + status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." + ) + if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: + g().user.admin = True except: pass return g().user From a932f8a3d0c43692f6ec2b1ae9284c279ab7396c Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 21 Sep 2022 15:37:07 +0100 Subject: [PATCH 046/614] reverted other try --- lnbits/core/views/generic.py | 19 ++++++------------- lnbits/decorators.py | 2 -- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 63f7af681..83648c444 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -133,19 +133,12 @@ async def wallet( return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "User does not exist."} ) - try: - if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS: - return template_renderer().TemplateResponse( - "error.html", {"request": request, "err": "User not authorized."} - ) - except: - pass - - try: - if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS: - user.admin = True - except: - pass + if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS: + return template_renderer().TemplateResponse( + "error.html", {"request": request, "err": "User not authorized."} + ) + if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS: + user.admin = True if not wallet_id: if user.wallets and not wallet_name: # type: ignore wallet = user.wallets[0] # type: ignore diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 904ca1c25..dd26d8feb 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -245,6 +245,4 @@ async def check_user_exists(usr: UUID4) -> User: ) if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: g().user.admin = True - except: - pass return g().user From c32e0cbecb147918b2c95ad29bcaf8876855ac0b Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 21 Sep 2022 18:40:46 +0100 Subject: [PATCH 047/614] fix main merge missing settings --- lnbits/app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lnbits/app.py b/lnbits/app.py index 950b61407..49ef3d1bd 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -56,6 +56,11 @@ def create_app(config_object="lnbits.settings") -> FastAPI: "url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE", }, ) + if lnbits.settings.LNBITS_ADMIN_UI: + g().admin_conf = conf + check_settings(app) + + g().WALLET = WALLET app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static") app.mount( "/core/static", From e4c310d197fbb6ddb9a7d5528aab66fe60608313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 10:46:11 +0200 Subject: [PATCH 048/614] format --- lnbits/app.py | 22 +++++++++++++--------- lnbits/commands.py | 6 +++++- lnbits/config.py | 25 ++++++++++++++++++------- lnbits/core/views/generic.py | 1 + lnbits/decorators.py | 2 +- lnbits/helpers.py | 8 +++++--- 6 files changed, 43 insertions(+), 21 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 49ef3d1bd..00ed6d8dc 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -115,24 +115,28 @@ def create_app(config_object="lnbits.settings") -> FastAPI: return app + def check_settings(app: FastAPI): @app.on_event("startup") async def check_settings_admin(): while True: admin_set = await get_admin_settings() - if admin_set : + if admin_set: break print("Waiting for admin settings... retrying in 5 seconds!") await asyncio.sleep(5) - - admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(',')) - admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(',')) - admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(',')) - admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(',')) - admin_set.theme = removeEmptyString(admin_set.theme.split(',')) - admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(',')) + + admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(",")) + admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(",")) + admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(",")) + admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(",")) + admin_set.theme = removeEmptyString(admin_set.theme.split(",")) + admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(",")) g().admin_conf = conf.copy(update=admin_set.dict()) - print(f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}") + print( + f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}" + ) + def check_funding_source(app: FastAPI) -> None: @app.on_event("startup") diff --git a/lnbits/commands.py b/lnbits/commands.py index 763a5b90b..86868f1f7 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -5,6 +5,7 @@ import re import warnings import click +from genericpath import exists from loguru import logger from .core import db as core_db @@ -52,6 +53,7 @@ def bundle_vendored(): with open(outputpath, "w") as f: f.write(output) + async def get_admin_settings(): from lnbits.extensions.admin.models import Admin @@ -61,6 +63,7 @@ async def get_admin_settings(): return False async with ext_db.connect() as conn: + if conn.type == SQLITE: exists = await conn.fetchone( "SELECT * FROM sqlite_master WHERE type='table' AND name='admin'" @@ -69,7 +72,7 @@ async def get_admin_settings(): exists = await conn.fetchone( "SELECT * FROM information_schema.tables WHERE table_name = 'admin'" ) - + if not exists: return False @@ -77,6 +80,7 @@ async def get_admin_settings(): return Admin(**row) if row else None + async def migrate_databases(): """Creates the necessary databases if they don't exist already; or migrates them.""" diff --git a/lnbits/config.py b/lnbits/config.py index 37b700fde..cf26ad21e 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -6,17 +6,19 @@ from typing import List, Optional from pydantic import BaseSettings, Field wallets_module = importlib.import_module("lnbits.wallets") -wallet_class = getattr( +wallet_class = getattr( wallets_module, getenv("LNBITS_BACKEND_WALLET_CLASS", "VoidWallet") ) WALLET = wallet_class() + def list_parse_fallback(v): try: return json.loads(v) except Exception as e: - return v.replace(' ','').split(',') + return v.replace(" ", "").split(",") + class Settings(BaseSettings): admin_ui: bool = Field(default=True, env="LNBITS_ADMIN_UI") @@ -24,7 +26,9 @@ class Settings(BaseSettings): admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS") allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS") admin_ext: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_EXTENSIONS") - disabled_ext: List[str] = Field(default_factory=list, env="LNBITS_DISABLED_EXTENSIONS") + disabled_ext: List[str] = Field( + default_factory=list, env="LNBITS_DISABLED_EXTENSIONS" + ) funding_source: str = Field(default="VoidWallet", env="LNBITS_BACKEND_WALLET_CLASS") # ops data_folder: str = Field(default=None, env="LNBITS_DATA_FOLDER") @@ -37,10 +41,17 @@ class Settings(BaseSettings): denomination: str = Field(default="sats", env="LNBITS_DENOMINATION") # Change theme site_title: str = Field(default="LNbits", env="LNBITS_SITE_TITLE") - site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE") + site_tagline: str = Field( + default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE" + ) site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") - default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME") - theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS") + default_wallet_name: str = Field( + default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME" + ) + theme: List[str] = Field( + default=["classic, flamingo, mint, salvador, monochrome, autumn"], + env="LNBITS_THEME_OPTIONS", + ) custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env @@ -48,7 +59,7 @@ class Settings(BaseSettings): debug: Optional[str] host: Optional[str] port: Optional[str] - lnbits_path: Optional[str] = path.dirname(path.realpath(__file__)) + lnbits_path: Optional[str] = path.dirname(path.realpath(__file__)) # @validator('admin_users', 'allowed_users', 'admin_ext', 'disabled_ext', pre=True) # def validate(cls, val): diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 83648c444..3a1fbdfcf 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -39,6 +39,7 @@ from ..services import pay_invoice, redeem_lnurl_withdraw core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"]) + @core_html_routes.get("/favicon.ico", response_class=FileResponse) async def favicon(): return FileResponse("lnbits/core/static/favicon.ico") diff --git a/lnbits/decorators.py b/lnbits/decorators.py index dd26d8feb..58b025aa3 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -235,7 +235,7 @@ async def check_user_exists(usr: UUID4) -> User: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="User does not exist." ) - + if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users diff --git a/lnbits/helpers.py b/lnbits/helpers.py index 7bd1b54a9..f4255c866 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -157,11 +157,13 @@ def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> s url = f"{base}{endpoint}{url_params}" return url + def removeEmptyString(arr): return list(filter(None, arr)) + def template_renderer(additional_folders: List = []) -> Jinja2Templates: - if(settings.LNBITS_ADMIN_UI): + if settings.LNBITS_ADMIN_UI: _ = g().admin_conf settings.LNBITS_AD_SPACE = _.ad_space settings.LNBITS_HIDE_API = _.hide_api @@ -170,8 +172,8 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates: settings.LNBITS_SITE_TAGLINE = _.site_tagline settings.LNBITS_SITE_DESCRIPTION = _.site_description settings.LNBITS_THEME_OPTIONS = _.theme - settings.LNBITS_CUSTOM_LOGO = _.custom_logo - + settings.LNBITS_CUSTOM_LOGO = _.custom_logo + t = Jinja2Templates( loader=jinja2.FileSystemLoader( ["lnbits/templates", "lnbits/core/templates", *additional_folders] From 6c2a9b2258087302487716e3d35b1b2387bb58ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 11:47:24 +0200 Subject: [PATCH 049/614] format black --- lnbits/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/app.py b/lnbits/app.py index 00ed6d8dc..6ae75d7ab 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -56,6 +56,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI: "url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE", }, ) + if lnbits.settings.LNBITS_ADMIN_UI: g().admin_conf = conf check_settings(app) From 7c05d4c354be10d502b7c453f96902f76f9c15d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 15:29:12 +0200 Subject: [PATCH 050/614] fix AD_SPACE --- lnbits/core/templates/core/wallet.html | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index bccdc2b48..189b060b2 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -385,12 +385,9 @@ - {% endif %} {% if AD_SPACE %} {% for ADS in AD_SPACE %} {% set AD = - ADS.split(';') %} + {% endif %} {% if AD_SPACE %} {% for AD in AD_SPACE %} - {% endfor %} {% endif %}
From 415165c6fe7ec2affda20078c81e12b4fc2944de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 15:55:37 +0200 Subject: [PATCH 051/614] fix some javascript errors when adding users --- lnbits/extensions/admin/templates/admin/index.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index d34b90683..1e881cb69 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1221,7 +1221,7 @@ addAdminUser() { let addUser = this.data.admin_users_add let admin_users = this.data.admin.admin_users - if (addUser.length && !admin_users.includes(addUser)) { + if (addUser && addUser.length && !admin_users.includes(addUser)) { admin_users.push(addUser) this.data.admin.admin_users = admin_users this.data.admin_users_add = '' @@ -1234,7 +1234,7 @@ addAllowedUser() { let addUser = this.data.allowed_users_add let allowed_users = this.data.admin.allowed_users - if (addUser.length && !allowed_users.includes(addUser)) { + if (addUser && addUser.length && !allowed_users.includes(addUser)) { allowed_users.push(addUser) this.data.admin.allowed_users = allowed_users this.data.allowed_users_add = '' @@ -1336,7 +1336,6 @@ custom_logo, ad_space } = this.data.admin - //console.log("this", this.data.admin) let data = { admin_users: admin_users.toString(), allowed_users: allowed_users.toString(), From 850ed85311fea5c5e10bca8cdeaaa0a3dffbe71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 16:04:55 +0200 Subject: [PATCH 052/614] fix ADMIN_UI=false errors --- lnbits/core/views/generic.py | 3 +++ lnbits/decorators.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 3a1fbdfcf..db4fac430 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -124,6 +124,9 @@ async def wallet( if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users + else: + LNBITS_ADMIN_USERS = [] + LNBITS_ALLOWED_USERS = [] if not user_id: user = await get_user((await create_account()).id) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 58b025aa3..5a3c0a5c3 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -141,6 +141,8 @@ async def get_key_type( if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users + else: + LNBITS_ADMIN_USERS = [] for typenr, WalletChecker in zip( [0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker] @@ -239,6 +241,10 @@ async def check_user_exists(usr: UUID4) -> User: if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users + else: + LNBITS_ADMIN_USERS = [] + LNBITS_ALLOWED_USERS = [] + if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: raise HTTPException( status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." From 393d1a8204f04498aca712604f5646dcb30d7746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 16:14:17 +0200 Subject: [PATCH 053/614] prettier --- lnbits/core/templates/core/wallet.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index 189b060b2..22c65bf3e 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -386,8 +386,7 @@ {% endif %} {% if AD_SPACE %} {% for AD in AD_SPACE %} - - {% endfor %} {% endif %} From 622d0f50da4b319765175c97613ab31e5e1dd722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 18:35:47 +0200 Subject: [PATCH 054/614] fix migration tests --- lnbits/extensions/admin/migrations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 388f5ec64..196c9fc07 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -7,7 +7,7 @@ from lnbits.helpers import urlsafe_short_hash async def get_admin_user(): - if conf.admin_users[0]: + if len(conf.admin_users) > 0: return conf.admin_users[0] from lnbits.core.crud import create_account, get_user From 26769d94984c5d7c4f56b1bd06ca4ce3e376453c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 26 Sep 2022 16:54:19 +0200 Subject: [PATCH 055/614] change comments to use multiple lines --- .env.example | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 7a49d5c5d..c1ac74977 100644 --- a/.env.example +++ b/.env.example @@ -3,11 +3,15 @@ PORT=5000 DEBUG=false -LNBITS_ADMIN_USERS="" # User IDs seperated by comma -LNBITS_ADMIN_EXTENSIONS="ngrok, admin" # Extensions only admin can access -LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available +# User IDs seperated by comma +LNBITS_ADMIN_USERS="" +# Extensions only admin can access +LNBITS_ADMIN_EXTENSIONS="ngrok, admin" +# Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available +LNBITS_ADMIN_UI=false -LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma +# Restricts access, User IDs seperated by comma +LNBITS_ALLOWED_USERS="" LNBITS_DEFAULT_WALLET_NAME="LNbits wallet" From cadb6b21617c77fac36ea55937a03c223ec5aa56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 26 Sep 2022 16:59:30 +0200 Subject: [PATCH 056/614] fix merge error --- lnbits/app.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 6ae75d7ab..60c090380 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -57,10 +57,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: }, ) - if lnbits.settings.LNBITS_ADMIN_UI: - g().admin_conf = conf - check_settings(app) - g().WALLET = WALLET app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static") app.mount( From a78ebbacd700c7975662424e07bb042a2da380f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 27 Sep 2022 14:14:36 +0200 Subject: [PATCH 057/614] use logger in app.py --- lnbits/app.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 60c090380..d7bd3ea68 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -31,8 +31,6 @@ from .helpers import ( url_for_vendored, ) from .requestvars import g - -# from .settings import WALLET from .tasks import ( catch_everything_and_restart, check_pending_payments, @@ -120,7 +118,7 @@ def check_settings(app: FastAPI): admin_set = await get_admin_settings() if admin_set: break - print("Waiting for admin settings... retrying in 5 seconds!") + logger.info("Waiting for admin settings... retrying in 5 seconds!") await asyncio.sleep(5) admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(",")) @@ -130,7 +128,7 @@ def check_settings(app: FastAPI): admin_set.theme = removeEmptyString(admin_set.theme.split(",")) admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(",")) g().admin_conf = conf.copy(update=admin_set.dict()) - print( + logger.info( f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}" ) From 8be9b950fdff51e8c9d94d4123cae0dfb650dd3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 27 Sep 2022 14:14:57 +0200 Subject: [PATCH 058/614] fix config --- lnbits/config.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lnbits/config.py b/lnbits/config.py index cf26ad21e..874effae2 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -17,7 +17,11 @@ def list_parse_fallback(v): try: return json.loads(v) except Exception as e: - return v.replace(" ", "").split(",") + replaced = v.replace(" ", "") + if replaced: + return replaced.split(",") + else: + return [] class Settings(BaseSettings): @@ -48,10 +52,7 @@ class Settings(BaseSettings): default_wallet_name: str = Field( default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME" ) - theme: List[str] = Field( - default=["classic, flamingo, mint, salvador, monochrome, autumn"], - env="LNBITS_THEME_OPTIONS", - ) + theme: List[str] = Field(default_factory=list, env="LNBITS_THEME_OPTIONS") custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env @@ -74,4 +75,5 @@ class Settings(BaseSettings): conf = Settings() +print(conf) WALLET = wallet_class() From f2e494384e96587b0b0fb2a9de8825aa9115ee21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 27 Sep 2022 14:17:20 +0200 Subject: [PATCH 059/614] concatenate first migrations script fixup --- lnbits/config.py | 1 - lnbits/extensions/admin/migrations.py | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lnbits/config.py b/lnbits/config.py index 874effae2..fe8dabf9c 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -75,5 +75,4 @@ class Settings(BaseSettings): conf = Settings() -print(conf) WALLET = wallet_class() diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 196c9fc07..2d48a8e4c 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -23,6 +23,8 @@ async def get_admin_user(): async def m001_create_admin_table(db): + + # users/server user = await get_admin_user() admin_users = ",".join(conf.admin_users) @@ -78,7 +80,7 @@ async def m001_create_admin_table(db): await db.execute( """ INSERT INTO admin.admin ( - "user", + "user", admin_users, allowed_users, admin_ext, @@ -126,9 +128,6 @@ async def m001_create_admin_table(db): ), ) - -async def m001_create_funding_table(db): - funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS") # Make the funding table, if it does not already exist From 9757fad8684d166ce9ac04e6a69b0ff820c4de61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 27 Sep 2022 16:37:08 +0200 Subject: [PATCH 060/614] add restart button to frontend --- .../admin/templates/admin/index.html | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 1e881cb69..319ca3f0d 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -65,6 +65,14 @@ :options="data.funding_source" >
+
+ +

Fee reserve

@@ -89,7 +97,7 @@ >
-
+ @@ -1257,6 +1265,24 @@ let spaces = this.data.admin.ad_space this.data.admin.ad_space = spaces.filter(s => s !== ad) }, + restartServer() { + LNbits.api + .request( + 'GET', + '/admin/api/v1/admin/restart/', + this.g.user.wallets[0].adminkey + ) + .then(response => { + this.$q.notify({ + type: 'positive', + message: 'Success! Restarted Server', + icon: null + }) + }) + .catch(function (error) { + LNbits.utils.notifyApiError(error) + }) + }, topupWallet() { LNbits.api .request( From 66739f4246bdc6d3b347f54f675c00937601935e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 3 Oct 2022 16:34:52 +0200 Subject: [PATCH 061/614] make extension use new settings --- lnbits/extensions/boltz/boltz.py | 14 ++++++-------- lnbits/extensions/boltz/mempool.py | 15 ++++++--------- lnbits/extensions/boltz/views_api.py | 4 ++-- lnbits/extensions/lndhub/views_api.py | 4 ++-- lnbits/extensions/tpos/views.py | 14 +++++++------- 5 files changed, 23 insertions(+), 28 deletions(-) diff --git a/lnbits/extensions/boltz/boltz.py b/lnbits/extensions/boltz/boltz.py index ac99d4f4a..424d0bf78 100644 --- a/lnbits/extensions/boltz/boltz.py +++ b/lnbits/extensions/boltz/boltz.py @@ -12,7 +12,7 @@ from loguru import logger from lnbits.core.services import create_invoice, pay_invoice from lnbits.helpers import urlsafe_short_hash -from lnbits.settings import BOLTZ_NETWORK, BOLTZ_URL +from lnbits.settings import settings from .crud import update_swap_status from .mempool import ( @@ -33,9 +33,7 @@ from .models import ( ) from .utils import check_balance, get_timestamp, req_wrap -net = NETWORKS[BOLTZ_NETWORK] -logger.trace(f"BOLTZ_URL: {BOLTZ_URL}") -logger.trace(f"Bitcoin Network: {net['name']}") +net = NETWORKS[settings.boltz_network] async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap: @@ -62,7 +60,7 @@ async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap: res = req_wrap( "post", - f"{BOLTZ_URL}/createswap", + f"{settings.boltz_url}/createswap", json={ "type": "submarine", "pairId": "BTC/BTC", @@ -129,7 +127,7 @@ async def create_reverse_swap( res = req_wrap( "post", - f"{BOLTZ_URL}/createswap", + f"{settings.boltz_url}/createswap", json={ "type": "reversesubmarine", "pairId": "BTC/BTC", @@ -409,7 +407,7 @@ def check_boltz_limits(amount): def get_boltz_pairs(): res = req_wrap( "get", - f"{BOLTZ_URL}/getpairs", + f"{settings.boltz_url}/getpairs", headers={"Content-Type": "application/json"}, ) return res.json() @@ -418,7 +416,7 @@ def get_boltz_pairs(): def get_boltz_status(boltzid): res = req_wrap( "post", - f"{BOLTZ_URL}/swapstatus", + f"{settings.boltz_url}/swapstatus", json={"id": boltzid}, ) return res.json() diff --git a/lnbits/extensions/boltz/mempool.py b/lnbits/extensions/boltz/mempool.py index a44c0f02c..a64cadad0 100644 --- a/lnbits/extensions/boltz/mempool.py +++ b/lnbits/extensions/boltz/mempool.py @@ -7,14 +7,11 @@ import websockets from embit.transaction import Transaction from loguru import logger -from lnbits.settings import BOLTZ_MEMPOOL_SPACE_URL, BOLTZ_MEMPOOL_SPACE_URL_WS +from lnbits.settings import settings from .utils import req_wrap -logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL: {BOLTZ_MEMPOOL_SPACE_URL}") -logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL_WS: {BOLTZ_MEMPOOL_SPACE_URL_WS}") - -websocket_url = f"{BOLTZ_MEMPOOL_SPACE_URL_WS}/api/v1/ws" +websocket_url = f"{settings.boltz_mempool_space_url_ws}/api/v1/ws" async def wait_for_websocket_message(send, message_string): @@ -33,7 +30,7 @@ async def wait_for_websocket_message(send, message_string): def get_mempool_tx(address): res = req_wrap( "get", - f"{BOLTZ_MEMPOOL_SPACE_URL}/api/address/{address}/txs", + f"{settings.boltz_mempool_space_url}/api/address/{address}/txs", headers={"Content-Type": "text/plain"}, ) txs = res.json() @@ -70,7 +67,7 @@ def get_fee_estimation() -> int: def get_mempool_fees() -> int: res = req_wrap( "get", - f"{BOLTZ_MEMPOOL_SPACE_URL}/api/v1/fees/recommended", + f"{settings.boltz_mempool_space_url}/api/v1/fees/recommended", headers={"Content-Type": "text/plain"}, ) fees = res.json() @@ -80,7 +77,7 @@ def get_mempool_fees() -> int: def get_mempool_blockheight() -> int: res = req_wrap( "get", - f"{BOLTZ_MEMPOOL_SPACE_URL}/api/blocks/tip/height", + f"{settings.boltz_mempool_space_url}/api/blocks/tip/height", headers={"Content-Type": "text/plain"}, ) return int(res.text) @@ -91,7 +88,7 @@ async def send_onchain_tx(tx: Transaction): logger.debug(f"Boltz - mempool sending onchain tx...") req_wrap( "post", - f"{BOLTZ_MEMPOOL_SPACE_URL}/api/tx", + f"{settings.boltz_mempool_space_url}/api/tx", headers={"Content-Type": "text/plain"}, content=raw, ) diff --git a/lnbits/extensions/boltz/views_api.py b/lnbits/extensions/boltz/views_api.py index a4b7d3181..18ca14cb7 100644 --- a/lnbits/extensions/boltz/views_api.py +++ b/lnbits/extensions/boltz/views_api.py @@ -14,7 +14,7 @@ from starlette.requests import Request from lnbits.core.crud import get_user from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key -from lnbits.settings import BOLTZ_MEMPOOL_SPACE_URL +from lnbits.settings import settings from . import boltz_ext from .boltz import ( @@ -55,7 +55,7 @@ from .utils import check_balance response_model=str, ) async def api_mempool_url(): - return BOLTZ_MEMPOOL_SPACE_URL + return settings.boltz_mempool_space_url # NORMAL SWAP diff --git a/lnbits/extensions/lndhub/views_api.py b/lnbits/extensions/lndhub/views_api.py index 8cbe5a6bf..b2328c392 100644 --- a/lnbits/extensions/lndhub/views_api.py +++ b/lnbits/extensions/lndhub/views_api.py @@ -12,7 +12,7 @@ from lnbits import bolt11 from lnbits.core.crud import delete_expired_invoices, get_payments from lnbits.core.services import create_invoice, pay_invoice from lnbits.decorators import WalletTypeInfo -from lnbits.settings import LNBITS_SITE_TITLE, WALLET +from lnbits.settings import WALLET, settings from . import lndhub_ext from .decorators import check_wallet, require_admin_key @@ -56,7 +56,7 @@ async def lndhub_addinvoice( _, pr = await create_invoice( wallet_id=wallet.wallet.id, amount=int(data.amt), - memo=data.memo or LNBITS_SITE_TITLE, + memo=data.memo or settings.lnbits_site_title, extra={"tag": "lndhub"}, ) except: diff --git a/lnbits/extensions/tpos/views.py b/lnbits/extensions/tpos/views.py index e1f1d21e3..dac129a93 100644 --- a/lnbits/extensions/tpos/views.py +++ b/lnbits/extensions/tpos/views.py @@ -8,7 +8,7 @@ from starlette.responses import HTMLResponse from lnbits.core.models import User from lnbits.decorators import check_user_exists -from lnbits.settings import LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE +from lnbits.settings import settings from . import tpos_ext, tpos_renderer from .crud import get_tpos @@ -50,12 +50,12 @@ async def manifest(tpos_id: str): ) return { - "short_name": LNBITS_SITE_TITLE, - "name": tpos.name + " - " + LNBITS_SITE_TITLE, + "short_name": settings.lnbits_site_title, + "name": tpos.name + " - " + settings.lnbits_site_title, "icons": [ { - "src": LNBITS_CUSTOM_LOGO - if LNBITS_CUSTOM_LOGO + "src": settings.lnbits_custom_logo + if settings.lnbits_custom_logo else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png", "type": "image/png", "sizes": "900x900", @@ -69,9 +69,9 @@ async def manifest(tpos_id: str): "theme_color": "#1F2234", "shortcuts": [ { - "name": tpos.name + " - " + LNBITS_SITE_TITLE, + "name": tpos.name + " - " + settings.lnbits_site_title, "short_name": tpos.name, - "description": tpos.name + " - " + LNBITS_SITE_TITLE, + "description": tpos.name + " - " + settings.lnbits_site_title, "url": "/tpos/" + tpos_id, } ], From 2b2884142fd9c2ac6fbfa3cbd8916aaf2225068a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 3 Oct 2022 16:35:26 +0200 Subject: [PATCH 062/614] remove enviroms --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 19dac8600..7a69ec430 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,6 @@ charset-normalizer = "2.0.6" click = "8.0.1" ecdsa = "0.17.0" embit = "0.4.9" -environs = "9.3.3" fastapi = "0.78.0" h11 = "0.12.0" httpcore = "0.15.0" From 840ede1bd66fd437e342bbefc0880de6fc2f216c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 3 Oct 2022 16:36:14 +0200 Subject: [PATCH 063/614] fix admin --- lnbits/extensions/admin/crud.py | 24 +- lnbits/extensions/admin/migrations.py | 337 +---- lnbits/extensions/admin/models.py | 58 +- .../admin/templates/admin/_tab_funding.html | 158 +++ .../admin/templates/admin/_tab_server.html | 78 ++ .../admin/templates/admin/_tab_theme.html | 122 ++ .../admin/templates/admin/_tab_users.html | 96 ++ .../admin/templates/admin/index.html | 1133 +---------------- lnbits/extensions/admin/views.py | 16 +- lnbits/extensions/admin/views_api.py | 28 +- 10 files changed, 576 insertions(+), 1474 deletions(-) create mode 100644 lnbits/extensions/admin/templates/admin/_tab_funding.html create mode 100644 lnbits/extensions/admin/templates/admin/_tab_server.html create mode 100644 lnbits/extensions/admin/templates/admin/_tab_theme.html create mode 100644 lnbits/extensions/admin/templates/admin/_tab_users.html diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 0d7019ccf..e4cb5d77e 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -2,10 +2,11 @@ from typing import List from lnbits.core.crud import create_payment from lnbits.helpers import urlsafe_short_hash +from lnbits.settings import Settings from lnbits.tasks import internal_invoice_queue from . import db -from .models import Admin, Funding +from .models import Funding async def update_wallet_balance(wallet_id: str, amount: int) -> str: @@ -23,26 +24,26 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str: ) # manually send this for now await internal_invoice_queue.put(internal_id) - return payment -async def update_admin(user: str, **kwargs) -> Admin: +async def update_settings(user: str, **kwargs) -> Settings: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) # print("UPDATE", q) await db.execute( - f'UPDATE admin.admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) + f'UPDATE admin.settings SET {q} WHERE "user" = ?', (*kwargs.values(), user) ) - row = await db.fetchone('SELECT * FROM admin.admin WHERE "user" = ?', (user,)) + row = await db.fetchone('SELECT * FROM admin.settings WHERE "user" = ?', (user,)) assert row, "Newly updated settings couldn't be retrieved" - return Admin(**row) if row else None - - -async def get_admin() -> Admin: - row = await db.fetchone("SELECT * FROM admin.admin") - return Admin(**row) if row else None + return Settings(**row) if row else None async def update_funding(data: Funding) -> Funding: + await db.execute( + """ + UPDATE admin.settings SET funding_source = ? WHERE user = ? + """, + (data.backend_wallet, data.user), + ) await db.execute( """ UPDATE admin.funding @@ -69,5 +70,4 @@ async def update_funding(data: Funding) -> Funding: async def get_funding() -> List[Funding]: rows = await db.fetchall("SELECT * FROM admin.funding") - return [Funding(**row) for row in rows] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 2d48a8e4c..8f6c76a0e 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -1,292 +1,57 @@ -from os import getenv - -from sqlalchemy.exc import OperationalError # type: ignore - -from lnbits.config import conf -from lnbits.helpers import urlsafe_short_hash - - -async def get_admin_user(): - if len(conf.admin_users) > 0: - return conf.admin_users[0] - from lnbits.core.crud import create_account, get_user - - print("Seems like there's no admin users yet. Let's create an account for you!") - account = await create_account() - user = account.id - assert user, "Newly created user couldn't be retrieved" - print( - f"Your newly created account/user id is: {user}. This will be the Super Admin user." - ) - conf.admin_users.insert(0, user) - return user - - -async def m001_create_admin_table(db): - - - # users/server - user = await get_admin_user() - admin_users = ",".join(conf.admin_users) - allowed_users = ",".join(conf.allowed_users) - admin_ext = ",".join(conf.admin_ext) - disabled_ext = ",".join(conf.disabled_ext) - funding_source = conf.funding_source - # operational - data_folder = conf.data_folder - database_url = conf.database_url - force_https = conf.force_https - reserve_fee_min = conf.reserve_fee_min - reserve_fee_pct = conf.reserve_fee_pct - service_fee = conf.service_fee - hide_api = conf.hide_api - denomination = conf.denomination - # Theme'ing - site_title = conf.site_title - site_tagline = conf.site_tagline - site_description = conf.site_description - default_wallet_name = conf.default_wallet_name - theme = ",".join(conf.theme) - custom_logo = conf.custom_logo - ad_space = ",".join(conf.ad_space) - +async def m001_create_admin_settings_table(db): await db.execute( """ - CREATE TABLE IF NOT EXISTS admin.admin ( - "user" TEXT PRIMARY KEY, - admin_users TEXT, - allowed_users TEXT, - admin_ext TEXT, - disabled_ext TEXT, - funding_source TEXT, - data_folder TEXT, - database_url TEXT, - force_https BOOLEAN, - reserve_fee_min INT, - reserve_fee_pct REAL, - service_fee REAL, - hide_api BOOLEAN, - denomination TEXT, - site_title TEXT, - site_tagline TEXT, - site_description TEXT, - default_wallet_name TEXT, - theme TEXT, - custom_logo TEXT, - ad_space TEXT - ); - """ - ) - await db.execute( - """ - INSERT INTO admin.admin ( - "user", - admin_users, - allowed_users, - admin_ext, - disabled_ext, - funding_source, - data_folder, - database_url, - force_https, - reserve_fee_min, - reserve_fee_pct, - service_fee, - hide_api, - denomination, - site_title, - site_tagline, - site_description, - default_wallet_name, - theme, - custom_logo, - ad_space) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - user, - admin_users, - allowed_users, - admin_ext, - disabled_ext, - funding_source, - data_folder, - database_url, - force_https, - reserve_fee_min, - reserve_fee_pct, - service_fee, - hide_api, - denomination, - site_title, - site_tagline, - site_description, - default_wallet_name, - theme, - custom_logo, - ad_space, - ), - ) - - funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS") - - # Make the funding table, if it does not already exist - await db.execute( - """ - CREATE TABLE IF NOT EXISTS admin.funding ( - id TEXT PRIMARY KEY, - backend_wallet TEXT, - endpoint TEXT, + CREATE TABLE IF NOT EXISTS admin.settings ( + lnbits_admin_ui TEXT, + debug TEXT, + host TEXT, port INT, - read_key TEXT, - invoice_key TEXT, - admin_key TEXT, - cert TEXT, - balance INT, - selected INT + lnbits_path TEXT, + lnbits_commit TEXT, + lnbits_admin_users TEXT, + lnbits_allowed_users TEXT, + lnbits_allowed_funding_sources TEXT, + lnbits_admin_extensions TEXT, + lnbits_disabled_extensions TEXT, + lnbits_site_title TEXT, + lnbits_site_tagline TEXT, + lnbits_site_description TEXT, + lnbits_default_wallet_name TEXT, + lnbits_theme_options TEXT, + lnbits_custom_logo TEXT, + lnbits_ad_space TEXT, + lnbits_data_folder TEXT, + lnbits_database_url TEXT, + lnbits_force_https TEXT, + lnbits_reserve_fee_min TEXT, + lnbits_reserve_fee_percent TEXT, + lnbits_service_fee TEXT, + lnbits_hide_api TEXT, + lnbits_denomination TEXT, + lnbits_backend_wallet_class TEXT, + fake_wallet_secret TEXT, + lnbits_endpoint TEXT, + lnbits_key TEXT, + cliche_endpoint TEXT, + corelightning_rpc TEXT, + eclair_url TEXT, + eclair_pass TEXT, + lnd_rest_endpoint TEXT, + lnd_rest_cert TEXT, + lnd_rest_macaroon TEXT, + lnpay_api_endpoint TEXT, + lnpay_api_key TEXT, + lnpay_wallet_key TEXT, + lntxbot_api_endpoint TEXT, + lntxbot_key TEXT, + opennode_api_endpoint TEXT, + opennode_key TEXT, + spark_url TEXT, + spark_token TEXT, + boltz_network TEXT, + boltz_url TEXT, + boltz_mempool_space_url TEXT, + boltz_mempool_space_url_ws TEXT ); """ ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, selected) - VALUES (?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "CLightningWallet", - getenv("CLIGHTNING_RPC"), - 1 if funding_wallet == "CLightningWallet" else 0, - ), - ) - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "SparkWallet", - getenv("SPARK_URL"), - getenv("SPARK_TOKEN"), - 1 if funding_wallet == "SparkWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LnbitsWallet", - getenv("LNBITS_ENDPOINT"), - getenv("LNBITS_KEY"), - 1 if funding_wallet == "LnbitsWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, port, admin_key, cert, selected) - VALUES (?, ?, ?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LndWallet", - getenv("LND_GRPC_ENDPOINT"), - getenv("LND_GRPC_PORT"), - getenv("LND_GRPC_MACAROON"), - getenv("LND_GRPC_CERT"), - 1 if funding_wallet == "LndWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected) - VALUES (?, ?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LndRestWallet", - getenv("LND_REST_ENDPOINT"), - getenv("LND_REST_MACAROON"), - getenv("LND_REST_CERT"), - 1 if funding_wallet == "LndWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected) - VALUES (?, ?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LNPayWallet", - getenv("LNPAY_API_ENDPOINT"), - getenv("LNPAY_WALLET_KEY"), - getenv("LNPAY_API_KEY"), # this is going in as the cert - 1 if funding_wallet == "LNPayWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LntxbotWallet", - getenv("LNTXBOT_API_ENDPOINT"), - getenv("LNTXBOT_KEY"), - 1 if funding_wallet == "LntxbotWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "OpenNodeWallet", - getenv("OPENNODE_API_ENDPOINT"), - getenv("OPENNODE_KEY"), - 1 if funding_wallet == "OpenNodeWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "SparkWallet", - getenv("SPARK_URL"), - getenv("SPARK_TOKEN"), - 1 if funding_wallet == "SparkWallet" else 0, - ), - ) - - ## PLACEHOLDER FOR ECLAIR WALLET - # await db.execute( - # """ - # INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - # VALUES (?, ?, ?, ?, ?) - # """, - # ( - # urlsafe_short_hash(), - # "EclairWallet", - # getenv("ECLAIR_URL"), - # getenv("ECLAIR_PASS"), - # 1 if funding_wallet == "EclairWallet" else 0, - # ), - # ) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 6e95d68fb..ef57cadd3 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -29,36 +29,36 @@ class UpdateAdminSettings(BaseModel): ad_space: str = Query(None) -class Admin(BaseModel): - # users - user: str - admin_users: Optional[str] - allowed_users: Optional[str] - admin_ext: Optional[str] - disabled_ext: Optional[str] - funding_source: Optional[str] - # ops - data_folder: Optional[str] - database_url: Optional[str] - force_https: bool = Field(default=True) - reserve_fee_min: Optional[int] - reserve_fee_pct: Optional[float] - service_fee: float = Optional[float] - hide_api: bool = Field(default=False) - # Change theme - site_title: Optional[str] - site_tagline: Optional[str] - site_description: Optional[str] - default_wallet_name: Optional[str] - denomination: str = Field(default="sats") - theme: Optional[str] - custom_logo: Optional[str] - ad_space: Optional[str] +# class Admin(BaseModel): +# # users +# user: str +# admin_users: Optional[str] +# allowed_users: Optional[str] +# admin_ext: Optional[str] +# disabled_ext: Optional[str] +# funding_source: Optional[str] +# # ops +# data_folder: Optional[str] +# database_url: Optional[str] +# force_https: bool = Field(default=True) +# reserve_fee_min: Optional[int] +# reserve_fee_pct: Optional[float] +# service_fee: float = Optional[float] +# hide_api: bool = Field(default=False) +# # Change theme +# site_title: Optional[str] +# site_tagline: Optional[str] +# site_description: Optional[str] +# default_wallet_name: Optional[str] +# denomination: str = Field(default="sats") +# theme: Optional[str] +# custom_logo: Optional[str] +# ad_space: Optional[str] - @classmethod - def from_row(cls, row: Row) -> "Admin": - data = dict(row) - return cls(**data) +# @classmethod +# def from_row(cls, row: Row) -> "Admin": +# data = dict(row) +# return cls(**data) class Funding(BaseModel): diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html new file mode 100644 index 000000000..2ed0aae27 --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html @@ -0,0 +1,158 @@ + + +
Wallets Management
+
+
+
+
+

Funding Source Info

+
    + {%raw%} +
  • + Funding Source: {{data.settings.lnbits_backend_wallet_class}} +
  • +
  • Balance: {{data.balance / 1000}} sats
  • + {%endraw%} +
+
+
+
+
+
+
+
+

Active Funding (Requires server restart)

+ +
+
+ +
+
+
+

Fee reserve

+
+
+ +
+
+ +
+
+
+
+
+
+

TopUp a wallet

+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+

Funding Sources

+ {% raw %} + + + + + + + + + + + + + + {% endraw %} +
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_server.html b/lnbits/extensions/admin/templates/admin/_tab_server.html new file mode 100644 index 000000000..2924e6a40 --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/_tab_server.html @@ -0,0 +1,78 @@ + + +
Server Management
+
+
+
+
+

Server Info

+
    + {%raw%} +
  • + SQlite: {{data.settings.lnbits_data_folder}} +
  • +
  • + Postgres: {{data.settings.lnbits_database_url}} +
  • + {%endraw%} +
+
+
+
+
+
+

Service Fee

+ +
+
+
+

Miscelaneous

+ + + Force HTTPS + Prefer secure URLs + + + + + + + + Hide API + Hides wallet api, extensions can choose to honor + + + + + +
+
+
+
+ +
+ Save +
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_theme.html b/lnbits/extensions/admin/templates/admin/_tab_theme.html new file mode 100644 index 000000000..41dc04477 --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/_tab_theme.html @@ -0,0 +1,122 @@ + + +
UI Management
+
+
+
+
+

Site Title

+ +
+
+
+

Site Tagline

+ +
+
+
+
+

Site Description

+ +
+
+
+
+

Default Wallet Name

+ +
+
+
+

Denomination

+ +
+
+
+
+
+

Themes

+ +
+
+
+

Advertisement Slots

+ + + +
+ {% raw %} + + {{ space.slice(0, 8) + " ... " + space.slice(-8) }} + + {% endraw %} +
+
+
+
+
+
+

Custom Logo

+ +
+
+
+
+ +
+ Save +
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_users.html b/lnbits/extensions/admin/templates/admin/_tab_users.html new file mode 100644 index 000000000..3eb53ac48 --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/_tab_users.html @@ -0,0 +1,96 @@ + + +
User Management
+
+

+ Super Admin: {% raw %}{{this.data.settings.lnbits_admin_users[0]}}{% + endraw %} +

+
+
+

Admin Users

+ + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+

Allowed Users

+ + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+
+

Admin Extensions

+ +
+
+
+

Disabled Extensions

+ +
+
+
+
+ Save +
+
+
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 319ca3f0d..87e893215 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -29,1118 +29,16 @@ - - - -
Wallets Management
-
-
-
-
-

Funding Source Info

-
    - {%raw%} -
  • Funding Source: {{data.admin.funding_source}}
  • -
  • Balance: {{data.admin.balance / 1000}} sats
  • - {%endraw%} -
-
-
-
-
-
-
-
-

- Active Funding - (Requires server restart) -

- -
-
- -
-
-
-

Fee reserve

-
-
- -
-
- -
-
- -
-
-
-
- -

TopUp a wallet

-
-
- -
-
-
- -
-
-
- -
- -
-
-
-

Funding Sources

- {% raw %} - - - - - - - - - - - - - - {% endraw %} - -
-
-
- - -
User Management
-
-

- Super Admin: {% raw %}{{this.data.admin.user}}{% endraw %} -

-
-
-

Admin Users

- - - -
- {% raw %} - - {{ user }} - - {% endraw %} -
-
-
-
-

Allowed Users

- - - -
- {% raw %} - - {{ user }} - - {% endraw %} -
-
-
-
-
-

Admin Extensions

- -
-
-
-

Disabled Extensions

- -
-
-
-
- Save -
-
-
- - -
Server Management
-
-
-
-
-

Server Info

-
    - {%raw%} -
  • - SQlite: {{data.admin.data_folder}} -
  • -
  • - Postgres: {{data.admin.database_url}} -
  • - {%endraw%} -
-
-
-
-
-
-

Service Fee

- -
-
-
-

Miscelaneous

- - - Force HTTPS - Prefer secure URLs - - - - - - - - Hide API - Hides wallet api, extensions can choose to - honor - - - - - -
-
-
-
- -
- Save -
-
-
- - -
UI Management
-
-
-
-
-

Site Title

- -
-
-
-

Site Tagline

- -
-
-
-
-

Site Description

- -
-
-
-
-

Default Wallet Name

- -
-
-
-

Denomination

- -
-
-
-
-
-

Themes

- -
-
-
-

Advertisement Slots

- - - -
- {% raw %} - - {{ space.slice(0, 8) + " ... " + space.slice(-8) }} - - {% endraw %} -
-
-
-
-
-
-

Custom Logo

- -
-
-
-
- -
- Save -
-
-
+ {% include "admin/_tab_funding.html" %} {% include + "admin/_tab_users.html" %} {% include "admin/_tab_server.html" %} {% + include "admin/_tab_theme.html" %}
- - -
- -
{% endblock %} {% block scripts %} {{ window_vars(user) }} {% endblock %} From 3778990000848fc74c23e4fdab3696f73edda4b1 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Fri, 7 Oct 2022 19:24:07 +0100 Subject: [PATCH 104/614] make saving possible (possible will change in future) --- .../admin/templates/admin/index.html | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 72352651b..18df16a97 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -121,8 +121,11 @@ created: function () { this.settings = JSON.parse('{{ settings|tojson|safe }}') //DB data this.balance = +'{{ balance|safe }}' - this.formData = this.settings //model + this.formData = _.clone(this.settings) //model + //this.formData.lnbits_ad_space = "hdh" console.log(this.formData) + console.log(_.isEqual(this.settings, this.formData)) + }, methods: { addAdminUser() { @@ -206,18 +209,27 @@ }, updateSettings() { let data = { - ...this.settings, - ...this.formData + lnbits_backend_wallet_class: this.formData.lnbits_backend_wallet_class, + lnbits_admin_users: this.formData.lnbits_admin_users.toString(), + lnbits_allowed_users: this.formData.lnbits_allowed_users.toString(), + lnbits_admin_ext: this.formData.lnbits_admin_ext, + lnbits_disabled_ext: this.formData.lnbits_disabled_ext, + lnbits_funding_source: this.formData.lnbits_funding_source, + lnbits_force_https: this.formData.lnbits_force_https, + lnbits_reserve_fee_min: this.formData.lnbits_reserve_fee_min, + lnbits_reserve_fee_percent: this.formData.lnbits_reserve_fee_percent, + lnbits_service_fee: this.formData.lnbits_service_fee, + lnbits_hide_api: this.formData.lnbits_hide_api, + lnbits_site_title: this.formData.lnbits_site_title, + lnbits_site_tagline: this.formData.lnbits_site_tagline, + lnbits_site_description: this.formData.lnbits_site_description, + lnbits_default_wallet_name: this.formData.lnbits_default_wallet_name, + lnbits_denomination: this.formData.lnbits_denomination, + lnbits_theme: this.formData.lnbits_theme, + lnbits_custom_logo: this.formData.lnbits_custom_logo, + lnbits_ad_space: this.formData.lnbits_ad_space.toString() } - /* - const formElement = document.getElementById('settings_form') - const formData = new FormData(formElement) - const data = {} - formData.forEach((value, key) => (data[key] = value)) - // only for debugging - for (const [key, value] of formData) { - console.log(`${key}: ${value}\n`) - }*/ + console.log(data) LNbits.api .request( 'PUT', From 3fdafca0a15bb9931fbfe3a61b7a228c91b2d855 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Fri, 7 Oct 2022 19:44:03 +0100 Subject: [PATCH 105/614] some more refining --- lnbits/extensions/admin/models.py | 10 +++++----- .../extensions/admin/templates/admin/_tab_users.html | 2 ++ lnbits/extensions/admin/templates/admin/index.html | 12 +++++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 13a6cd233..45cd990d4 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -4,10 +4,10 @@ from pydantic import BaseModel class UpdateSettings(BaseModel): lnbits_backend_wallet_class: str = Query(None) - lnbits_admin_users: str = Query(None) - lnbits_allowed_users: str = Query(None) - lnbits_admin_ext: str = Query(None) - lnbits_disabled_ext: str = Query(None) + lnbits_admin_users: str = Query(None) #this should be List[str] ?? + lnbits_allowed_users: str = Query(None) #this should be List[str] ?? + lnbits_admin_ext: str = Query(None) #this should be List[str] ?? + lnbits_disabled_ext: str = Query(None) #this should be List[str] ?? lnbits_funding_source: str = Query(None) lnbits_force_https: bool = Query(None) lnbits_reserve_fee_min: int = Query(None, ge=0) @@ -21,4 +21,4 @@ class UpdateSettings(BaseModel): lnbits_denomination: str = Query(None) lnbits_theme: str = Query(None) lnbits_custom_logo: str = Query(None) - lnbits_ad_space: str = Query(None) + lnbits_ad_space: str = Query(None) #this should be List[str] ?? diff --git a/lnbits/extensions/admin/templates/admin/_tab_users.html b/lnbits/extensions/admin/templates/admin/_tab_users.html index c396ba7d6..08b08a628 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_users.html +++ b/lnbits/extensions/admin/templates/admin/_tab_users.html @@ -71,6 +71,7 @@ multiple hint="Extensions only user with admin privileges can use" label="Admin extensions" + :options="g.extensions.map(e => e.name)" >
@@ -79,6 +80,7 @@
- + Date: Mon, 10 Oct 2022 12:17:35 +0100 Subject: [PATCH 106/614] get saved data and alert when data changed --- .../admin/templates/admin/index.html | 18 +++++++----------- lnbits/extensions/admin/views_api.py | 5 +++-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 4754656d7..4e401cb40 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -3,7 +3,7 @@
- + { + this.settings = response.data.settings + this.formData = _.clone(this.settings) this.$q.notify({ type: 'positive', message: 'Success! Settings changed!', diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index c81205643..c2079e377 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -43,8 +43,9 @@ async def api_update_settings( user: User = Depends(check_admin), data: UpdateSettings = Body(...), ): - await update_settings(data) - return {"status": "Success"} + settings = await update_settings(data) + logger.debug(settings) + return {"status": "Success", "settings": settings.dict()} @admin_ext.delete("/api/v1/settings/", status_code=HTTPStatus.OK) From a3b05e26b718db4969884e642416b2eac119f506 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 10 Oct 2022 12:23:19 +0100 Subject: [PATCH 107/614] cleanup and typing fix for data --- lnbits/extensions/admin/models.py | 12 +++++----- .../admin/templates/admin/index.html | 22 ++----------------- lnbits/extensions/admin/views_api.py | 1 - 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 45cd990d4..94fa56bb7 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -1,13 +1,15 @@ +from typing import List + from fastapi import Query from pydantic import BaseModel class UpdateSettings(BaseModel): lnbits_backend_wallet_class: str = Query(None) - lnbits_admin_users: str = Query(None) #this should be List[str] ?? - lnbits_allowed_users: str = Query(None) #this should be List[str] ?? - lnbits_admin_ext: str = Query(None) #this should be List[str] ?? - lnbits_disabled_ext: str = Query(None) #this should be List[str] ?? + lnbits_admin_users: List[str] = Query(None) + lnbits_allowed_users: List[str] = Query(None) + lnbits_admin_ext: List[str] = Query(None) + lnbits_disabled_ext: List[str] = Query(None) lnbits_funding_source: str = Query(None) lnbits_force_https: bool = Query(None) lnbits_reserve_fee_min: int = Query(None, ge=0) @@ -21,4 +23,4 @@ class UpdateSettings(BaseModel): lnbits_denomination: str = Query(None) lnbits_theme: str = Query(None) lnbits_custom_logo: str = Query(None) - lnbits_ad_space: str = Query(None) #this should be List[str] ?? + lnbits_ad_space: List[str] = Query(None) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 4e401cb40..d8111595f 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -209,26 +209,8 @@ }) }, updateSettings() { - let data = { - lnbits_backend_wallet_class: this.formData.lnbits_backend_wallet_class, - lnbits_admin_users: this.formData.lnbits_admin_users.toString(), - lnbits_allowed_users: this.formData.lnbits_allowed_users.toString(), - lnbits_admin_ext: this.formData.lnbits_admin_ext, - lnbits_disabled_ext: this.formData.lnbits_disabled_ext, - lnbits_funding_source: this.formData.lnbits_funding_source, - lnbits_force_https: this.formData.lnbits_force_https, - lnbits_reserve_fee_min: this.formData.lnbits_reserve_fee_min, - lnbits_reserve_fee_percent: this.formData.lnbits_reserve_fee_percent, - lnbits_service_fee: this.formData.lnbits_service_fee, - lnbits_hide_api: this.formData.lnbits_hide_api, - lnbits_site_title: this.formData.lnbits_site_title, - lnbits_site_tagline: this.formData.lnbits_site_tagline, - lnbits_site_description: this.formData.lnbits_site_description, - lnbits_default_wallet_name: this.formData.lnbits_default_wallet_name, - lnbits_denomination: this.formData.lnbits_denomination, - lnbits_theme: this.formData.lnbits_theme, - lnbits_custom_logo: this.formData.lnbits_custom_logo, - lnbits_ad_space: this.formData.lnbits_ad_space.toString() + let data = { + ...this.formData } LNbits.api .request( diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index c2079e377..19b52e352 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -44,7 +44,6 @@ async def api_update_settings( data: UpdateSettings = Body(...), ): settings = await update_settings(data) - logger.debug(settings) return {"status": "Success", "settings": settings.dict()} From 91a5f7d2143ac4e2eb4f58607a062c1386bea6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 10 Oct 2022 23:27:46 +0200 Subject: [PATCH 108/614] add callback for saas app --- lnbits/app.py | 2 +- lnbits/extensions/admin/migrations.py | 3 +++ lnbits/extensions/admin/models.py | 10 ++++----- lnbits/extensions/admin/views_api.py | 5 ----- lnbits/settings.py | 32 +++++++++++++++++++++++---- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index a8371950b..6fa5cf5d0 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -64,7 +64,7 @@ def create_app() -> FastAPI: # TODO: why those 2? g().config = settings - # g().base_url = f"http://{settings.host}:{settings.port}" + g().base_url = f"http://{settings.host}:{settings.port}" app.add_middleware(GZipMiddleware, minimum_size=1000) diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index c4bc98d84..ea698c27b 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -6,6 +6,9 @@ async def m001_create_admin_settings_table(db): debug TEXT, host TEXT, port INTEGER, + lnbits_saas_instance_id TEXT, + lnbits_saas_callback TEXT, + lnbits_saas_secret TEXT, lnbits_path TEXT, lnbits_commit TEXT, lnbits_admin_users TEXT, diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 94fa56bb7..ada84e9d6 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -6,10 +6,10 @@ from pydantic import BaseModel class UpdateSettings(BaseModel): lnbits_backend_wallet_class: str = Query(None) - lnbits_admin_users: List[str] = Query(None) - lnbits_allowed_users: List[str] = Query(None) - lnbits_admin_ext: List[str] = Query(None) - lnbits_disabled_ext: List[str] = Query(None) + lnbits_admin_users: List[str] = Query(None) + lnbits_allowed_users: List[str] = Query(None) + lnbits_admin_ext: List[str] = Query(None) + lnbits_disabled_ext: List[str] = Query(None) lnbits_funding_source: str = Query(None) lnbits_force_https: bool = Query(None) lnbits_reserve_fee_min: int = Query(None, ge=0) @@ -23,4 +23,4 @@ class UpdateSettings(BaseModel): lnbits_denomination: str = Query(None) lnbits_theme: str = Query(None) lnbits_custom_logo: str = Query(None) - lnbits_ad_space: List[str] = Query(None) + lnbits_ad_space: List[str] = Query(None) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 19b52e352..ae2959bc8 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -53,8 +53,3 @@ async def api_delete_settings( ): await delete_settings() return {"status": "Success"} - - -@admin_ext.get("/api/v1/backup/", status_code=HTTPStatus.OK) -async def api_backup(user: User = Depends(check_admin)): - return {"status": "not implemented"} diff --git a/lnbits/settings.py b/lnbits/settings.py index ffcdcc0a9..f183211c4 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,6 +1,7 @@ import importlib import json import subprocess +import httpx from os import path from sqlite3 import Row from typing import List, Optional @@ -9,6 +10,7 @@ from loguru import logger from pydantic import BaseSettings, Field, validator + def list_parse_fallback(v): try: return json.loads(v) @@ -34,6 +36,11 @@ class Settings(BaseSettings): lnbits_path: str = Field(default=".") lnbits_commit: str = Field(default="unknown") + # saas + lnbits_saas_callback: Optional[str] = Field(default=None) + lnbits_saas_secret: Optional[str] = Field(default=None) + lnbits_saas_instance_id: Optional[str] = Field(default=None) + # users lnbits_admin_users: List[str] = Field(default=[]) lnbits_allowed_users: List[str] = Field(default=[]) @@ -230,11 +237,28 @@ async def check_admin_settings(): http = "https" if settings.lnbits_force_https else "http" user = settings.lnbits_admin_users[0] - logger.warning( - f" ✔️ Access admin user account at: {http}://{settings.host}:{settings.port}/wallet?usr={user}" - ) + + admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}" + logger.warning(f"✔️ Access admin user account at: {admin_url}") + + if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id: + with httpx.Client() as client: + headers = { + "Content-Type": "application/json; charset=utf-8", + "X-API-KEY": settings.lnbits_saas_secret + } + payload = { + "instance_id": settings.lnbits_saas_instance_id, + "adminuser": user + } + try: + r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload) + logger.warning("sent admin user to saas application") + except: + logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}") + except: - logger.warning("admin.settings tables does not exist.") + logger.error("admin.settings tables does not exist.") raise From c9ead25d50cc1c8cd019e51d7147c4febb71b635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 12 Oct 2022 13:08:59 +0200 Subject: [PATCH 109/614] bugfixes and fix topup wallet --- lnbits/app.py | 1 + .../admin/templates/admin/_tab_funding.html | 4 +-- .../admin/templates/admin/index.html | 28 +++++++++------ lnbits/extensions/admin/views_api.py | 36 ++++++++++--------- lnbits/server.py | 1 + lnbits/settings.py | 29 ++++++++++----- lnbits/wallets/fake.py | 2 +- 7 files changed, 60 insertions(+), 41 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 6fa5cf5d0..49ad8d77d 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -84,6 +84,7 @@ async def check_funding_source() -> None: def signal_handler(signal, frame): logger.debug(f"SIGINT received, terminating LNbits.") sys.exit(1) + signal.signal(signal.SIGINT, signal_handler) WALLET = get_wallet_class() diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html index 8b5456f19..34162a9f1 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_funding.html +++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html @@ -8,9 +8,7 @@

Funding Source Info

    {%raw%} -
  • - Funding Source: {{settings.lnbits_backend_wallet_class}} -
  • +
  • Funding Source: {{settings.lnbits_backend_wallet_class}}
  • Balance: {{balance / 1000}} sats
  • {%endraw%}
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index d8111595f..d3e93a718 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -3,7 +3,13 @@
- + u !== user - ) + this.settings.lnbits_admin_users = admin_users.filter(u => u !== user) }, addAllowedUser() { let addUser = this.formData.allowed_users_add @@ -155,7 +159,9 @@ }, removeAllowedUser(user) { let allowed_users = this.settings.lnbits_allowed_users - this.settings.lnbits_allowed_users = allowed_users.filter(u => u !== user) + this.settings.lnbits_allowed_users = allowed_users.filter( + u => u !== user + ) }, addAdSpace() { let adSpace = this.formData.ad_space_add @@ -187,10 +193,10 @@ topupWallet() { LNbits.api .request( - 'POST', + 'PUT', '/admin/api/v1/topup/?usr=' + this.g.user.id, - this.wallet.id, - this.wallet.amount + this.g.user.wallets[0].adminkey, + this.wallet ) .then(response => { this.$q.notify({ @@ -209,7 +215,7 @@ }) }, updateSettings() { - let data = { + let data = { ...this.formData } LNbits.api @@ -262,7 +268,7 @@ LNbits.utils.notifyApiError(error) }) } - }, + } }) {% endblock %} diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index ae2959bc8..63ed5b3ca 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -1,7 +1,6 @@ from http import HTTPStatus -from fastapi import Body, Depends, Request -from loguru import logger +from fastapi import Body, Depends, Query from starlette.exceptions import HTTPException from lnbits.core.crud import get_wallet @@ -9,47 +8,50 @@ from lnbits.core.models import User from lnbits.decorators import check_admin from lnbits.extensions.admin import admin_ext from lnbits.extensions.admin.models import UpdateSettings -from lnbits.requestvars import g from lnbits.server import server_restart -from lnbits.settings import settings from .crud import delete_settings, update_settings, update_wallet_balance -@admin_ext.get("/api/v1/restart/", status_code=HTTPStatus.OK) -async def api_restart_server(user: User = Depends(check_admin)): +@admin_ext.get( + "/api/v1/restart/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) +async def api_restart_server() -> dict[str, str]: server_restart.set() return {"status": "Success"} -@admin_ext.put("/api/v1/topup/", status_code=HTTPStatus.OK) +@admin_ext.put( + "/api/v1/topup/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) async def api_update_balance( - wallet_id, topup_amount: int, user: User = Depends(check_admin) -): + id: str = Body(...), amount: int = Body(...) +) -> dict[str, str]: try: - wallet = await get_wallet(wallet_id) + await get_wallet(id) except: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="wallet does not exist." ) - await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount)) + await update_wallet_balance(wallet_id=id, amount=int(amount)) return {"status": "Success"} -@admin_ext.put("/api/v1/settings/", status_code=HTTPStatus.OK) +@admin_ext.put( + "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) async def api_update_settings( - user: User = Depends(check_admin), data: UpdateSettings = Body(...), ): settings = await update_settings(data) return {"status": "Success", "settings": settings.dict()} -@admin_ext.delete("/api/v1/settings/", status_code=HTTPStatus.OK) -async def api_delete_settings( - user: User = Depends(check_admin), -): +@admin_ext.delete( + "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) +async def api_delete_settings() -> dict[str, str]: await delete_settings() return {"status": "Success"} diff --git a/lnbits/server.py b/lnbits/server.py index 79af8112f..6d4cd2e70 100644 --- a/lnbits/server.py +++ b/lnbits/server.py @@ -52,6 +52,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload: port=port, host=host, reload=reload, + forwarded_allow_ips="*", ssl_keyfile=ssl_keyfile, ssl_certfile=ssl_certfile, **d diff --git a/lnbits/settings.py b/lnbits/settings.py index f183211c4..61dbd6f29 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,20 +1,19 @@ import importlib import json import subprocess -import httpx from os import path from sqlite3 import Row from typing import List, Optional +import httpx from loguru import logger from pydantic import BaseSettings, Field, validator - def list_parse_fallback(v): try: return json.loads(v) - except Exception as e: + except Exception: replaced = v.replace(" ", "") if replaced: return replaced.split(",") @@ -238,24 +237,36 @@ async def check_admin_settings(): http = "https" if settings.lnbits_force_https else "http" user = settings.lnbits_admin_users[0] - admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}" + admin_url = ( + f"{http}://{settings.host}:{settings.port}/wallet?usr={user}" + ) logger.warning(f"✔️ Access admin user account at: {admin_url}") - if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id: + if ( + settings.lnbits_saas_callback + and settings.lnbits_saas_secret + and settings.lnbits_saas_instance_id + ): with httpx.Client() as client: headers = { "Content-Type": "application/json; charset=utf-8", - "X-API-KEY": settings.lnbits_saas_secret + "X-API-KEY": settings.lnbits_saas_secret, } payload = { "instance_id": settings.lnbits_saas_instance_id, - "adminuser": user + "adminuser": user, } try: - r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload) + client.post( + settings.lnbits_saas_callback, + headers=headers, + json=payload, + ) logger.warning("sent admin user to saas application") except: - logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}") + logger.error( + f"error sending admin user to saas: {settings.lnbits_saas_callback}" + ) except: logger.error("admin.settings tables does not exist.") diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index 73458e8c4..94ff5f48b 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -19,7 +19,6 @@ from .base import ( class FakeWallet(Wallet): - queue: asyncio.Queue = asyncio.Queue(0) secret: str = settings.fake_wallet_secret privkey: str = hashlib.pbkdf2_hmac( "sha256", @@ -98,6 +97,7 @@ class FakeWallet(Wallet): return PaymentStatus(None) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value: Invoice = await self.queue.get() yield value.payment_hash From 9a48b174c62bd1908be3a8698c36b34aa78a3188 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 12 Oct 2022 19:04:46 +0100 Subject: [PATCH 110/614] add funding sources options --- lnbits/app.py | 1 + lnbits/extensions/admin/models.py | 41 +++- .../admin/templates/admin/_tab_funding.html | 25 +- .../admin/templates/admin/index.html | 217 +++++++++++++++++- 4 files changed, 259 insertions(+), 25 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index a8371950b..50f218b72 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -84,6 +84,7 @@ async def check_funding_source() -> None: def signal_handler(signal, frame): logger.debug(f"SIGINT received, terminating LNbits.") sys.exit(1) + signal.signal(signal.SIGINT, signal_handler) WALLET = get_wallet_class() diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 94fa56bb7..d9d2b22ff 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -6,10 +6,10 @@ from pydantic import BaseModel class UpdateSettings(BaseModel): lnbits_backend_wallet_class: str = Query(None) - lnbits_admin_users: List[str] = Query(None) - lnbits_allowed_users: List[str] = Query(None) - lnbits_admin_ext: List[str] = Query(None) - lnbits_disabled_ext: List[str] = Query(None) + lnbits_admin_users: List[str] = Query(None) + lnbits_allowed_users: List[str] = Query(None) + lnbits_admin_ext: List[str] = Query(None) + lnbits_disabled_ext: List[str] = Query(None) lnbits_funding_source: str = Query(None) lnbits_force_https: bool = Query(None) lnbits_reserve_fee_min: int = Query(None, ge=0) @@ -23,4 +23,35 @@ class UpdateSettings(BaseModel): lnbits_denomination: str = Query(None) lnbits_theme: str = Query(None) lnbits_custom_logo: str = Query(None) - lnbits_ad_space: List[str] = Query(None) + lnbits_ad_space: List[str] = Query(None) + + # funding sources + fake_wallet_secret: str = Query(None) + lnbits_endpoint: str = Query(None) + lnbits_key: str = Query(None) + cliche_endpoint: str = Query(None) + corelightning_rpc: str = Query(None) + eclair_url: str = Query(None) + eclair_pass: str = Query(None) + lnd_rest_endpoint: str = Query(None) + lnd_rest_cert: str = Query(None) + lnd_rest_macaroon: str = Query(None) + lnd_rest_macaroon_encrypted: str = Query(None) + lnd_cert: str = Query(None) + lnd_admin_macaroon: str = Query(None) + lnd_invoice_macaroon: str = Query(None) + lnd_grpc_endpoint: str = Query(None) + lnd_grpc_cert: str = Query(None) + lnd_grpc_port: int = Query(None, ge=0) + lnd_grpc_admin_macaroon: str = Query(None) + lnd_grpc_invoice_macaroon: str = Query(None) + lnd_grpc_macaroon_encrypted: str = Query(None) + lnpay_api_endpoint: str = Query(None) + lnpay_api_key: str = Query(None) + lnpay_wallet_key: str = Query(None) + lntxbot_api_endpoint: str = Query(None) + lntxbot_key: str = Query(None) + opennode_api_endpoint: str = Query(None) + opennode_key: str = Query(None) + spark_url: str = Query(None) + spark_token: str = Query(None) diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html index 8b5456f19..a523d4e59 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_funding.html +++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html @@ -8,9 +8,7 @@

Funding Source Info

    {%raw%} -
  • - Funding Source: {{settings.lnbits_backend_wallet_class}} -
  • +
  • Funding Source: {{settings.lnbits_backend_wallet_class}}
  • Balance: {{balance / 1000}} sats
  • {%endraw%}
@@ -60,21 +58,30 @@
-

Funding Sources

+

Funding Sources (Requires server restart)

- + - - + diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index d8111595f..ccaddda43 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -3,7 +3,13 @@
- + u !== user - ) + this.settings.lnbits_admin_users = admin_users.filter(u => u !== user) }, addAllowedUser() { let addUser = this.formData.allowed_users_add @@ -155,7 +335,9 @@ }, removeAllowedUser(user) { let allowed_users = this.settings.lnbits_allowed_users - this.settings.lnbits_allowed_users = allowed_users.filter(u => u !== user) + this.settings.lnbits_allowed_users = allowed_users.filter( + u => u !== user + ) }, addAdSpace() { let adSpace = this.formData.ad_space_add @@ -208,8 +390,19 @@ LNbits.utils.notifyApiError(error) }) }, + updateFundingData(){ + this.settings.lnbits_allowed_funding_sources.map(f => { + let opts = this.funding_sources.get(f) + if (!opts) return + + Object.keys(opts).forEach(e => { + opts[e].value = this.settings[e] + }) + }) + console.log("funding", this.funding_sources) + }, updateSettings() { - let data = { + let data = { ...this.formData } LNbits.api @@ -222,11 +415,13 @@ .then(response => { this.settings = response.data.settings this.formData = _.clone(this.settings) + this.updateFundingData() this.$q.notify({ type: 'positive', message: 'Success! Settings changed!', icon: null }) + console.log(this.settings) }) .catch(function (error) { LNbits.utils.notifyApiError(error) @@ -262,7 +457,7 @@ LNbits.utils.notifyApiError(error) }) } - }, + } }) {% endblock %} From dde28e66a24e09517b668e291ed58a6eb7df37e2 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Thu, 13 Oct 2022 15:52:02 +0100 Subject: [PATCH 111/614] added tooltips, moved reset, and warnings --- .../admin/templates/admin/index.html | 97 ++++++++++++------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 575b377f2..7d268301b 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1,8 +1,14 @@ {% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block page %}
-
- +
+ + Save your changes - - - + + + Restart the server for changes to take effect + + + + + Add funds to a wallet. + + > --> + + Delete all settings and reset to defaults. +
@@ -121,6 +136,7 @@ show: false }, tab: 'funding', + needsRestart: false, funding_sources: new Map([ ['VoidWallet', null], [ @@ -302,13 +318,12 @@ this.balance = +'{{ balance|safe }}' this.formData = _.clone(this.settings) //model this.updateFundingData() - console.log(this.settings) }, computed: { checkChanges() { return !_.isEqual(this.settings, this.formData) - }, + } }, methods: { addAdminUser() { @@ -361,6 +376,7 @@ message: 'Success! Restarted Server', icon: null }) + this.needsRestart = false }) .catch(function (error) { LNbits.utils.notifyApiError(error) @@ -390,16 +406,15 @@ LNbits.utils.notifyApiError(error) }) }, - updateFundingData(){ + updateFundingData() { this.settings.lnbits_allowed_funding_sources.map(f => { let opts = this.funding_sources.get(f) if (!opts) return - + Object.keys(opts).forEach(e => { opts[e].value = this.settings[e] }) }) - console.log("funding", this.funding_sources) }, updateSettings() { let data = { @@ -415,31 +430,41 @@ .then(response => { this.settings = response.data.settings this.formData = _.clone(this.settings) + this.needsRestart = true this.updateFundingData() this.$q.notify({ type: 'positive', message: 'Success! Settings changed!', icon: null }) - console.log(this.settings) }) .catch(function (error) { LNbits.utils.notifyApiError(error) }) }, deleteSettings() { - LNbits.api - .request('DELETE', '/admin/api/v1/settings/?usr=' + this.g.user.id) - .then(response => { - this.$q.notify({ - type: 'positive', - message: - 'Success! Restored settings to defaults, restart required!', - icon: null - }) - }) - .catch(function (error) { - LNbits.utils.notifyApiError(error) + LNbits.utils + .confirmDialog( + 'Are you sure you want to restore settings to default?' + ) + .onOk(() => { + LNbits.api + .request( + 'DELETE', + '/admin/api/v1/settings/?usr=' + this.g.user.id + ) + .then(response => { + this.$q.notify({ + type: 'positive', + message: + 'Success! Restored settings to defaults, restart required!', + icon: null + }) + this.needsRestart = true + }) + .catch(function (error) { + LNbits.utils.notifyApiError(error) + }) }) }, downloadBackup() { From fb2dee73955aef2700bdf52decb6a570440beb23 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Thu, 13 Oct 2022 15:52:26 +0100 Subject: [PATCH 112/614] moved to columns --- .../admin/templates/admin/_tab_funding.html | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html index a523d4e59..a69ecb47e 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_funding.html +++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html @@ -27,38 +27,38 @@ :options="settings.lnbits_allowed_funding_sources" >
-
-

Fee reserve

-
-
- -
-
- -
+
+
+
+
+

Fee reserve

+
+
+ + +
+
+
-
-
-
-

Funding Sources (Requires server restart)

+

+ Funding Sources (Requires server restart) +

Date: Thu, 13 Oct 2022 15:52:39 +0100 Subject: [PATCH 113/614] themes not displaying fixed --- lnbits/extensions/admin/templates/admin/_tab_theme.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/admin/templates/admin/_tab_theme.html b/lnbits/extensions/admin/templates/admin/_tab_theme.html index 46bf83e9c..c327733ff 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_theme.html +++ b/lnbits/extensions/admin/templates/admin/_tab_theme.html @@ -63,7 +63,7 @@

Themes

Date: Fri, 21 Oct 2022 10:00:47 +0200 Subject: [PATCH 114/614] add loop to uvicorn --- lnbits/server.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lnbits/server.py b/lnbits/server.py index 6d4cd2e70..eb7c12b16 100644 --- a/lnbits/server.py +++ b/lnbits/server.py @@ -1,12 +1,7 @@ -import asyncio - import uvloop - uvloop.install() -import contextlib import multiprocessing as mp -import sys import time import click @@ -49,6 +44,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload: while True: config = uvicorn.Config( "lnbits.__main__:app", + loop="uvloop", port=port, host=host, reload=reload, @@ -65,9 +61,10 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload: server_restart.clear() server.should_exit = True server.force_exit = True + time.sleep(3) process.terminate() process.join() - time.sleep(3) + time.sleep(1) server_restart = mp.Event() From 1b675f295bd2ec1a69ae972cce9b70f0371eefdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 21 Oct 2022 11:13:40 +0200 Subject: [PATCH 115/614] add get settings endpoint with only values you can also save --- lnbits/app.py | 6 +---- lnbits/extensions/admin/crud.py | 12 +++++++++- lnbits/extensions/admin/models.py | 6 ++++- .../admin/templates/admin/index.html | 22 +++++++++++++++---- lnbits/extensions/admin/views_api.py | 7 +++++- lnbits/server.py | 1 + 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 49ad8d77d..959a81689 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -62,10 +62,6 @@ def create_app() -> FastAPI: CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"] ) - # TODO: why those 2? - g().config = settings - g().base_url = f"http://{settings.host}:{settings.port}" - app.add_middleware(GZipMiddleware, minimum_size=1000) register_startup(app) @@ -174,7 +170,7 @@ def register_assets(app: FastAPI): @app.on_event("startup") async def vendored_assets_variable(): - if g().config.debug: + if settings.debug: g().VENDORED_JS = map(url_for_vendored, get_js_vendored()) g().VENDORED_CSS = map(url_for_vendored, get_css_vendored()) else: diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index cc937b5e9..2ce916127 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -6,7 +6,7 @@ from lnbits.settings import Settings, read_only_variables from lnbits.tasks import internal_invoice_queue from . import db -from .models import UpdateSettings +from .models import AdminSettings, UpdateSettings async def update_wallet_balance(wallet_id: str, amount: int) -> str: @@ -26,6 +26,16 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str: await internal_invoice_queue.put(internal_id) +async def get_settings() -> AdminSettings: + row = await db.fetchone("SELECT * FROM admin.settings") + all_settings = Settings(**row) + settings = AdminSettings() + for key, value in row.items(): + if hasattr(settings, key): + setattr(settings, key, getattr(all_settings, key)) + return settings + + async def update_settings(data: UpdateSettings) -> Settings: fields = [] for key, value in data.dict(exclude_none=True).items(): diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index d9d2b22ff..318116594 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional from fastapi import Query from pydantic import BaseModel @@ -55,3 +55,7 @@ class UpdateSettings(BaseModel): opennode_key: str = Query(None) spark_url: str = Query(None) spark_token: str = Query(None) + + +class AdminSettings(UpdateSettings): + lnbits_allowed_funding_sources: Optional[List[str]] diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 7d268301b..103912615 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -314,11 +314,8 @@ } }, created: function () { - this.settings = JSON.parse('{{ settings|tojson|safe }}') //DB data + this.getSettings() this.balance = +'{{ balance|safe }}' - this.formData = _.clone(this.settings) //model - this.updateFundingData() - console.log(this.settings) }, computed: { checkChanges() { @@ -416,6 +413,23 @@ }) }) }, + getSettings() { + LNbits.api + .request( + 'GET', + '/admin/api/v1/settings/?usr=' + this.g.user.id, + this.g.user.wallets[0].adminkey + ) + .then(response => { + this.settings = response.data + this.formData = _.clone(this.settings) + this.updateFundingData() + console.log(this.settings) + }) + .catch(function (error) { + LNbits.utils.notifyApiError(error) + }) + }, updateSettings() { let data = { ...this.formData diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 63ed5b3ca..57d62ed49 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -10,7 +10,7 @@ from lnbits.extensions.admin import admin_ext from lnbits.extensions.admin.models import UpdateSettings from lnbits.server import server_restart -from .crud import delete_settings, update_settings, update_wallet_balance +from .crud import delete_settings, get_settings, update_settings, update_wallet_balance @admin_ext.get( @@ -21,6 +21,11 @@ async def api_restart_server() -> dict[str, str]: return {"status": "Success"} +@admin_ext.get("/api/v1/settings/", dependencies=[Depends(check_admin)]) +async def api_get_settings() -> UpdateSettings: + return await get_settings() + + @admin_ext.put( "/api/v1/topup/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] ) diff --git a/lnbits/server.py b/lnbits/server.py index eb7c12b16..ecf7ff62c 100644 --- a/lnbits/server.py +++ b/lnbits/server.py @@ -1,4 +1,5 @@ import uvloop + uvloop.install() import multiprocessing as mp From 49c58cc8d0cb48fd874ba40d28c4331a065424df Mon Sep 17 00:00:00 2001 From: Gene Takavic <80261724+iWarpBTC@users.noreply.github.com> Date: Mon, 24 Oct 2022 11:48:34 +0200 Subject: [PATCH 116/614] return a reason of failed payment to the pos; disable a change of wallet in the form --- lnbits/extensions/boltcards/lnurl.py | 4 ++-- lnbits/extensions/boltcards/templates/boltcards/index.html | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/boltcards/lnurl.py b/lnbits/extensions/boltcards/lnurl.py index 064bde2c6..cd4c6ba45 100644 --- a/lnbits/extensions/boltcards/lnurl.py +++ b/lnbits/extensions/boltcards/lnurl.py @@ -136,8 +136,8 @@ async def lnurl_callback( ) return {"status": "OK"} - except: - return {"status": "ERROR", "reason": f"Payment failed"} + except Exception as exc: + return {"status": "ERROR", "reason": f"Payment failed - {exc}"} # /boltcards/api/v1/auth?a=00000000000000000000000000000000 diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html index f795e4545..7b9713e29 100644 --- a/lnbits/extensions/boltcards/templates/boltcards/index.html +++ b/lnbits/extensions/boltcards/templates/boltcards/index.html @@ -215,6 +215,7 @@ emit-value v-model="cardDialog.data.wallet" :options="g.user.walletOptions" + :disable="cardDialog.data.id != null" label="Wallet *" > From 708f855c1d485639dfa559c2f2a26a5a9f41c1d7 Mon Sep 17 00:00:00 2001 From: benarc Date: Mon, 7 Mar 2022 05:03:32 +0000 Subject: [PATCH 117/614] Added old admin extension --- .env.example | 14 +- lnbits/extensions/admin/README.md | 11 + lnbits/extensions/admin/__init__.py | 10 + lnbits/extensions/admin/config.json | 6 + lnbits/extensions/admin/crud.py | 59 ++ lnbits/extensions/admin/migrations.py | 256 ++++++++ lnbits/extensions/admin/models.py | 38 ++ .../admin/templates/admin/index.html | 565 ++++++++++++++++++ lnbits/extensions/admin/views.py | 20 + lnbits/extensions/admin/views_api.py | 41 ++ 10 files changed, 1014 insertions(+), 6 deletions(-) create mode 100644 lnbits/extensions/admin/README.md create mode 100644 lnbits/extensions/admin/__init__.py create mode 100644 lnbits/extensions/admin/config.json create mode 100644 lnbits/extensions/admin/crud.py create mode 100644 lnbits/extensions/admin/migrations.py create mode 100644 lnbits/extensions/admin/models.py create mode 100644 lnbits/extensions/admin/templates/admin/index.html create mode 100644 lnbits/extensions/admin/views.py create mode 100644 lnbits/extensions/admin/views_api.py diff --git a/.env.example b/.env.example index 987c6ca69..bfaeb5152 100644 --- a/.env.example +++ b/.env.example @@ -3,17 +3,19 @@ PORT=5000 DEBUG=false -LNBITS_ALLOWED_USERS="" -LNBITS_ADMIN_USERS="" -# Extensions only admin can access -LNBITS_ADMIN_EXTENSIONS="ngrok" +LNBITS_ADMIN_USERS="" # User IDs seperated by comma +LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access +LNBITS_ADMIN_UI=false # Extensions only admin can access + +LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma + LNBITS_DEFAULT_WALLET_NAME="LNbits wallet" # csv ad image filepaths or urls, extensions can choose to honor 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 +69,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 diff --git a/lnbits/extensions/admin/README.md b/lnbits/extensions/admin/README.md new file mode 100644 index 000000000..277294592 --- /dev/null +++ b/lnbits/extensions/admin/README.md @@ -0,0 +1,11 @@ +

Example Extension

+

*tagline*

+This is an example extension to help you organise and build you own. + +Try to include an image + + + +

If your extension has API endpoints, include useful ones here

+ +curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY" diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py new file mode 100644 index 000000000..d5f26c90d --- /dev/null +++ b/lnbits/extensions/admin/__init__.py @@ -0,0 +1,10 @@ +from quart import Blueprint +from lnbits.db import Database + +db = Database("ext_admin") + +admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates") + + +from .views_api import * # noqa +from .views import * # noqa diff --git a/lnbits/extensions/admin/config.json b/lnbits/extensions/admin/config.json new file mode 100644 index 000000000..696617335 --- /dev/null +++ b/lnbits/extensions/admin/config.json @@ -0,0 +1,6 @@ +{ + "name": "Admin", + "short_description": "Manage your LNbits install", + "icon": "build", + "contributors": ["benarc"] +} diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py new file mode 100644 index 000000000..cb8f9b5be --- /dev/null +++ b/lnbits/extensions/admin/crud.py @@ -0,0 +1,59 @@ +from typing import List, Optional + +from . import db +from .models import Admin, Funding +from lnbits.settings import * +from lnbits.helpers import urlsafe_short_hash +from lnbits.core.crud import create_payment +from lnbits.db import Connection + + +def update_wallet_balance(wallet_id: str, amount: int) -> str: + temp_id = f"temp_{urlsafe_short_hash()}" + internal_id = f"internal_{urlsafe_short_hash()}" + create_payment( + wallet_id=wallet_id, + checking_id=internal_id, + payment_request="admin_internal", + payment_hash="admin_internal", + amount=amount * 1000, + memo="Admin top up", + pending=False, + ) + return "success" + + +async def update_admin( +) -> Optional[Admin]: + if not CLightningWallet: + print("poo") + await db.execute( + """ + UPDATE admin + SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ? + WHERE 1 + """, + ( + + ), + ) + row = await db.fetchone("SELECT * FROM admin WHERE 1") + return Admin.from_row(row) if row else None + +async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]: + q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) + await db.execute( + f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id) + ) + row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,)) + return Jukebox(**row) if row else None + +async def get_admin() -> List[Admin]: + row = await db.fetchone("SELECT * FROM admin WHERE 1") + return Admin.from_row(row) if row else None + + +async def get_funding() -> List[Funding]: + rows = await db.fetchall("SELECT * FROM funding") + + return [Funding.from_row(row) for row in rows] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py new file mode 100644 index 000000000..82d934cb6 --- /dev/null +++ b/lnbits/extensions/admin/migrations.py @@ -0,0 +1,256 @@ +from sqlalchemy.exc import OperationalError # type: ignore +from os import getenv +from lnbits.helpers import urlsafe_short_hash + + +async def m001_create_admin_table(db): + user = None + site_title = None + site_tagline = None + site_description = None + allowed_users = None + admin_user = None + default_wallet_name = None + data_folder = None + disabled_ext = None + force_https = True + service_fee = 0 + funding_source = "" + + if getenv("LNBITS_SITE_TITLE"): + site_title = getenv("LNBITS_SITE_TITLE") + + if getenv("LNBITS_SITE_TAGLINE"): + site_tagline = getenv("LNBITS_SITE_TAGLINE") + + if getenv("LNBITS_SITE_DESCRIPTION"): + site_description = getenv("LNBITS_SITE_DESCRIPTION") + + if getenv("LNBITS_ALLOWED_USERS"): + allowed_users = getenv("LNBITS_ALLOWED_USERS") + + if getenv("LNBITS_ADMIN_USER"): + admin_user = getenv("LNBITS_ADMIN_USER") + + if getenv("LNBITS_DEFAULT_WALLET_NAME"): + default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") + + if getenv("LNBITS_DATA_FOLDER"): + data_folder = getenv("LNBITS_DATA_FOLDER") + + if getenv("LNBITS_DISABLED_EXTENSIONS"): + disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS") + + if getenv("LNBITS_FORCE_HTTPS"): + force_https = getenv("LNBITS_FORCE_HTTPS") + + if getenv("LNBITS_SERVICE_FEE"): + service_fee = getenv("LNBITS_SERVICE_FEE") + + if getenv("LNBITS_BACKEND_WALLET_CLASS"): + funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS") + + await db.execute( + """ + CREATE TABLE IF NOT EXISTS admin ( + user TEXT, + site_title TEXT, + site_tagline TEXT, + site_description TEXT, + admin_user TEXT, + allowed_users TEXT, + default_wallet_name TEXT, + data_folder TEXT, + disabled_ext TEXT, + force_https BOOLEAN, + service_fee INT, + funding_source TEXT + ); + """ + ) + await db.execute( + """ + INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + user, + site_title, + site_tagline, + site_description, + admin_user, + allowed_users, + default_wallet_name, + data_folder, + disabled_ext, + force_https, + service_fee, + funding_source, + ), + ) + + +async def m001_create_funding_table(db): + + funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS") + + # Make the funding table, if it does not already exist + await db.execute( + """ + CREATE TABLE IF NOT EXISTS funding ( + id TEXT PRIMARY KEY, + backend_wallet TEXT, + endpoint TEXT, + port INT, + read_key TEXT, + invoice_key TEXT, + admin_key TEXT, + cert TEXT, + balance INT, + selected INT + ); + """ + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, selected) + VALUES (?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "CLightningWallet", + getenv("CLIGHTNING_RPC"), + 1 if funding_wallet == "CLightningWallet" else 0, + ), + ) + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "SparkWallet", + getenv("SPARK_URL"), + getenv("SPARK_TOKEN"), + 1 if funding_wallet == "SparkWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LnbitsWallet", + getenv("LNBITS_ENDPOINT"), + getenv("LNBITS_KEY"), + 1 if funding_wallet == "LnbitsWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, port, admin_key, cert, selected) + VALUES (?, ?, ?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LndWallet", + getenv("LND_GRPC_ENDPOINT"), + getenv("LND_GRPC_PORT"), + getenv("LND_GRPC_MACAROON"), + getenv("LND_GRPC_CERT"), + 1 if funding_wallet == "LndWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected) + VALUES (?, ?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LndRestWallet", + getenv("LND_REST_ENDPOINT"), + getenv("LND_REST_MACAROON"), + getenv("LND_REST_CERT"), + 1 if funding_wallet == "LndWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected) + VALUES (?, ?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LNPayWallet", + getenv("LNPAY_API_ENDPOINT"), + getenv("LNPAY_WALLET_KEY"), + getenv("LNPAY_API_KEY"), # this is going in as the cert + 1 if funding_wallet == "LNPayWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "LntxbotWallet", + getenv("LNTXBOT_API_ENDPOINT"), + getenv("LNTXBOT_KEY"), + 1 if funding_wallet == "LntxbotWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "OpenNodeWallet", + getenv("OPENNODE_API_ENDPOINT"), + getenv("OPENNODE_KEY"), + 1 if funding_wallet == "OpenNodeWallet" else 0, + ), + ) + + await db.execute( + """ + INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + VALUES (?, ?, ?, ?, ?) + """, + ( + urlsafe_short_hash(), + "SparkWallet", + getenv("SPARK_URL"), + getenv("SPARK_TOKEN"), + 1 if funding_wallet == "SparkWallet" else 0, + ), + ) + + ## PLACEHOLDER FOR ECLAIR WALLET + # await db.execute( + # """ + # INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + # VALUES (?, ?, ?, ?, ?) + # """, + # ( + # urlsafe_short_hash(), + # "EclairWallet", + # getenv("ECLAIR_URL"), + # getenv("ECLAIR_PASS"), + # 1 if funding_wallet == "EclairWallet" else 0, + # ), + # ) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py new file mode 100644 index 000000000..c38f17f48 --- /dev/null +++ b/lnbits/extensions/admin/models.py @@ -0,0 +1,38 @@ +from typing import NamedTuple +from sqlite3 import Row + +class Admin(NamedTuple): + user: str + site_title: str + site_tagline: str + site_description:str + allowed_users: str + admin_user: str + default_wallet_name: str + data_folder: str + disabled_ext: str + force_https: str + service_fee: str + funding_source: str + + @classmethod + def from_row(cls, row: Row) -> "Admin": + data = dict(row) + return cls(**data) + +class Funding(NamedTuple): + id: str + backend_wallet: str + endpoint: str + port: str + read_key: str + invoice_key: str + admin_key: str + cert: str + balance: int + selected: int + + @classmethod + def from_row(cls, row: Row) -> "Funding": + data = dict(row) + return cls(**data) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html new file mode 100644 index 000000000..87cf09efa --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -0,0 +1,565 @@ +{% extends "base.html" %} {% from "macros.jinja" import window_vars with context +%} {% block page %} + +

Admin

+

+ +
+
+ + +
Settings
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+

+
+
+ +
+
+
+
+ +
+ + +
Wallet topup
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+ +{% endblock %} {% block scripts %} {{ window_vars(user) }} + +{% endblock %} diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py new file mode 100644 index 000000000..5e17919c5 --- /dev/null +++ b/lnbits/extensions/admin/views.py @@ -0,0 +1,20 @@ +from quart import g, render_template, request, jsonify +import json + +from lnbits.decorators import check_user_exists, validate_uuids +from lnbits.extensions.admin import admin_ext +from lnbits.core.crud import get_user, create_account +from .crud import get_admin, get_funding +from lnbits.settings import WALLET + + +@admin_ext.route("/") +@validate_uuids(["usr"], required=True) +@check_user_exists() +async def index(): + user_id = g.user + admin = await get_admin() + + funding = [{**funding._asdict()} for funding in await get_funding()] + + return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py new file mode 100644 index 000000000..2a61b6f55 --- /dev/null +++ b/lnbits/extensions/admin/views_api.py @@ -0,0 +1,41 @@ +from quart import jsonify, g, request +from http import HTTPStatus +from .crud import update_wallet_balance +from lnbits.extensions.admin import admin_ext +from lnbits.decorators import api_check_wallet_key, api_validate_post_request +from lnbits.core.crud import get_wallet +from .crud import get_admin,update_admin +import json + +@admin_ext.route("/api/v1/admin//", methods=["GET"]) +@api_check_wallet_key("admin") +async def api_update_balance(wallet_id, topup_amount): + print(g.data.wallet) + try: + wallet = await get_wallet(wallet_id) + except: + return ( + jsonify({"error": "Not allowed: not an admin"}), + HTTPStatus.FORBIDDEN, + ) + print(wallet) + print(topup_amount) + return jsonify({"status": "Success"}), HTTPStatus.OK + + +@admin_ext.route("/api/v1/admin/", methods=["POST"]) +@api_check_wallet_key("admin") +@api_validate_post_request(schema={}) +async def api_update_admin(): + body = await request.get_json() + admin = await get_admin() + print(g.wallet[2]) + print(body["admin_user"]) + if not admin.admin_user == g.wallet[2] and admin.admin_user != None: + return ( + jsonify({"error": "Not allowed: not an admin"}), + HTTPStatus.FORBIDDEN, + ) + updated = await update_admin(body) + print(updated) + return jsonify({"status": "Success"}), HTTPStatus.OK \ No newline at end of file From a3b1d9528c92008b42116904f5ebbdf8d9360173 Mon Sep 17 00:00:00 2001 From: benarc Date: Mon, 7 Mar 2022 05:11:55 +0000 Subject: [PATCH 118/614] old admin setup UI --- lnbits/core/templates/core/admin.html | 717 ++++++++++++++++++++++++++ 1 file changed, 717 insertions(+) create mode 100644 lnbits/core/templates/core/admin.html diff --git a/lnbits/core/templates/core/admin.html b/lnbits/core/templates/core/admin.html new file mode 100644 index 000000000..e81765550 --- /dev/null +++ b/lnbits/core/templates/core/admin.html @@ -0,0 +1,717 @@ +{% extends "public.html" %} {% from "macros.jinja" import window_vars with +context %} {% block page %} +
+
+ + +

+
Welcome to LNbits
+

+
+ Fill in the information below to setup your LNbits instance. Details + can be changed later. +
+

+ + +
+ +
Branding
+
+
+ +
+
+ +
+
+
+
+ + + +
+
+ + + +
+
+ +
Service settings
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ Funding source information (at least one required)
*if installed through RaspiBlitz, MyNode, etc, details + should be filled in for you
+
+ + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+ View project in GitHub + Donate +
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(funding) }} + +{% endblock %} From b325566302f079575e478d9ca9eeff47a8f10a1a Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:18:09 +0000 Subject: [PATCH 119/614] convert to FastAPI --- lnbits/extensions/admin/__init__.py | 11 +++- lnbits/extensions/admin/crud.py | 50 +++++++-------- lnbits/extensions/admin/migrations.py | 23 ++++--- lnbits/extensions/admin/models.py | 51 ++++++++++------ .../admin/templates/admin/index.html | 23 +++++-- lnbits/extensions/admin/views.py | 39 ++++++++---- lnbits/extensions/admin/views_api.py | 61 ++++++++++--------- 7 files changed, 151 insertions(+), 107 deletions(-) diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py index d5f26c90d..6a56b2bb1 100644 --- a/lnbits/extensions/admin/__init__.py +++ b/lnbits/extensions/admin/__init__.py @@ -1,10 +1,15 @@ -from quart import Blueprint +from fastapi import APIRouter + from lnbits.db import Database +from lnbits.helpers import template_renderer db = Database("ext_admin") -admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates") +admin_ext: APIRouter = APIRouter(prefix="/admin", tags=["admin"]) + +def admin_renderer(): + return template_renderer(["lnbits/extensions/admin/templates"]) -from .views_api import * # noqa from .views import * # noqa +from .views_api import * # noqa diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index cb8f9b5be..872d6c97b 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -1,11 +1,11 @@ from typing import List, Optional +from lnbits.core.crud import create_payment +from lnbits.helpers import urlsafe_short_hash +from lnbits.settings import * + from . import db from .models import Admin, Funding -from lnbits.settings import * -from lnbits.helpers import urlsafe_short_hash -from lnbits.core.crud import create_payment -from lnbits.db import Connection def update_wallet_balance(wallet_id: str, amount: int) -> str: @@ -22,38 +22,30 @@ def update_wallet_balance(wallet_id: str, amount: int) -> str: ) return "success" - -async def update_admin( -) -> Optional[Admin]: - if not CLightningWallet: - print("poo") - await db.execute( - """ - UPDATE admin - SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ? - WHERE 1 - """, - ( - - ), - ) - row = await db.fetchone("SELECT * FROM admin WHERE 1") - return Admin.from_row(row) if row else None - -async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]: +async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) + print("UPDATE", q) await db.execute( - f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id) + f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) ) - row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,)) - return Jukebox(**row) if row else None + row = await db.fetchone('SELECT * FROM admin WHERE "user" = ?', (user,)) + assert row, "Newly updated settings couldn't be retrieved" + return Admin(**row) if row else None + +# async def update_admin(user: str, **kwargs) -> Optional[Admin]: +# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) +# await db.execute( +# f"UPDATE admin SET {q} WHERE user = ?", (*kwargs.values(), user) +# ) +# new_settings = await get_admin() +# return new_settings async def get_admin() -> List[Admin]: - row = await db.fetchone("SELECT * FROM admin WHERE 1") - return Admin.from_row(row) if row else None + row = await db.fetchone("SELECT * FROM admin") + return Admin(**row) if row else None async def get_funding() -> List[Funding]: rows = await db.fetchall("SELECT * FROM funding") - return [Funding.from_row(row) for row in rows] + return [Funding(**row) for row in rows] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 82d934cb6..13b769232 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -1,5 +1,7 @@ -from sqlalchemy.exc import OperationalError # type: ignore from os import getenv + +from sqlalchemy.exc import OperationalError # type: ignore + from lnbits.helpers import urlsafe_short_hash @@ -9,7 +11,7 @@ async def m001_create_admin_table(db): site_tagline = None site_description = None allowed_users = None - admin_user = None + admin_users = None default_wallet_name = None data_folder = None disabled_ext = None @@ -29,8 +31,9 @@ async def m001_create_admin_table(db): if getenv("LNBITS_ALLOWED_USERS"): allowed_users = getenv("LNBITS_ALLOWED_USERS") - if getenv("LNBITS_ADMIN_USER"): - admin_user = getenv("LNBITS_ADMIN_USER") + if getenv("LNBITS_ADMIN_USERS"): + admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split()) + user = admin_users.split(',')[0] if getenv("LNBITS_DEFAULT_WALLET_NAME"): default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") @@ -53,32 +56,32 @@ async def m001_create_admin_table(db): await db.execute( """ CREATE TABLE IF NOT EXISTS admin ( - user TEXT, + "user" TEXT, site_title TEXT, site_tagline TEXT, site_description TEXT, - admin_user TEXT, + admin_users TEXT, allowed_users TEXT, default_wallet_name TEXT, data_folder TEXT, disabled_ext TEXT, force_https BOOLEAN, - service_fee INT, + service_fee REAL, funding_source TEXT ); """ ) await db.execute( """ - INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) + INSERT INTO admin ("user", site_title, site_tagline, site_description, admin_users, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( - user, + user.strip(), site_title, site_tagline, site_description, - admin_user, + admin_users[1:], allowed_users, default_wallet_name, data_folder, diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index c38f17f48..4080ff018 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -1,18 +1,35 @@ -from typing import NamedTuple from sqlite3 import Row +from typing import List, Optional -class Admin(NamedTuple): +from fastapi import Query +from pydantic import BaseModel + + +class UpdateAdminSettings(BaseModel): + site_title: Optional[str] + site_tagline: Optional[str] + site_description: Optional[str] + allowed_users: Optional[str] + admin_users: Optional[str] + default_wallet_name: Optional[str] + data_folder: Optional[str] + disabled_ext: Optional[str] + force_https: Optional[bool] + service_fee: Optional[float] + funding_source: Optional[str] + +class Admin(BaseModel): user: str - site_title: str - site_tagline: str - site_description:str - allowed_users: str - admin_user: str + site_title: Optional[str] + site_tagline: Optional[str] + site_description: Optional[str] + allowed_users: Optional[str] + admin_users: str default_wallet_name: str data_folder: str disabled_ext: str - force_https: str - service_fee: str + force_https: Optional[bool] = Query(True) + service_fee: float funding_source: str @classmethod @@ -20,16 +37,16 @@ class Admin(NamedTuple): data = dict(row) return cls(**data) -class Funding(NamedTuple): +class Funding(BaseModel): id: str backend_wallet: str - endpoint: str - port: str - read_key: str - invoice_key: str - admin_key: str - cert: str - balance: int + endpoint: str = Query(None) + port: str = Query(None) + read_key: str = Query(None) + invoice_key: str = Query(None) + admin_key: str = Query(None) + cert: str = Query(None) + balance: int = Query(None) selected: int @classmethod diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 87cf09efa..a6b456259 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -87,7 +87,7 @@ @@ -442,13 +442,14 @@ site_title: '{{admin.site_title}}', tagline: '{{admin.site_tagline}}', description: '{{admin.site_description}}', - admin_user: '{{admin.admin_user}}', - service_fee: parseInt('{{admin.service_fee}}'), + admin_users: '{{admin.admin_users}}', + service_fee: parseFloat('{{admin.service_fee}}'), default_wallet_name: '{{admin.default_wallet_name}}', data_folder: '{{admin.data_folder}}', funding_source_primary: '{{admin.funding_source}}', disabled_ext: '{{admin.disabled_ext}}'.split(','), edited: [], + funding: {}, senddata: {} } }, @@ -528,15 +529,27 @@ }, UpdateLNbits: function () { var self = this - console.log(self.data.admin) + let {site_title, admin_users, default_wallet_name, data_folder, disabled_ext, service_fee, funding_source_primary} = this.data.admin + let data = { + site_title, + site_tagline: this.data.admin.tagline, + site_description: this.data.admin.description, + admin_users: admin_users.toString(), + default_wallet_name, + data_folder, + disabled_ext: disabled_ext.toString(), + service_fee, + funding_source: funding_source_primary} + console.log(data) LNbits.api .request( 'POST', '/admin/api/v1/admin/', self.g.user.wallets[0].adminkey, - self.data.admin + data ) .then(function (response) { + console.log(response.data) self.$q.notify({ type: 'positive', message: diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py index 5e17919c5..00a0c99fc 100644 --- a/lnbits/extensions/admin/views.py +++ b/lnbits/extensions/admin/views.py @@ -1,20 +1,33 @@ -from quart import g, render_template, request, jsonify -import json +from email.policy import default +from os import getenv -from lnbits.decorators import check_user_exists, validate_uuids +from fastapi import Request +from fastapi.params import Depends +from fastapi.templating import Jinja2Templates +from starlette.responses import HTMLResponse + +from lnbits.core.models import User +from lnbits.decorators import check_user_exists from lnbits.extensions.admin import admin_ext -from lnbits.core.crud import get_user, create_account +from lnbits.requestvars import g + +from . import admin_ext, admin_renderer from .crud import get_admin, get_funding -from lnbits.settings import WALLET +templates = Jinja2Templates(directory="templates") -@admin_ext.route("/") -@validate_uuids(["usr"], required=True) -@check_user_exists() -async def index(): - user_id = g.user +@admin_ext.get("/", response_class=HTMLResponse) +async def index(request: Request, user: User = Depends(check_user_exists)): admin = await get_admin() + print(g()) + funding = [f.dict() for f in await get_funding()] - funding = [{**funding._asdict()} for funding in await get_funding()] - - return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding) + print("ADMIN", admin.dict()) + return admin_renderer().TemplateResponse( + "admin/index.html", { + "request": request, + "user": user.dict(), + "admin": admin.dict(), + "funding": funding + } + ) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 2a61b6f55..b2c65be25 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -1,41 +1,42 @@ -from quart import jsonify, g, request from http import HTTPStatus -from .crud import update_wallet_balance -from lnbits.extensions.admin import admin_ext -from lnbits.decorators import api_check_wallet_key, api_validate_post_request -from lnbits.core.crud import get_wallet -from .crud import get_admin,update_admin -import json -@admin_ext.route("/api/v1/admin//", methods=["GET"]) -@api_check_wallet_key("admin") -async def api_update_balance(wallet_id, topup_amount): - print(g.data.wallet) +from fastapi import Body, Depends, Request +from starlette.exceptions import HTTPException + +from lnbits.core.crud import get_wallet +from lnbits.decorators import WalletTypeInfo, require_admin_key +from lnbits.extensions.admin import admin_ext +from lnbits.extensions.admin.models import Admin, UpdateAdminSettings + +from .crud import get_admin, update_admin, update_wallet_balance + + +@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK) +async def api_update_balance(wallet_id, topup_amount, g: WalletTypeInfo = Depends(require_admin_key)): + print(g.wallet) try: wallet = await get_wallet(wallet_id) except: - return ( - jsonify({"error": "Not allowed: not an admin"}), - HTTPStatus.FORBIDDEN, - ) + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) print(wallet) print(topup_amount) - return jsonify({"status": "Success"}), HTTPStatus.OK + return {"status": "Success"} -@admin_ext.route("/api/v1/admin/", methods=["POST"]) -@api_check_wallet_key("admin") -@api_validate_post_request(schema={}) -async def api_update_admin(): - body = await request.get_json() +@admin_ext.post("/api/v1/admin/", status_code=HTTPStatus.OK) +async def api_update_admin( + request: Request, + data: UpdateAdminSettings = Body(...), + g: WalletTypeInfo = Depends(require_admin_key) + ): admin = await get_admin() - print(g.wallet[2]) - print(body["admin_user"]) - if not admin.admin_user == g.wallet[2] and admin.admin_user != None: - return ( - jsonify({"error": "Not allowed: not an admin"}), - HTTPStatus.FORBIDDEN, - ) - updated = await update_admin(body) + print(data) + if not admin.user == g.wallet.user: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) + updated = await update_admin(user=g.wallet.user, **data.dict()) print(updated) - return jsonify({"status": "Success"}), HTTPStatus.OK \ No newline at end of file + return {"status": "Success"} From b4885de9e2fcf598994dd5ca2360cc29629aaa23 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:18:58 +0000 Subject: [PATCH 120/614] remove core admin html (renamed for now) --- lnbits/core/templates/core/core_admin.html | 717 +++++++++++++++++++++ 1 file changed, 717 insertions(+) create mode 100644 lnbits/core/templates/core/core_admin.html diff --git a/lnbits/core/templates/core/core_admin.html b/lnbits/core/templates/core/core_admin.html new file mode 100644 index 000000000..835fc00a3 --- /dev/null +++ b/lnbits/core/templates/core/core_admin.html @@ -0,0 +1,717 @@ +{% extends "public.html" %} {% from "macros.jinja" import window_vars with +context %} {% block page %} +
+
+ + +

+
Welcome to LNbits
+

+
+ Fill in the information below to setup your LNbits instance. Details + can be changed later. +
+

+ + +
+ +
Branding
+
+
+ +
+
+ +
+
+
+
+ + + +
+
+ + + +
+
+ +
Service settings
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ Funding source information (at least one required)
*if installed through RaspiBlitz, MyNode, etc, details + should be filled in for you
+
+ + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+ View project in GitHub + Donate +
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(funding) }} + +{% endblock %} From c41b4e714c2e20c36d6a262c77966598b1fd85c4 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:21:38 +0000 Subject: [PATCH 121/614] typo --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index bfaeb5152..4192f82ec 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,7 @@ DEBUG=false LNBITS_ADMIN_USERS="" # User IDs seperated by comma LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access -LNBITS_ADMIN_UI=false # Extensions only admin can access +LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma From 922206b365cff523a12bf5a8014b544aa88398f3 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:22:23 +0000 Subject: [PATCH 122/614] add admin_ui env --- lnbits/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lnbits/settings.py b/lnbits/settings.py index 3f4e31cc5..43cb87cbe 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,5 +1,6 @@ import importlib import subprocess +from email.policy import default from os import path from typing import List @@ -27,6 +28,7 @@ LNBITS_DATABASE_URL = env.str("LNBITS_DATABASE_URL", default=None) LNBITS_ALLOWED_USERS: List[str] = [ x.strip(" ") for x in env.list("LNBITS_ALLOWED_USERS", default=[], subcast=str) ] +LNBITS_ADMIN_UI = env.bool("LNBITS_ADMIN_UI", default=False) LNBITS_ADMIN_USERS: List[str] = [ x.strip(" ") for x in env.list("LNBITS_ADMIN_USERS", default=[], subcast=str) ] From b47ed3068105f2526365f6708efe5134a5fe1c0e Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:23:16 +0000 Subject: [PATCH 123/614] add db config at startup --- lnbits/commands.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lnbits/commands.py b/lnbits/commands.py index 0f7454f23..8c39c338f 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -52,6 +52,25 @@ def bundle_vendored(): with open(outputpath, "w") as f: f.write(output) +async def get_admin_settings(): + from lnbits.extensions.admin.models import Admin + + async with core_db.connect() as conn: + + if conn.type == SQLITE: + exists = await conn.fetchone( + "SELECT * FROM sqlite_master WHERE type='table' AND name='admin'" + ) + elif conn.type in {POSTGRES, COCKROACH}: + exists = await conn.fetchone( + "SELECT * FROM information_schema.tables WHERE table_name = 'admin'" + ) + if not exists: + return False + + row = await conn.fetchone("SELECT * from admin") + + return Admin(**row) if row else None async def migrate_databases(): """Creates the necessary databases if they don't exist already; or migrates them.""" From 4bf17c5df2d07443c972be9356be710e4578c79b Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:23:53 +0000 Subject: [PATCH 124/614] get admin settings at startup --- lnbits/app.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lnbits/app.py b/lnbits/app.py index 075828ef0..7ff9e4eb5 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -18,6 +18,7 @@ from loguru import logger import lnbits.settings from lnbits.core.tasks import register_task_listeners +from .commands import get_admin_settings from .core import core_app from .core.views.generic import core_html_routes from .helpers import ( @@ -42,6 +43,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI: """Create application factory. :param config_object: The configuration object to use. """ +<<<<<<< HEAD configure_logger() app = FastAPI( @@ -53,6 +55,14 @@ def create_app(config_object="lnbits.settings") -> FastAPI: }, ) app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static") +======= + app = FastAPI() + + if lnbits.settings.LNBITS_ADMIN_UI: + check_settings(app) + + app.mount("/static", StaticFiles(directory="lnbits/static"), name="static") +>>>>>>> e3a1b3ae (get admin settings at startup) app.mount( "/core/static", StaticFiles(packages=[("lnbits.core", "static")]), @@ -64,7 +74,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: app.add_middleware( CORSMiddleware, allow_origins=origins, allow_methods=["*"], allow_headers=["*"] ) - g().config = lnbits.settings g().base_url = f"http://{lnbits.settings.HOST}:{lnbits.settings.PORT}" @@ -101,6 +110,18 @@ def create_app(config_object="lnbits.settings") -> FastAPI: return app +def check_settings(app: FastAPI): + @app.on_event("startup") + async def check_settings_admin(): + while True: + admin_set = await get_admin_settings() + if admin_set : + break + print("ERROR:", admin_set) + await asyncio.sleep(5) + # admin_set = await get_admin_settings() + g().admin_conf = admin_set + def check_funding_source(app: FastAPI) -> None: @app.on_event("startup") async def check_wallet_status(): From 48d6a89e5ba9e1e082a2ff189b3307d685f521e4 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Sat, 12 Mar 2022 14:24:11 +0000 Subject: [PATCH 125/614] remove core admin.html --- lnbits/core/templates/core/admin.html | 717 -------------------------- 1 file changed, 717 deletions(-) delete mode 100644 lnbits/core/templates/core/admin.html diff --git a/lnbits/core/templates/core/admin.html b/lnbits/core/templates/core/admin.html deleted file mode 100644 index e81765550..000000000 --- a/lnbits/core/templates/core/admin.html +++ /dev/null @@ -1,717 +0,0 @@ -{% extends "public.html" %} {% from "macros.jinja" import window_vars with -context %} {% block page %} -
-
- - -

-
Welcome to LNbits
-

-
- Fill in the information below to setup your LNbits instance. Details - can be changed later. -
-

- - -
- -
Branding
-
-
- -
-
- -
-
-
-
- - - -
-
- - - -
-
- -
Service settings
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
- Funding source information (at least one required)
*if installed through RaspiBlitz, MyNode, etc, details - should be filled in for you
-
- - - - - - - - - - - - - -
-
- -
-
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
-
-
-
- -
-
- -
-
-
-
-
- - - - -
-
- -
-
- -
-
-
-
-
-
- -
- - -
-
-
-
- View project in GitHub - Donate -
-
-
-
-
-{% endblock %} {% block scripts %} {{ window_vars(funding) }} - -{% endblock %} From 87bee88de403723b9d98f64716c573e21289a50a Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Fri, 18 Mar 2022 16:55:31 +0000 Subject: [PATCH 126/614] refactor ui --- .../admin/templates/admin/index.html | 727 +++++++++++++++++- 1 file changed, 712 insertions(+), 15 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index a6b456259..65ac9f336 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1,6 +1,670 @@ {% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block page %} +
+
+ +
+
+ + + + + + +
+
+ + + + +
Wallets Management
+
+
+
+
+

Funding Source Info

+
    + {%raw%} +
  • Funding Source: {{data.admin.funding_source}}
  • +
  • Balance: {{data.admin.balance / 1000}} sats
  • + {%endraw%} +
+
+
+
+
+
+

Active Funding

+ +
+
+
+ +

TopUp a wallet

+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+

Funding Sources

+ + + + + + + + + + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+ +
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+
+
+ +
+ Save +
+
+
+ + +
User Management
+
+

+ Super Admin: {% raw + %}{{this.data.admin.user}}{% endraw %} +

+
+
+

Admin Users

+ + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+

Allowed Users

+ + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+
+

Admin Extensions

+ +
+
+
+

Disabled Extensions

+ +
+
+
+
+ Save +
+
+
+ + +
Server Management
+
+
+
+
+

Server Info

+
    + {%raw%} +
  • SQlite: {{data.admin.data_folder}}
  • +
  • Postgres: {{data.admin.database_url}}
  • + {%endraw%} +
+
+
+
+
+
+

Service Fee

+ +
+
+
+

Miscelaneous

+ + + Force HTTPS + Prefer secure URLs + + + + + + + + Hide API + Hides wallet api, extensions can choose to honor + + + + + +
+
+
+
+ +
+ Save +
+
+
+ + +
UI Management
+
+
+
+
+

Site Title

+ +
+
+
+

Site Tagline

+ +
+
+
+
+

Site Description

+ +
+
+
+
+

Default Wallet Name

+ +
+
+
+

Denomination

+ +
+
+
+
+
+

Themes

+ +
+
+
+

Advertisement Slots

+ + + +
+ {% raw %} + + {{ space.slice(0, 8) + " ... " + space.slice(-8) }} + + {% endraw %} +
+
+
+
+
+ +
+ Save +
+
+
+
+
+
+
+
+

Admin

-
+
@@ -426,6 +1090,7 @@ return { wallet: {data: {}}, cancel: {}, + tab: 'funding', data: { funding_source: [ 'CLightningWallet', @@ -436,24 +1101,14 @@ 'LnbitsWallet', 'OpenNodeWallet' ], - + admin: { - user: '{{ user.id }}', - site_title: '{{admin.site_title}}', - tagline: '{{admin.site_tagline}}', - description: '{{admin.site_description}}', - admin_users: '{{admin.admin_users}}', - service_fee: parseFloat('{{admin.service_fee}}'), - default_wallet_name: '{{admin.default_wallet_name}}', - data_folder: '{{admin.data_folder}}', - funding_source_primary: '{{admin.funding_source}}', - disabled_ext: '{{admin.disabled_ext}}'.split(','), edited: [], funding: {}, senddata: {} } }, - + themes: ['classic', 'bitcoin', 'flamingo', 'mint', 'autumn', 'monochrome', 'salvador'], options: [ 'bleskomat', 'captcha', @@ -489,9 +1144,51 @@ for (i = 0; i < funding.length; i++) { self.data.admin.funding[funding[i].backend_wallet] = funding[i] } - console.log(self.data.admin) + let settings = JSON.parse('{{ settings | tojson|safe }}') + settings.balance = '{{ balance }}' + this.data.admin = {...this.data.admin, ...settings} + console.log(this.g.user) }, methods: { + addAdminUser(){ + let addUser = this.data.admin_users_add + let admin_users = this.data.admin.admin_users + if(addUser.length && !admin_users.includes(addUser)){ + admin_users.push(addUser) + this.data.admin.admin_users = admin_users + this.data.admin_users_add = "" + } + }, + removeAdminUser(user){ + let admin_users = this.data.admin.admin_users + this.data.admin.admin_users = admin_users.filter(u => u !== user) + }, + addAllowedUser(){ + let addUser = this.data.allowed_users_add + let allowed_users = this.data.admin.allowed_users + if(addUser.length && !allowed_users.includes(addUser)){ + allowed_users.push(addUser) + this.data.admin.allowed_users = allowed_users + this.data.allowed_users_add = "" + } + }, + removeAllowedUser(user){ + let allowed_users = this.data.admin.allowed_users + this.data.admin.allowed_users = allowed_users.filter(u => u !== user) + }, + addAdSpace(){ + let adSpace = this.data.ad_space_add + let spaces = this.data.admin.ad_space + if(adSpace.length && !spaces.includes(adSpace)){ + spaces.push(adSpace) + this.data.admin.ad_space = spaces + this.data.ad_space_add = "" + } + }, + removeAdSpace(ad){ + let spaces = this.data.admin.ad_space + this.data.admin.ad_space = spaces.filter(s => s !== ad) + }, topupWallet: function () { var self = this LNbits.api From 8c1c7d13b87ba7b7408dccd719a9a9c66e6e4325 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Fri, 18 Mar 2022 16:59:06 +0000 Subject: [PATCH 127/614] make it work from g() --- lnbits/app.py | 34 +++--- lnbits/config.py | 62 ++++++++++ lnbits/extensions/admin/crud.py | 2 +- lnbits/extensions/admin/migrations.py | 162 +++++++++++++++++--------- lnbits/extensions/admin/models.py | 27 +++-- lnbits/extensions/admin/views.py | 8 +- lnbits/settings.py | 2 +- 7 files changed, 218 insertions(+), 79 deletions(-) create mode 100644 lnbits/config.py diff --git a/lnbits/app.py b/lnbits/app.py index 7ff9e4eb5..1ffedb54c 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -19,6 +19,7 @@ import lnbits.settings from lnbits.core.tasks import register_task_listeners from .commands import get_admin_settings +from .config import WALLET, conf from .core import core_app from .core.views.generic import core_html_routes from .helpers import ( @@ -29,7 +30,8 @@ from .helpers import ( url_for_vendored, ) from .requestvars import g -from .settings import WALLET + +# from .settings import WALLET from .tasks import ( catch_everything_and_restart, check_pending_payments, @@ -43,7 +45,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: """Create application factory. :param config_object: The configuration object to use. """ -<<<<<<< HEAD configure_logger() app = FastAPI( @@ -55,20 +56,18 @@ def create_app(config_object="lnbits.settings") -> FastAPI: }, ) app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static") -======= - app = FastAPI() - - if lnbits.settings.LNBITS_ADMIN_UI: - check_settings(app) - - app.mount("/static", StaticFiles(directory="lnbits/static"), name="static") ->>>>>>> e3a1b3ae (get admin settings at startup) app.mount( "/core/static", StaticFiles(packages=[("lnbits.core", "static")]), name="core_static", ) + if lnbits.settings.LNBITS_ADMIN_UI: + g().admin_conf = conf + check_settings(app) + + g().WALLET = WALLET + origins = ["*"] app.add_middleware( @@ -109,18 +108,27 @@ def create_app(config_object="lnbits.settings") -> FastAPI: return app - def check_settings(app: FastAPI): @app.on_event("startup") async def check_settings_admin(): + + def removeEmptyString(arr): + return list(filter(None, arr)) + while True: admin_set = await get_admin_settings() if admin_set : break print("ERROR:", admin_set) await asyncio.sleep(5) - # admin_set = await get_admin_settings() - g().admin_conf = admin_set + + admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(',')) + admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(',')) + admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(',')) + admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(',')) + admin_set.theme = removeEmptyString(admin_set.theme.split(',')) + admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(',')) + g().admin_conf = conf.copy(update=admin_set.dict()) def check_funding_source(app: FastAPI) -> None: @app.on_event("startup") diff --git a/lnbits/config.py b/lnbits/config.py new file mode 100644 index 000000000..02e8cf537 --- /dev/null +++ b/lnbits/config.py @@ -0,0 +1,62 @@ +import importlib +import json +from os import getenv, path +from typing import List, Optional + +from pydantic import BaseSettings, Field, validator + +wallets_module = importlib.import_module("lnbits.wallets") +wallet_class = getattr( + wallets_module, getenv("LNBITS_BACKEND_WALLET_CLASS", "VoidWallet") +) + +WALLET = wallet_class() + +def list_parse_fallback(v): + try: + return json.loads(v) + except Exception as e: + return v.replace(' ','').split(',') + +class Settings(BaseSettings): + # users + admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS") + allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS") + admin_ext: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_EXTENSIONS") + disabled_ext: List[str] = Field(default_factory=list, env="LNBITS_DISABLED_EXTENSIONS") + funding_source: str = Field(default="VoidWallet", env="LNBITS_BACKEND_WALLET_CLASS") + # ops + data_folder: str = Field(default=None, env="LNBITS_DATA_FOLDER") + database_url: str = Field(default=None, env="LNBITS_DATABASE_URL") + force_https: bool = Field(default=True, env="LNBITS_FORCE_HTTPS") + service_fee: float = Field(default=0, env="LNBITS_SERVICE_FEE") + hide_api: bool = Field(default=False, env="LNBITS_HIDE_API") + denomination: str = Field(default="sats", env="LNBITS_DENOMINATION") + # Change theme + site_title: str = Field(default=None, env="LNBITS_SITE_TITLE") + site_tagline: str = Field(default=None, env="LNBITS_SITE_TAGLINE") + site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") + default_wallet_name: str = Field(default=None, env="LNBITS_DEFAULT_WALLET_NAME") + theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS") + ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") + # .env + env: Optional[str] + debug: Optional[str] + host: Optional[str] + port: Optional[str] + lnbits_path: Optional[str] = path.dirname(path.realpath(__file__)) + + # @validator('admin_users', 'allowed_users', 'admin_ext', 'disabled_ext', pre=True) + # def validate(cls, val): + # print(val) + # return val.split(',') + + class Config: + env_file = ".env" + env_file_encoding = "utf-8" + case_sensitive = False + json_loads = list_parse_fallback + + +conf = Settings() +WALLET = wallet_class() diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 872d6c97b..6fccb8ee6 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -40,7 +40,7 @@ async def update_admin(user: str, **kwargs) -> Admin: # new_settings = await get_admin() # return new_settings -async def get_admin() -> List[Admin]: +async def get_admin() -> Admin: row = await db.fetchone("SELECT * FROM admin") return Admin(**row) if row else None diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 13b769232..574f772d1 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -2,93 +2,151 @@ from os import getenv from sqlalchemy.exc import OperationalError # type: ignore +from lnbits.config import conf from lnbits.helpers import urlsafe_short_hash async def m001_create_admin_table(db): - user = None - site_title = None - site_tagline = None - site_description = None - allowed_users = None - admin_users = None - default_wallet_name = None - data_folder = None - disabled_ext = None - force_https = True - service_fee = 0 - funding_source = "" + # users/server + user = conf.admin_users[0] + admin_users = ",".join(conf.admin_users) + allowed_users = ",".join(conf.allowed_users) + admin_ext = ",".join(conf.admin_ext) + disabled_ext = ",".join(conf.disabled_ext) + funding_source = conf.funding_source + #operational + data_folder = conf.data_folder + database_url = conf.database_url + force_https = conf.force_https + service_fee = conf.service_fee + hide_api = conf.hide_api + denomination = conf.denomination + # Theme'ing + site_title = conf.site_title + site_tagline = conf.site_tagline + site_description = conf.site_description + default_wallet_name = conf.default_wallet_name + theme = ",".join(conf.theme) + ad_space = ",".join(conf.ad_space) - if getenv("LNBITS_SITE_TITLE"): - site_title = getenv("LNBITS_SITE_TITLE") + # if getenv("LNBITS_ADMIN_EXTENSIONS"): + # admin_ext = getenv("LNBITS_ADMIN_EXTENSIONS") - if getenv("LNBITS_SITE_TAGLINE"): - site_tagline = getenv("LNBITS_SITE_TAGLINE") + # if getenv("LNBITS_DATABASE_URL"): + # database_url = getenv("LNBITS_DATABASE_URL") - if getenv("LNBITS_SITE_DESCRIPTION"): - site_description = getenv("LNBITS_SITE_DESCRIPTION") + # if getenv("LNBITS_HIDE_API"): + # hide_api = getenv("LNBITS_HIDE_API") - if getenv("LNBITS_ALLOWED_USERS"): - allowed_users = getenv("LNBITS_ALLOWED_USERS") + # if getenv("LNBITS_THEME_OPTIONS"): + # theme = getenv("LNBITS_THEME_OPTIONS") - if getenv("LNBITS_ADMIN_USERS"): - admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split()) - user = admin_users.split(',')[0] + # if getenv("LNBITS_AD_SPACE"): + # ad_space = getenv("LNBITS_AD_SPACE") - if getenv("LNBITS_DEFAULT_WALLET_NAME"): - default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") + # if getenv("LNBITS_SITE_TITLE"): + # site_title = getenv("LNBITS_SITE_TITLE") - if getenv("LNBITS_DATA_FOLDER"): - data_folder = getenv("LNBITS_DATA_FOLDER") + # if getenv("LNBITS_SITE_TAGLINE"): + # site_tagline = getenv("LNBITS_SITE_TAGLINE") - if getenv("LNBITS_DISABLED_EXTENSIONS"): - disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS") + # if getenv("LNBITS_SITE_DESCRIPTION"): + # site_description = getenv("LNBITS_SITE_DESCRIPTION") - if getenv("LNBITS_FORCE_HTTPS"): - force_https = getenv("LNBITS_FORCE_HTTPS") + # if getenv("LNBITS_ALLOWED_USERS"): + # allowed_users = getenv("LNBITS_ALLOWED_USERS") - if getenv("LNBITS_SERVICE_FEE"): - service_fee = getenv("LNBITS_SERVICE_FEE") + # if getenv("LNBITS_ADMIN_USERS"): + # admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split()) + # user = admin_users.split(',')[0] + + # if getenv("LNBITS_DEFAULT_WALLET_NAME"): + # default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") - if getenv("LNBITS_BACKEND_WALLET_CLASS"): - funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS") + # if getenv("LNBITS_DATA_FOLDER"): + # data_folder = getenv("LNBITS_DATA_FOLDER") + + # if getenv("LNBITS_DISABLED_EXTENSIONS"): + # disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS") + + # if getenv("LNBITS_FORCE_HTTPS"): + # force_https = getenv("LNBITS_FORCE_HTTPS") + + # if getenv("LNBITS_SERVICE_FEE"): + # service_fee = getenv("LNBITS_SERVICE_FEE") + + # if getenv("LNBITS_DENOMINATION"): + # denomination = getenv("LNBITS_DENOMINATION", "sats") + + # if getenv("LNBITS_BACKEND_WALLET_CLASS"): + # funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS") await db.execute( """ CREATE TABLE IF NOT EXISTS admin ( - "user" TEXT, + "user" TEXT PRIMARY KEY, + admin_users TEXT, + allowed_users TEXT, + admin_ext TEXT, + disabled_ext TEXT, + funding_source TEXT, + data_folder TEXT, + database_url TEXT, + force_https BOOLEAN, + service_fee REAL, + hide_api BOOLEAN, + denomination TEXT, site_title TEXT, site_tagline TEXT, site_description TEXT, - admin_users TEXT, - allowed_users TEXT, default_wallet_name TEXT, - data_folder TEXT, - disabled_ext TEXT, - force_https BOOLEAN, - service_fee REAL, - funding_source TEXT + theme TEXT, + ad_space TEXT ); """ ) await db.execute( """ - INSERT INTO admin ("user", site_title, site_tagline, site_description, admin_users, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - user.strip(), + INSERT INTO admin ( + "user", + admin_users, + allowed_users, + admin_ext, + disabled_ext, + funding_source, + data_folder, + database_url, + force_https, + service_fee, + hide_api, + denomination, site_title, site_tagline, site_description, - admin_users[1:], - allowed_users, default_wallet_name, - data_folder, + theme, + ad_space) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + user, + admin_users, + allowed_users, + admin_ext, disabled_ext, + funding_source, + data_folder, + database_url, force_https, service_fee, - funding_source, + hide_api, + denomination, + site_title, + site_tagline, + site_description, + default_wallet_name, + theme, + ad_space, ), ) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 4080ff018..f7c64de55 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -2,7 +2,7 @@ from sqlite3 import Row from typing import List, Optional from fastapi import Query -from pydantic import BaseModel +from pydantic import BaseModel, Field class UpdateAdminSettings(BaseModel): @@ -19,18 +19,27 @@ class UpdateAdminSettings(BaseModel): funding_source: Optional[str] class Admin(BaseModel): + # users user: str + admin_users: Optional[str] + allowed_users: Optional[str] + admin_ext: Optional[str] + disabled_ext: Optional[str] + funding_source: Optional[str] + # ops + data_folder: Optional[str] + database_url: Optional[str] + force_https: bool = Field(default=True) + service_fee: float = Field(default=0) + hide_api: bool = Field(default=False) + # Change theme site_title: Optional[str] site_tagline: Optional[str] site_description: Optional[str] - allowed_users: Optional[str] - admin_users: str - default_wallet_name: str - data_folder: str - disabled_ext: str - force_https: Optional[bool] = Query(True) - service_fee: float - funding_source: str + default_wallet_name: Optional[str] + denomination: str = Field(default="sats") + theme: Optional[str] + ad_space: Optional[str] @classmethod def from_row(cls, row: Row) -> "Admin": diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py index 00a0c99fc..105f05a10 100644 --- a/lnbits/extensions/admin/views.py +++ b/lnbits/extensions/admin/views.py @@ -19,15 +19,17 @@ templates = Jinja2Templates(directory="templates") @admin_ext.get("/", response_class=HTMLResponse) async def index(request: Request, user: User = Depends(check_user_exists)): admin = await get_admin() - print(g()) funding = [f.dict() for f in await get_funding()] - + error, balance = await g().WALLET.status() print("ADMIN", admin.dict()) + print(g().admin_conf) return admin_renderer().TemplateResponse( "admin/index.html", { "request": request, "user": user.dict(), "admin": admin.dict(), - "funding": funding + "funding": funding, + "settings": g().admin_conf.dict(), + "balance": balance } ) diff --git a/lnbits/settings.py b/lnbits/settings.py index 43cb87cbe..ed5c77f7e 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -4,7 +4,7 @@ from email.policy import default from os import path from typing import List -from environs import Env # type: ignore +from environs import Env env = Env() env.read_env() From a72ed98997656a709b00d171c27c387c26dfe0bd Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 10:28:07 +0000 Subject: [PATCH 128/614] topup wallet endpoint --- lnbits/extensions/admin/crud.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 6fccb8ee6..683558f94 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -1,26 +1,31 @@ +import json from typing import List, Optional from lnbits.core.crud import create_payment from lnbits.helpers import urlsafe_short_hash from lnbits.settings import * +from lnbits.tasks import internal_invoice_queue from . import db from .models import Admin, Funding -def update_wallet_balance(wallet_id: str, amount: int) -> str: +async def update_wallet_balance(wallet_id: str, amount: int) -> str: temp_id = f"temp_{urlsafe_short_hash()}" internal_id = f"internal_{urlsafe_short_hash()}" - create_payment( + + payment = await create_payment( wallet_id=wallet_id, checking_id=internal_id, payment_request="admin_internal", payment_hash="admin_internal", - amount=amount * 1000, + amount=amount*1000, memo="Admin top up", pending=False, ) - return "success" + # manually send this for now + await internal_invoice_queue.put(internal_id) + return payment async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) From 42a9af986ab3620bdb61270961c6adbda4a66c95 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 10:28:44 +0000 Subject: [PATCH 129/614] update admin settings in db --- lnbits/extensions/admin/models.py | 29 +++++++++++++++++----------- lnbits/extensions/admin/views_api.py | 8 ++++---- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index f7c64de55..36d9b8152 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -6,17 +6,24 @@ from pydantic import BaseModel, Field class UpdateAdminSettings(BaseModel): - site_title: Optional[str] - site_tagline: Optional[str] - site_description: Optional[str] - allowed_users: Optional[str] - admin_users: Optional[str] - default_wallet_name: Optional[str] - data_folder: Optional[str] - disabled_ext: Optional[str] - force_https: Optional[bool] - service_fee: Optional[float] - funding_source: Optional[str] + # users + admin_users: str = Query(None) + allowed_users: str = Query(None) + admin_ext: str = Query(None) + disabled_ext: str = Query(None) + funding_source: str = Query(None) + # ops + force_https: bool = Query(None) + service_fee: float = Query(None, ge=0) + hide_api: bool = Query(None) + # Change theme + site_title: str = Query(None) + site_tagline: str = Query(None) + site_description: str = Query(None) + default_wallet_name: str = Query(None) + denomination: str = Query(None) + theme: str = Query(None) + ad_space: str = Query(None) class Admin(BaseModel): # users diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index b2c65be25..cb526aa54 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -12,16 +12,16 @@ from .crud import get_admin, update_admin, update_wallet_balance @admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK) -async def api_update_balance(wallet_id, topup_amount, g: WalletTypeInfo = Depends(require_admin_key)): - print(g.wallet) +async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)): try: wallet = await get_wallet(wallet_id) except: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" ) - print(wallet) - print(topup_amount) + + await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount)) + return {"status": "Success"} From 2ebb4448d5ff8daa3b1c4729ce33dd4c5c744c7b Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 10:29:18 +0000 Subject: [PATCH 130/614] update settings and topup logic --- .../admin/templates/admin/index.html | 91 ++++++++++++------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 65ac9f336..e9ddc7c47 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -30,7 +30,7 @@
- + @@ -61,7 +61,7 @@
- +

TopUp a wallet

@@ -87,13 +87,13 @@
- +
@@ -577,7 +577,6 @@

Site Description

s !== ad) }, - topupWallet: function () { - var self = this + topupWallet() { LNbits.api .request( 'GET', '/admin/api/v1/admin/' + - self.wallet.id + + this.wallet.data.id + '/' + - self.wallet.data.amount, - self.g.user.wallets[0].adminkey + this.wallet.data.amount, + this.g.user.wallets[0].adminkey ) - .then(function (response) { - self.$q.notify({ + .then((response) => { + this.$q.notify({ type: 'positive', message: - 'Success! Added ' + - self.wallet.amount + - ' to ' + - self.wallet.id, + 'Success! Added ' + + this.wallet.data.amount + + ' to ' + + this.wallet.data.id, icon: null }) + this.wallet.data = {} }) .catch(function (error) { LNbits.utils.notifyApiError(error) @@ -1224,36 +1224,59 @@ self.data.admin.edited.push(source) console.log(self.data.admin.edited) }, - UpdateLNbits: function () { - var self = this - let {site_title, admin_users, default_wallet_name, data_folder, disabled_ext, service_fee, funding_source_primary} = this.data.admin + UpdateLNbits() { + let { + admin_users, + allowed_users, + admin_ext, + disabled_ext, + funding_source, + force_https, + service_fee, + hide_api, + site_title, + site_tagline, + site_description, + default_wallet_name, + denomination, + theme, + ad_space + } = this.data.admin + //console.log("this", this.data.admin) let data = { - site_title, - site_tagline: this.data.admin.tagline, - site_description: this.data.admin.description, - admin_users: admin_users.toString(), - default_wallet_name, - data_folder, + admin_users: admin_users.toString(), + allowed_users: allowed_users.toString(), + admin_ext: admin_ext.toString(), disabled_ext: disabled_ext.toString(), - service_fee, - funding_source: funding_source_primary} + funding_source, + force_https, + service_fee, + hide_api, + site_title, + site_tagline, + site_description, + default_wallet_name, + denomination, + theme: theme.toString(), + ad_space: ad_space.toString() + } console.log(data) LNbits.api .request( 'POST', '/admin/api/v1/admin/', - self.g.user.wallets[0].adminkey, + this.g.user.wallets[0].adminkey, data ) - .then(function (response) { + .then(response => { console.log(response.data) - self.$q.notify({ + this.$q.notify({ type: 'positive', message: 'Success! Added ' + - self.wallet.amount + + this.wallet.amount + ' to ' + - self.wallet.id, + this.wallet.id, icon: null }) }) From 3082a393436b88a24d860f87983560e758a02f66 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:34:47 +0000 Subject: [PATCH 131/614] make removeEmptyString fn as helper fn --- lnbits/app.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 1ffedb54c..c8f5c60a4 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -26,6 +26,7 @@ from .helpers import ( get_css_vendored, get_js_vendored, get_valid_extensions, + removeEmptyString, template_renderer, url_for_vendored, ) @@ -112,9 +113,6 @@ def check_settings(app: FastAPI): @app.on_event("startup") async def check_settings_admin(): - def removeEmptyString(arr): - return list(filter(None, arr)) - while True: admin_set = await get_admin_settings() if admin_set : From c419bd27ebcd212da37f27994e630df08bdba7d4 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:35:15 +0000 Subject: [PATCH 132/614] add some defaults --- lnbits/config.py | 6 +++--- lnbits/extensions/admin/models.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lnbits/config.py b/lnbits/config.py index 02e8cf537..b2fbfff12 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -33,10 +33,10 @@ class Settings(BaseSettings): hide_api: bool = Field(default=False, env="LNBITS_HIDE_API") denomination: str = Field(default="sats", env="LNBITS_DENOMINATION") # Change theme - site_title: str = Field(default=None, env="LNBITS_SITE_TITLE") - site_tagline: str = Field(default=None, env="LNBITS_SITE_TAGLINE") + site_title: str = Field(default="LNbits", env="LNBITS_SITE_TITLE") + site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE") site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") - default_wallet_name: str = Field(default=None, env="LNBITS_DEFAULT_WALLET_NAME") + default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME") theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 36d9b8152..0f25679dc 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -17,11 +17,11 @@ class UpdateAdminSettings(BaseModel): service_fee: float = Query(None, ge=0) hide_api: bool = Query(None) # Change theme - site_title: str = Query(None) - site_tagline: str = Query(None) + site_title: str = Query("LNbits") + site_tagline: str = Query("free and open-source lightning wallet") site_description: str = Query(None) - default_wallet_name: str = Query(None) - denomination: str = Query(None) + default_wallet_name: str = Query("LNbits wallet") + denomination: str = Query("sats") theme: str = Query(None) ad_space: str = Query(None) From 0897d0476356d1956711fc0f1c6448fbb88275fb Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:36:04 +0000 Subject: [PATCH 133/614] removeEmtpy sting as helper fn --- lnbits/helpers.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lnbits/helpers.py b/lnbits/helpers.py index e213240cd..e456f7150 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -154,8 +154,20 @@ def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> s url = f"{base}{endpoint}{url_params}" return url +def removeEmptyString(arr): + return list(filter(None, arr)) def template_renderer(additional_folders: List = []) -> Jinja2Templates: + if(settings.LNBITS_ADMIN_UI): + _ = g().admin_conf + settings.LNBITS_AD_SPACE = _.ad_space + settings.LNBITS_HIDE_API = _.hide_api + settings.LNBITS_SITE_TITLE = _.site_title + settings.LNBITS_DENOMINATION = _.denomination + settings.LNBITS_SITE_TAGLINE = _.site_tagline + settings.LNBITS_SITE_DESCRIPTION = _.site_description + settings.LNBITS_THEME_OPTIONS = _.theme + t = Jinja2Templates( loader=jinja2.FileSystemLoader( ["lnbits/templates", "lnbits/core/templates", *additional_folders] From 8c93aa304f0d484e908cd40c0a851b95fbbfd992 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:41:11 +0000 Subject: [PATCH 134/614] cleanup --- lnbits/extensions/admin/crud.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 683558f94..e14ad1949 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -1,9 +1,7 @@ -import json -from typing import List, Optional +from typing import List from lnbits.core.crud import create_payment from lnbits.helpers import urlsafe_short_hash -from lnbits.settings import * from lnbits.tasks import internal_invoice_queue from . import db @@ -37,14 +35,6 @@ async def update_admin(user: str, **kwargs) -> Admin: assert row, "Newly updated settings couldn't be retrieved" return Admin(**row) if row else None -# async def update_admin(user: str, **kwargs) -> Optional[Admin]: -# q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) -# await db.execute( -# f"UPDATE admin SET {q} WHERE user = ?", (*kwargs.values(), user) -# ) -# new_settings = await get_admin() -# return new_settings - async def get_admin() -> Admin: row = await db.fetchone("SELECT * FROM admin") return Admin(**row) if row else None From 5ce73697d47aa6eb96bb6d69c7da1e4521bc4943 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:42:28 +0000 Subject: [PATCH 135/614] make string to list --- lnbits/extensions/admin/views_api.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index cb526aa54..1d4e6a9c7 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -1,5 +1,6 @@ from http import HTTPStatus +# from config import conf from fastapi import Body, Depends, Request from starlette.exceptions import HTTPException @@ -7,6 +8,8 @@ from lnbits.core.crud import get_wallet from lnbits.decorators import WalletTypeInfo, require_admin_key from lnbits.extensions.admin import admin_ext from lnbits.extensions.admin.models import Admin, UpdateAdminSettings +from lnbits.helpers import removeEmptyString +from lnbits.requestvars import g from .crud import get_admin, update_admin, update_wallet_balance @@ -19,7 +22,7 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" ) - + await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount)) return {"status": "Success"} @@ -29,14 +32,24 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D async def api_update_admin( request: Request, data: UpdateAdminSettings = Body(...), - g: WalletTypeInfo = Depends(require_admin_key) + w: WalletTypeInfo = Depends(require_admin_key) ): admin = await get_admin() print(data) - if not admin.user == g.wallet.user: + if not admin.user == w.wallet.user: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" ) - updated = await update_admin(user=g.wallet.user, **data.dict()) - print(updated) + updated = await update_admin(user=w.wallet.user, **data.dict()) + + updated.admin_users = removeEmptyString(updated.admin_users.split(',')) + updated.allowed_users = removeEmptyString(updated.allowed_users.split(',')) + updated.admin_ext = removeEmptyString(updated.admin_ext.split(',')) + updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(',')) + updated.theme = removeEmptyString(updated.theme.split(',')) + updated.ad_space = removeEmptyString(updated.ad_space.split(',')) + + g().admin_conf = g().admin_conf.copy(update=updated.dict()) + + print(g().admin_conf) return {"status": "Success"} From c299927e7ca1e16c8bc29a98b5af38ed402496de Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 22 Mar 2022 11:42:47 +0000 Subject: [PATCH 136/614] success message --- lnbits/extensions/admin/templates/admin/index.html | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index e9ddc7c47..9aa4f12a8 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1273,10 +1273,7 @@ this.$q.notify({ type: 'positive', message: - 'Success! Added ' + - this.wallet.amount + - ' to ' + - this.wallet.id, + 'Success! Settings changed!', icon: null }) }) From 2a63fb191423b2f23de7f70e8197109a290f3689 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Thu, 14 Apr 2022 10:42:26 +0100 Subject: [PATCH 137/614] allow html to be passed to description --- lnbits/core/templates/core/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html index 68a7b7ede..146fc6ade 100644 --- a/lnbits/core/templates/core/index.html +++ b/lnbits/core/templates/core/index.html @@ -82,7 +82,7 @@ >
-

{{SITE_DESCRIPTION}}

+

{{SITE_DESCRIPTION | safe}}

From 3fbdac127adf9459d8af4453a802248f7c3d94fc Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Thu, 14 Apr 2022 16:37:13 +0100 Subject: [PATCH 138/614] update funding wallets --- lnbits/extensions/admin/crud.py | 12 + .../admin/templates/admin/index.html | 523 ++++++++++-------- lnbits/extensions/admin/views.py | 3 +- lnbits/extensions/admin/views_api.py | 19 +- 4 files changed, 315 insertions(+), 242 deletions(-) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index e14ad1949..dd39e8e42 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -39,6 +39,18 @@ async def get_admin() -> Admin: row = await db.fetchone("SELECT * FROM admin") return Admin(**row) if row else None +async def update_funding(data: Funding) -> Funding: + await db.execute( + """ + UPDATE funding + SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ? + WHERE id = ? + """, + (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,), + ) + row = await db.fetchone('SELECT * FROM funding WHERE "id" = ?', (data.id,)) + assert row, "Newly updated settings couldn't be retrieved" + return Funding(**row) if row else None async def get_funding() -> List[Funding]: rows = await db.fetchall("SELECT * FROM funding") diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 9aa4f12a8..d56b3d793 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -31,11 +31,11 @@
- - - -
Wallets Management
-
+ + + +
Wallets Management
+
@@ -62,43 +62,96 @@
-

TopUp a wallet

-
-
- -
-
-
- -
+

TopUp a wallet

+
+
+ +
-
- +
+
+
+
+ +

Funding Sources

- + {% raw %} + + + + + + + + + + + + + + {% endraw %} + +
+ + + + +
User Management
+
+

+ Super Admin: {% raw %}{{this.data.admin.user}}{% endraw %} +

+

Admin Users

+ hint="Users with admin privileges" + >
{% raw %} -
-
-
-

Allowed Users

- - - +
- {% raw %} - Allowed Users

+ - {{ user }} -
- {% endraw %} + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+

Admin Extensions

+ +
+
+
+

Disabled Extensions

+ +
+
+
+
+ Save +
+ + + + +
Server Management

-
-
-
-

Admin Extensions

- -
-
-
-

Disabled Extensions

- -
-
-
-
- Save -
-
-
- - -
Server Management
-

Server Info

    {%raw%} -
  • SQlite: {{data.admin.data_folder}}
  • -
  • Postgres: {{data.admin.database_url}}
  • +
  • + SQlite: {{data.admin.data_folder}} +
  • +
  • + Postgres: {{data.admin.database_url}} +
  • {%endraw%}

@@ -520,7 +576,10 @@ Hide API - Hides wallet api, extensions can choose to honor + Hides wallet api, extensions can choose to + honor
-
- -
- Save -
-
-
- - -
UI Management
-
+
+ +
+ Save +
+ + + + +
UI Management
+
@@ -575,15 +629,15 @@
-

Site Description

- -
-
+

Site Description

+ +
+

Default Wallet Name

@@ -628,12 +682,15 @@ @keydown.enter="addAdSpace" type="text" label="Ad image URL" - hint="Ad image filepaths or urls, extensions can choose to honor"> + hint="Ad image filepaths or urls, extensions can choose to honor" + >
{% raw %} -
-
- -
- Save -
-
-
- - +
+ +
+ Save +
+
+
+
+
-
- -

Admin

-

+ + - -
- - -
Wallet topup
-
-
- -
-
- -
-
-
- -
-
-
-
{% endblock %} {% block scripts %} {{ window_vars(user) }} @@ -1100,14 +1114,22 @@ 'LnbitsWallet', 'OpenNodeWallet' ], - + admin: { edited: [], - funding: {}, + funding: [], senddata: {} } }, - themes: ['classic', 'bitcoin', 'flamingo', 'mint', 'autumn', 'monochrome', 'salvador'], + themes: [ + 'classic', + 'bitcoin', + 'flamingo', + 'mint', + 'autumn', + 'monochrome', + 'salvador' + ], options: [ 'bleskomat', 'captcha', @@ -1139,10 +1161,13 @@ self.cancel.on = true } funding = JSON.parse(String('{{ funding | tojson|safe }}')) - var i + funding.map(f => { + this.data.admin.funding.push(f) + }) + /*var i for (i = 0; i < funding.length; i++) { self.data.admin.funding[funding[i].backend_wallet] = funding[i] - } + }*/ let settings = JSON.parse('{{ settings | tojson|safe }}') settings.balance = '{{ balance }}' this.data.admin = {...this.data.admin, ...settings} @@ -1150,42 +1175,42 @@ console.log(settings) }, methods: { - addAdminUser(){ + addAdminUser() { let addUser = this.data.admin_users_add let admin_users = this.data.admin.admin_users - if(addUser.length && !admin_users.includes(addUser)){ + if (addUser.length && !admin_users.includes(addUser)) { admin_users.push(addUser) this.data.admin.admin_users = admin_users - this.data.admin_users_add = "" + this.data.admin_users_add = '' } }, - removeAdminUser(user){ + removeAdminUser(user) { let admin_users = this.data.admin.admin_users this.data.admin.admin_users = admin_users.filter(u => u !== user) }, - addAllowedUser(){ + addAllowedUser() { let addUser = this.data.allowed_users_add let allowed_users = this.data.admin.allowed_users - if(addUser.length && !allowed_users.includes(addUser)){ + if (addUser.length && !allowed_users.includes(addUser)) { allowed_users.push(addUser) this.data.admin.allowed_users = allowed_users - this.data.allowed_users_add = "" + this.data.allowed_users_add = '' } }, - removeAllowedUser(user){ + removeAllowedUser(user) { let allowed_users = this.data.admin.allowed_users this.data.admin.allowed_users = allowed_users.filter(u => u !== user) }, - addAdSpace(){ + addAdSpace() { let adSpace = this.data.ad_space_add let spaces = this.data.admin.ad_space - if(adSpace.length && !spaces.includes(adSpace)){ + if (adSpace.length && !spaces.includes(adSpace)) { spaces.push(adSpace) this.data.admin.ad_space = spaces - this.data.ad_space_add = "" + this.data.ad_space_add = '' } }, - removeAdSpace(ad){ + removeAdSpace(ad) { let spaces = this.data.admin.ad_space this.data.admin.ad_space = spaces.filter(s => s !== ad) }, @@ -1199,14 +1224,14 @@ this.wallet.data.amount, this.g.user.wallets[0].adminkey ) - .then((response) => { + .then(response => { this.$q.notify({ type: 'positive', message: - 'Success! Added ' + - this.wallet.data.amount + - ' to ' + - this.wallet.data.id, + 'Success! Added ' + + this.wallet.data.amount + + ' to ' + + this.wallet.data.id, icon: null }) this.wallet.data = {} @@ -1224,6 +1249,29 @@ self.data.admin.edited.push(source) console.log(self.data.admin.edited) }, + updateFunding(fund) { + let data = this.data.admin.funding.find(v => v.backend_wallet == fund) + + LNbits.api + .request( + 'POST', + '/admin/api/v1/admin/funding', + this.g.user.wallets[0].adminkey, + data + ) + .then(response => { + //let wallet = response.data.backend_wallet + //this.data.admin.funding[wallet] = response.data + //this.data.admin.funding[wallet].endpoint = response.data.endpoint + //console.log(this.data.admin.funding) + //console.log(this.data.admin) + this.$q.notify({ + type: 'positive', + message: `Success! ${response.data.backend_wallet} changed!`, + icon: null + }) + }) + }, UpdateLNbits() { let { admin_users, @@ -1272,8 +1320,7 @@ console.log(response.data) this.$q.notify({ type: 'positive', - message: - 'Success! Settings changed!', + message: 'Success! Settings changed!', icon: null }) }) diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py index 105f05a10..24b8ca859 100644 --- a/lnbits/extensions/admin/views.py +++ b/lnbits/extensions/admin/views.py @@ -21,8 +21,7 @@ async def index(request: Request, user: User = Depends(check_user_exists)): admin = await get_admin() funding = [f.dict() for f in await get_funding()] error, balance = await g().WALLET.status() - print("ADMIN", admin.dict()) - print(g().admin_conf) + return admin_renderer().TemplateResponse( "admin/index.html", { "request": request, diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 1d4e6a9c7..b797dc2d1 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -7,11 +7,11 @@ from starlette.exceptions import HTTPException from lnbits.core.crud import get_wallet from lnbits.decorators import WalletTypeInfo, require_admin_key from lnbits.extensions.admin import admin_ext -from lnbits.extensions.admin.models import Admin, UpdateAdminSettings +from lnbits.extensions.admin.models import Admin, Funding, UpdateAdminSettings from lnbits.helpers import removeEmptyString from lnbits.requestvars import g -from .crud import get_admin, update_admin, update_wallet_balance +from .crud import get_admin, update_admin, update_funding, update_wallet_balance @admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK) @@ -53,3 +53,18 @@ async def api_update_admin( print(g().admin_conf) return {"status": "Success"} + +@admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK) +async def api_update_funding( + request: Request, + data: Funding = Body(...), + w: WalletTypeInfo = Depends(require_admin_key) + ): + admin = await get_admin() + + if not admin.user == w.wallet.user: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) + funding = await update_funding(data=data) + return funding From a07fbf0187c72b45e0e102951faf3ed6cbfb75fc Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 18 Apr 2022 14:25:06 +0100 Subject: [PATCH 139/614] allow user settings without restart --- lnbits/core/views/generic.py | 7 ++++++- lnbits/decorators.py | 8 ++++++++ lnbits/helpers.py | 3 +++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 31a7b0300..83648c444 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -15,7 +15,9 @@ from lnbits.core import db from lnbits.core.models import User from lnbits.decorators import check_user_exists from lnbits.helpers import template_renderer, url_for +from lnbits.requestvars import g from lnbits.settings import ( + LNBITS_ADMIN_UI, LNBITS_ADMIN_USERS, LNBITS_ALLOWED_USERS, LNBITS_CUSTOM_LOGO, @@ -37,7 +39,6 @@ from ..services import pay_invoice, redeem_lnurl_withdraw core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"]) - @core_html_routes.get("/favicon.ico", response_class=FileResponse) async def favicon(): return FileResponse("lnbits/core/static/favicon.ico") @@ -119,6 +120,10 @@ async def wallet( wallet_name = nme service_fee = int(SERVICE_FEE) if int(SERVICE_FEE) == SERVICE_FEE else SERVICE_FEE + if LNBITS_ADMIN_UI: + LNBITS_ADMIN_USERS = g().admin_conf.admin_users + LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users + if not user_id: user = await get_user((await create_account()).id) logger.info(f"Create user {user.id}") # type: ignore diff --git a/lnbits/decorators.py b/lnbits/decorators.py index d4aa63aea..f951163f8 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -16,6 +16,7 @@ from lnbits.core.models import User, Wallet from lnbits.requestvars import g from lnbits.settings import ( LNBITS_ADMIN_EXTENSIONS, + LNBITS_ADMIN_UI, LNBITS_ADMIN_USERS, LNBITS_ALLOWED_USERS, ) @@ -138,6 +139,9 @@ async def get_key_type( detail="Invoice (or Admin) key required.", ) + if LNBITS_ADMIN_UI: + LNBITS_ADMIN_USERS = g().admin_conf.admin_users + for typenr, WalletChecker in zip( [0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker] ): @@ -231,6 +235,10 @@ async def check_user_exists(usr: UUID4) -> User: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="User does not exist." ) + + if LNBITS_ADMIN_UI: + LNBITS_ADMIN_USERS = g().admin_conf.admin_users + LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: raise HTTPException( diff --git a/lnbits/helpers.py b/lnbits/helpers.py index e456f7150..1167143f8 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -24,6 +24,9 @@ class Extension(NamedTuple): class ExtensionManager: def __init__(self): + if settings.LNBITS_ADMIN_UI: + settings.LNBITS_DISABLED_EXTENSIONS = g().admin_conf.disabled_ext + settings.LNBITS_ADMIN_EXTENSIONS = g().admin_conf.admin_ext self._disabled: List[str] = settings.LNBITS_DISABLED_EXTENSIONS self._admin_only: List[str] = [ x.strip(" ") for x in settings.LNBITS_ADMIN_EXTENSIONS From 931286b476bed767e3900a3b60cf10ab962c29e1 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 18 Apr 2022 14:25:26 +0100 Subject: [PATCH 140/614] advert for server restart option --- lnbits/extensions/admin/templates/admin/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index d56b3d793..089c5f1ce 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -51,7 +51,9 @@
-

Active Funding

+

+ Active Funding (Requires server restart) +

Date: Thu, 21 Apr 2022 11:08:26 +0100 Subject: [PATCH 141/614] cleanup prints and console logs --- lnbits/extensions/admin/crud.py | 2 +- lnbits/extensions/admin/templates/admin/index.html | 4 ++-- lnbits/extensions/admin/views_api.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index dd39e8e42..f866bc1aa 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -27,7 +27,7 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str: async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) - print("UPDATE", q) + # print("UPDATE", q) await db.execute( f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) ) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 089c5f1ce..584d3a33f 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1310,7 +1310,7 @@ theme: theme.toString(), ad_space: ad_space.toString() } - console.log(data) + //console.log(data) LNbits.api .request( 'POST', @@ -1319,7 +1319,7 @@ data ) .then(response => { - console.log(response.data) + //console.log(response.data) this.$q.notify({ type: 'positive', message: 'Success! Settings changed!', diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index b797dc2d1..c0650c8a9 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -35,7 +35,7 @@ async def api_update_admin( w: WalletTypeInfo = Depends(require_admin_key) ): admin = await get_admin() - print(data) + # print(data) if not admin.user == w.wallet.user: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" @@ -51,7 +51,7 @@ async def api_update_admin( g().admin_conf = g().admin_conf.copy(update=updated.dict()) - print(g().admin_conf) + # print(g().admin_conf) return {"status": "Success"} @admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK) From 3f38a9094b9c9e5e3c6b29df67b3508efbc41be6 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 16 May 2022 10:49:21 +0100 Subject: [PATCH 142/614] create first user on fresh install --- .env.example | 4 ++-- lnbits/config.py | 3 ++- lnbits/extensions/admin/README.md | 15 ++++++++------- lnbits/extensions/admin/migrations.py | 15 ++++++++++++++- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index 4192f82ec..f0e21aa86 100644 --- a/.env.example +++ b/.env.example @@ -4,8 +4,8 @@ PORT=5000 DEBUG=false LNBITS_ADMIN_USERS="" # User IDs seperated by comma -LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access -LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS +LNBITS_ADMIN_EXTENSIONS="ngrok, admin" # Extensions only admin can access +LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma diff --git a/lnbits/config.py b/lnbits/config.py index b2fbfff12..3ce51c3cb 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -19,6 +19,7 @@ def list_parse_fallback(v): return v.replace(' ','').split(',') class Settings(BaseSettings): + admin_ui: bool = Field(default=True, env="LNBITS_ADMIN_UI") # users admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS") allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS") @@ -37,7 +38,7 @@ class Settings(BaseSettings): site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE") site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME") - theme: List[str] = Field(default="classic, flamingo, mint, salvador, monochrome, autumn", env="LNBITS_THEME_OPTIONS") + theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env env: Optional[str] diff --git a/lnbits/extensions/admin/README.md b/lnbits/extensions/admin/README.md index 277294592..6cf073a11 100644 --- a/lnbits/extensions/admin/README.md +++ b/lnbits/extensions/admin/README.md @@ -1,11 +1,12 @@ -

Example Extension

-

*tagline*

-This is an example extension to help you organise and build you own. +# Admin Extension -Try to include an image - +## Dashboard to manage LNbits from the UI +With AdminUI you can manage your LNbits from the UI -

If your extension has API endpoints, include useful ones here

+![AdminUI](https://i.imgur.com/BIyLkyG.png) -curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY" +## Before you start + +**This extension doesn't discard the need for the `.env` file!** +In the .env file, set the `LNBITS_ADMIN_USERS` variable to include at least your user id. diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 574f772d1..0e22e6677 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -6,9 +6,22 @@ from lnbits.config import conf from lnbits.helpers import urlsafe_short_hash +async def get_admin_user(): + if(conf.admin_users[0]): + return conf.admin_users[0] + from lnbits.core.crud import create_account, get_user + print("Seems like there's no admin users yet. Let's create an account for you!") + account = await create_account() + user = account.id + assert user, "Newly created user couldn't be retrieved" + print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.") + return user + + + async def m001_create_admin_table(db): # users/server - user = conf.admin_users[0] + user = await get_admin_user() admin_users = ",".join(conf.admin_users) allowed_users = ",".join(conf.allowed_users) admin_ext = ",".join(conf.admin_ext) From 0a29fb736093aeca3987122a7a78381b6f760499 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 16 May 2022 12:29:58 +0100 Subject: [PATCH 143/614] fix schemas for admin --- lnbits/commands.py | 11 ++++++++--- lnbits/extensions/admin/crud.py | 12 ++++++------ lnbits/extensions/admin/migrations.py | 26 +++++++++++++------------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/lnbits/commands.py b/lnbits/commands.py index 8c39c338f..7d9b49e21 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -55,8 +55,12 @@ def bundle_vendored(): async def get_admin_settings(): from lnbits.extensions.admin.models import Admin - async with core_db.connect() as conn: + try: + ext_db = importlib.import_module(f"lnbits.extensions.admin").db + except: + return False + async with ext_db.connect() as conn: if conn.type == SQLITE: exists = await conn.fetchone( "SELECT * FROM sqlite_master WHERE type='table' AND name='admin'" @@ -65,11 +69,12 @@ async def get_admin_settings(): exists = await conn.fetchone( "SELECT * FROM information_schema.tables WHERE table_name = 'admin'" ) + print("EXISTS", exists) if not exists: return False - row = await conn.fetchone("SELECT * from admin") - + row = await conn.fetchone("SELECT * from admin.admin") + return Admin(**row) if row else None async def migrate_databases(): diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index f866bc1aa..67fbc6143 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -29,30 +29,30 @@ async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) # print("UPDATE", q) await db.execute( - f'UPDATE admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) + f'UPDATE admin.admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) ) - row = await db.fetchone('SELECT * FROM admin WHERE "user" = ?', (user,)) + row = await db.fetchone('SELECT * FROM admin.admin WHERE "user" = ?', (user,)) assert row, "Newly updated settings couldn't be retrieved" return Admin(**row) if row else None async def get_admin() -> Admin: - row = await db.fetchone("SELECT * FROM admin") + row = await db.fetchone("SELECT * FROM admin.admin") return Admin(**row) if row else None async def update_funding(data: Funding) -> Funding: await db.execute( """ - UPDATE funding + UPDATE admin.funding SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ? WHERE id = ? """, (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,), ) - row = await db.fetchone('SELECT * FROM funding WHERE "id" = ?', (data.id,)) + row = await db.fetchone('SELECT * FROM admin.funding WHERE "id" = ?', (data.id,)) assert row, "Newly updated settings couldn't be retrieved" return Funding(**row) if row else None async def get_funding() -> List[Funding]: - rows = await db.fetchall("SELECT * FROM funding") + rows = await db.fetchall("SELECT * FROM admin.funding") return [Funding(**row) for row in rows] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 0e22e6677..c94d140b7 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -96,7 +96,7 @@ async def m001_create_admin_table(db): await db.execute( """ - CREATE TABLE IF NOT EXISTS admin ( + CREATE TABLE IF NOT EXISTS admin.admin ( "user" TEXT PRIMARY KEY, admin_users TEXT, allowed_users TEXT, @@ -120,7 +120,7 @@ async def m001_create_admin_table(db): ) await db.execute( """ - INSERT INTO admin ( + INSERT INTO admin.admin ( "user", admin_users, allowed_users, @@ -171,7 +171,7 @@ async def m001_create_funding_table(db): # Make the funding table, if it does not already exist await db.execute( """ - CREATE TABLE IF NOT EXISTS funding ( + CREATE TABLE IF NOT EXISTS admin.funding ( id TEXT PRIMARY KEY, backend_wallet TEXT, endpoint TEXT, @@ -188,7 +188,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, selected) VALUES (?, ?, ?, ?) """, ( @@ -200,7 +200,7 @@ async def m001_create_funding_table(db): ) await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -214,7 +214,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -228,7 +228,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, port, admin_key, cert, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, port, admin_key, cert, selected) VALUES (?, ?, ?, ?, ?, ?, ?) """, ( @@ -244,7 +244,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected) VALUES (?, ?, ?, ?, ?, ?) """, ( @@ -259,7 +259,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected) VALUES (?, ?, ?, ?, ?, ?) """, ( @@ -274,7 +274,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -288,7 +288,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -302,7 +302,7 @@ async def m001_create_funding_table(db): await db.execute( """ - INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) VALUES (?, ?, ?, ?, ?) """, ( @@ -317,7 +317,7 @@ async def m001_create_funding_table(db): ## PLACEHOLDER FOR ECLAIR WALLET # await db.execute( # """ - # INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected) + # INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) # VALUES (?, ?, ?, ?, ?) # """, # ( From ac74cfaab7e70d8df8229241e040403d473f04e1 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 16 May 2022 12:53:47 +0100 Subject: [PATCH 144/614] fix sqlite and show user account --- lnbits/app.py | 1 + lnbits/commands.py | 2 +- lnbits/extensions/admin/migrations.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lnbits/app.py b/lnbits/app.py index c8f5c60a4..2b4837584 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -116,6 +116,7 @@ def check_settings(app: FastAPI): while True: admin_set = await get_admin_settings() if admin_set : + print(f"Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}") break print("ERROR:", admin_set) await asyncio.sleep(5) diff --git a/lnbits/commands.py b/lnbits/commands.py index 7d9b49e21..763a5b90b 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -69,7 +69,7 @@ async def get_admin_settings(): exists = await conn.fetchone( "SELECT * FROM information_schema.tables WHERE table_name = 'admin'" ) - print("EXISTS", exists) + if not exists: return False diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index c94d140b7..6c5b507d1 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -15,6 +15,7 @@ async def get_admin_user(): user = account.id assert user, "Newly created user couldn't be retrieved" print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.") + conf.admin_users.insert(0, user) return user From 1ebd557b1d544f6baab770111a4bf89f31abea66 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 16 May 2022 15:35:04 +0100 Subject: [PATCH 145/614] cleanup and info to user on startup --- lnbits/app.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 2b4837584..eaa331364 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -112,13 +112,11 @@ def create_app(config_object="lnbits.settings") -> FastAPI: def check_settings(app: FastAPI): @app.on_event("startup") async def check_settings_admin(): - while True: admin_set = await get_admin_settings() if admin_set : - print(f"Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}") break - print("ERROR:", admin_set) + print("Waiting for admin settings... retrying in 5 seconds!") await asyncio.sleep(5) admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(',')) @@ -128,6 +126,7 @@ def check_settings(app: FastAPI): admin_set.theme = removeEmptyString(admin_set.theme.split(',')) admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(',')) g().admin_conf = conf.copy(update=admin_set.dict()) + print(f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}") def check_funding_source(app: FastAPI) -> None: @app.on_event("startup") From e04e24faec18e01c306d86a6bd45f238a74e1d38 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 8 Jun 2022 11:00:43 +0100 Subject: [PATCH 146/614] add custom logo --- lnbits/config.py | 1 + lnbits/extensions/admin/migrations.py | 58 ++----------------- lnbits/extensions/admin/models.py | 2 + .../admin/templates/admin/index.html | 20 +++++-- lnbits/helpers.py | 3 +- 5 files changed, 26 insertions(+), 58 deletions(-) diff --git a/lnbits/config.py b/lnbits/config.py index 3ce51c3cb..d07ca044e 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -39,6 +39,7 @@ class Settings(BaseSettings): site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME") theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS") + custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env env: Optional[str] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 6c5b507d1..aad66f02a 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -41,60 +41,9 @@ async def m001_create_admin_table(db): site_description = conf.site_description default_wallet_name = conf.default_wallet_name theme = ",".join(conf.theme) + custom_logo = conf.custom_logo ad_space = ",".join(conf.ad_space) - # if getenv("LNBITS_ADMIN_EXTENSIONS"): - # admin_ext = getenv("LNBITS_ADMIN_EXTENSIONS") - - # if getenv("LNBITS_DATABASE_URL"): - # database_url = getenv("LNBITS_DATABASE_URL") - - # if getenv("LNBITS_HIDE_API"): - # hide_api = getenv("LNBITS_HIDE_API") - - # if getenv("LNBITS_THEME_OPTIONS"): - # theme = getenv("LNBITS_THEME_OPTIONS") - - # if getenv("LNBITS_AD_SPACE"): - # ad_space = getenv("LNBITS_AD_SPACE") - - # if getenv("LNBITS_SITE_TITLE"): - # site_title = getenv("LNBITS_SITE_TITLE") - - # if getenv("LNBITS_SITE_TAGLINE"): - # site_tagline = getenv("LNBITS_SITE_TAGLINE") - - # if getenv("LNBITS_SITE_DESCRIPTION"): - # site_description = getenv("LNBITS_SITE_DESCRIPTION") - - # if getenv("LNBITS_ALLOWED_USERS"): - # allowed_users = getenv("LNBITS_ALLOWED_USERS") - - # if getenv("LNBITS_ADMIN_USERS"): - # admin_users = "".join(getenv("LNBITS_ADMIN_USERS").split()) - # user = admin_users.split(',')[0] - - # if getenv("LNBITS_DEFAULT_WALLET_NAME"): - # default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME") - - # if getenv("LNBITS_DATA_FOLDER"): - # data_folder = getenv("LNBITS_DATA_FOLDER") - - # if getenv("LNBITS_DISABLED_EXTENSIONS"): - # disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS") - - # if getenv("LNBITS_FORCE_HTTPS"): - # force_https = getenv("LNBITS_FORCE_HTTPS") - - # if getenv("LNBITS_SERVICE_FEE"): - # service_fee = getenv("LNBITS_SERVICE_FEE") - - # if getenv("LNBITS_DENOMINATION"): - # denomination = getenv("LNBITS_DENOMINATION", "sats") - - # if getenv("LNBITS_BACKEND_WALLET_CLASS"): - # funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS") - await db.execute( """ CREATE TABLE IF NOT EXISTS admin.admin ( @@ -115,6 +64,7 @@ async def m001_create_admin_table(db): site_description TEXT, default_wallet_name TEXT, theme TEXT, + custom_logo TEXT, ad_space TEXT ); """ @@ -139,8 +89,9 @@ async def m001_create_admin_table(db): site_description, default_wallet_name, theme, + custom_logo, ad_space) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( user, @@ -160,6 +111,7 @@ async def m001_create_admin_table(db): site_description, default_wallet_name, theme, + custom_logo, ad_space, ), ) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 0f25679dc..3b17e720c 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -23,6 +23,7 @@ class UpdateAdminSettings(BaseModel): default_wallet_name: str = Query("LNbits wallet") denomination: str = Query("sats") theme: str = Query(None) + custom_logo: str = Query(None) ad_space: str = Query(None) class Admin(BaseModel): @@ -46,6 +47,7 @@ class Admin(BaseModel): default_wallet_name: Optional[str] denomination: str = Field(default="sats") theme: Optional[str] + custom_logo: Optional[str] ad_space: Optional[str] @classmethod diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 584d3a33f..d9790051e 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -705,6 +705,19 @@
+
+
+

Custom Logo

+ +
+
+
@@ -718,10 +731,7 @@
- +
@@ -1292,6 +1323,8 @@ disabled_ext, funding_source, force_https, + reserve_fee_min, + reserve_fee_pct, service_fee, hide_api, site_title, @@ -1311,6 +1344,8 @@ disabled_ext: disabled_ext.toString(), funding_source, force_https, + reserve_fee_min, + reserve_fee_pct, service_fee, hide_api, site_title, diff --git a/lnbits/settings.py b/lnbits/settings.py index ed5c77f7e..8e5c321a2 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,6 +1,5 @@ import importlib import subprocess -from email.policy import default from os import path from typing import List From 929d174ba4bc20c074ff7bfd2741521c846d005b Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 8 Jun 2022 15:38:28 +0100 Subject: [PATCH 148/614] calle's semantics --- lnbits/extensions/admin/templates/admin/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 832629bcf..d34b90683 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -67,7 +67,7 @@
-

Minimum wallet reserve

+

Fee reserve

From 8e0baf7b2dcc2eb018ed972aba856ff3aca41b18 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 5 Jul 2022 16:25:02 +0100 Subject: [PATCH 149/614] blacked --- lnbits/extensions/admin/__init__.py | 1 + lnbits/extensions/admin/crud.py | 23 ++++++++++++--- lnbits/extensions/admin/migrations.py | 10 ++++--- lnbits/extensions/admin/models.py | 2 ++ lnbits/extensions/admin/views.py | 10 ++++--- lnbits/extensions/admin/views_api.py | 41 ++++++++++++++------------- 6 files changed, 56 insertions(+), 31 deletions(-) diff --git a/lnbits/extensions/admin/__init__.py b/lnbits/extensions/admin/__init__.py index 6a56b2bb1..24b91fe22 100644 --- a/lnbits/extensions/admin/__init__.py +++ b/lnbits/extensions/admin/__init__.py @@ -7,6 +7,7 @@ db = Database("ext_admin") admin_ext: APIRouter = APIRouter(prefix="/admin", tags=["admin"]) + def admin_renderer(): return template_renderer(["lnbits/extensions/admin/templates"]) diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 67fbc6143..0d7019ccf 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -11,13 +11,13 @@ from .models import Admin, Funding async def update_wallet_balance(wallet_id: str, amount: int) -> str: temp_id = f"temp_{urlsafe_short_hash()}" internal_id = f"internal_{urlsafe_short_hash()}" - + payment = await create_payment( wallet_id=wallet_id, checking_id=internal_id, payment_request="admin_internal", payment_hash="admin_internal", - amount=amount*1000, + amount=amount * 1000, memo="Admin top up", pending=False, ) @@ -25,6 +25,7 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str: await internal_invoice_queue.put(internal_id) return payment + async def update_admin(user: str, **kwargs) -> Admin: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) # print("UPDATE", q) @@ -35,23 +36,37 @@ async def update_admin(user: str, **kwargs) -> Admin: assert row, "Newly updated settings couldn't be retrieved" return Admin(**row) if row else None + async def get_admin() -> Admin: row = await db.fetchone("SELECT * FROM admin.admin") return Admin(**row) if row else None + async def update_funding(data: Funding) -> Funding: await db.execute( """ UPDATE admin.funding SET backend_wallet = ?, endpoint = ?, port = ?, read_key = ?, invoice_key = ?, admin_key = ?, cert = ?, balance = ?, selected = ? WHERE id = ? - """, - (data.backend_wallet, data.endpoint, data.port, data.read_key, data.invoice_key, data.admin_key, data.cert, data.balance, data.selected, data.id,), + """, + ( + data.backend_wallet, + data.endpoint, + data.port, + data.read_key, + data.invoice_key, + data.admin_key, + data.cert, + data.balance, + data.selected, + data.id, + ), ) row = await db.fetchone('SELECT * FROM admin.funding WHERE "id" = ?', (data.id,)) assert row, "Newly updated settings couldn't be retrieved" return Funding(**row) if row else None + async def get_funding() -> List[Funding]: rows = await db.fetchall("SELECT * FROM admin.funding") diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index f3663435c..388f5ec64 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -7,19 +7,21 @@ from lnbits.helpers import urlsafe_short_hash async def get_admin_user(): - if(conf.admin_users[0]): + if conf.admin_users[0]: return conf.admin_users[0] from lnbits.core.crud import create_account, get_user + print("Seems like there's no admin users yet. Let's create an account for you!") account = await create_account() user = account.id assert user, "Newly created user couldn't be retrieved" - print(f"Your newly created account/user id is: {user}. This will be the Super Admin user.") + print( + f"Your newly created account/user id is: {user}. This will be the Super Admin user." + ) conf.admin_users.insert(0, user) return user - async def m001_create_admin_table(db): # users/server user = await get_admin_user() @@ -28,7 +30,7 @@ async def m001_create_admin_table(db): admin_ext = ",".join(conf.admin_ext) disabled_ext = ",".join(conf.disabled_ext) funding_source = conf.funding_source - #operational + # operational data_folder = conf.data_folder database_url = conf.database_url force_https = conf.force_https diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 3d8efdcd8..6e95d68fb 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -28,6 +28,7 @@ class UpdateAdminSettings(BaseModel): custom_logo: str = Query(None) ad_space: str = Query(None) + class Admin(BaseModel): # users user: str @@ -59,6 +60,7 @@ class Admin(BaseModel): data = dict(row) return cls(**data) + class Funding(BaseModel): id: str backend_wallet: str diff --git a/lnbits/extensions/admin/views.py b/lnbits/extensions/admin/views.py index 24b8ca859..ceda5192a 100644 --- a/lnbits/extensions/admin/views.py +++ b/lnbits/extensions/admin/views.py @@ -16,19 +16,21 @@ from .crud import get_admin, get_funding templates = Jinja2Templates(directory="templates") + @admin_ext.get("/", response_class=HTMLResponse) async def index(request: Request, user: User = Depends(check_user_exists)): admin = await get_admin() funding = [f.dict() for f in await get_funding()] error, balance = await g().WALLET.status() - + return admin_renderer().TemplateResponse( - "admin/index.html", { + "admin/index.html", + { "request": request, "user": user.dict(), "admin": admin.dict(), "funding": funding, "settings": g().admin_conf.dict(), - "balance": balance - } + "balance": balance, + }, ) diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index c0650c8a9..784ad97fa 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -15,16 +15,18 @@ from .crud import get_admin, update_admin, update_funding, update_wallet_balance @admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK) -async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key)): +async def api_update_balance( + wallet_id, topup_amount: int, g: WalletTypeInfo = Depends(require_admin_key) +): try: wallet = await get_wallet(wallet_id) except: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" - ) + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount)) - + return {"status": "Success"} @@ -32,39 +34,40 @@ async def api_update_balance(wallet_id, topup_amount: int, g: WalletTypeInfo = D async def api_update_admin( request: Request, data: UpdateAdminSettings = Body(...), - w: WalletTypeInfo = Depends(require_admin_key) - ): + w: WalletTypeInfo = Depends(require_admin_key), +): admin = await get_admin() # print(data) if not admin.user == w.wallet.user: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" - ) + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) updated = await update_admin(user=w.wallet.user, **data.dict()) - updated.admin_users = removeEmptyString(updated.admin_users.split(',')) - updated.allowed_users = removeEmptyString(updated.allowed_users.split(',')) - updated.admin_ext = removeEmptyString(updated.admin_ext.split(',')) - updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(',')) - updated.theme = removeEmptyString(updated.theme.split(',')) - updated.ad_space = removeEmptyString(updated.ad_space.split(',')) + updated.admin_users = removeEmptyString(updated.admin_users.split(",")) + updated.allowed_users = removeEmptyString(updated.allowed_users.split(",")) + updated.admin_ext = removeEmptyString(updated.admin_ext.split(",")) + updated.disabled_ext = removeEmptyString(updated.disabled_ext.split(",")) + updated.theme = removeEmptyString(updated.theme.split(",")) + updated.ad_space = removeEmptyString(updated.ad_space.split(",")) g().admin_conf = g().admin_conf.copy(update=updated.dict()) - + # print(g().admin_conf) return {"status": "Success"} + @admin_ext.post("/api/v1/admin/funding/", status_code=HTTPStatus.OK) async def api_update_funding( request: Request, data: Funding = Body(...), - w: WalletTypeInfo = Depends(require_admin_key) - ): + w: WalletTypeInfo = Depends(require_admin_key), +): admin = await get_admin() if not admin.user == w.wallet.user: raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" - ) + status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin" + ) funding = await update_funding(data=data) return funding From 429217f5a4876920277ad0719458daca52045b90 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 21 Sep 2022 15:28:13 +0100 Subject: [PATCH 150/614] Had to add a couple of tries --- lnbits/core/views/generic.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 83648c444..63f7af681 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -133,12 +133,19 @@ async def wallet( return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "User does not exist."} ) - if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS: - return template_renderer().TemplateResponse( - "error.html", {"request": request, "err": "User not authorized."} - ) - if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS: - user.admin = True + try: + if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS: + return template_renderer().TemplateResponse( + "error.html", {"request": request, "err": "User not authorized."} + ) + except: + pass + + try: + if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS: + user.admin = True + except: + pass if not wallet_id: if user.wallets and not wallet_name: # type: ignore wallet = user.wallets[0] # type: ignore From 1aa2f01d29a25dd5fffac385afd3b5814e10b45c Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 21 Sep 2022 15:31:31 +0100 Subject: [PATCH 151/614] Added couple more tries --- lnbits/decorators.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index f951163f8..a810892d9 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -239,13 +239,16 @@ async def check_user_exists(usr: UUID4) -> User: if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users - - if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: - raise HTTPException( - status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." - ) - - if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: - g().user.admin = True - + try: + if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: + raise HTTPException( + status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." + ) + except: + pass + try: + if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: + g().user.admin = True + except: + pass return g().user From 10a065a7ca4f98c725e690d66f9c4f5c60d0c4e3 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 21 Sep 2022 15:35:06 +0100 Subject: [PATCH 152/614] Reverted try --- lnbits/decorators.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index a810892d9..904ca1c25 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -239,16 +239,12 @@ async def check_user_exists(usr: UUID4) -> User: if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users - try: - if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: - raise HTTPException( - status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." - ) - except: - pass - try: - if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: - g().user.admin = True + if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: + raise HTTPException( + status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." + ) + if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: + g().user.admin = True except: pass return g().user From 3129692ab16fbd66727faba6fb04c87e56b6e0f7 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 21 Sep 2022 15:37:07 +0100 Subject: [PATCH 153/614] reverted other try --- lnbits/core/views/generic.py | 19 ++++++------------- lnbits/decorators.py | 2 -- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 63f7af681..83648c444 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -133,19 +133,12 @@ async def wallet( return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "User does not exist."} ) - try: - if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS: - return template_renderer().TemplateResponse( - "error.html", {"request": request, "err": "User not authorized."} - ) - except: - pass - - try: - if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS: - user.admin = True - except: - pass + if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS: + return template_renderer().TemplateResponse( + "error.html", {"request": request, "err": "User not authorized."} + ) + if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS: + user.admin = True if not wallet_id: if user.wallets and not wallet_name: # type: ignore wallet = user.wallets[0] # type: ignore diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 904ca1c25..dd26d8feb 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -245,6 +245,4 @@ async def check_user_exists(usr: UUID4) -> User: ) if LNBITS_ADMIN_USERS and g().user.id in LNBITS_ADMIN_USERS: g().user.admin = True - except: - pass return g().user From 42f6acd9f4f2f076cf278a843d48db11cbfb2b63 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 21 Sep 2022 18:40:46 +0100 Subject: [PATCH 154/614] fix main merge missing settings --- lnbits/app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lnbits/app.py b/lnbits/app.py index eaa331364..176c6bb97 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -56,6 +56,11 @@ def create_app(config_object="lnbits.settings") -> FastAPI: "url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE", }, ) + if lnbits.settings.LNBITS_ADMIN_UI: + g().admin_conf = conf + check_settings(app) + + g().WALLET = WALLET app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static") app.mount( "/core/static", From a6bdd8c575f74685c9f71dfd142c908543b1d210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 10:46:11 +0200 Subject: [PATCH 155/614] format --- lnbits/app.py | 22 +++++++++++++--------- lnbits/commands.py | 6 +++++- lnbits/config.py | 25 ++++++++++++++++++------- lnbits/core/views/generic.py | 1 + lnbits/decorators.py | 2 +- lnbits/helpers.py | 8 +++++--- 6 files changed, 43 insertions(+), 21 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 176c6bb97..f4e44a0f6 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -114,24 +114,28 @@ def create_app(config_object="lnbits.settings") -> FastAPI: return app + def check_settings(app: FastAPI): @app.on_event("startup") async def check_settings_admin(): while True: admin_set = await get_admin_settings() - if admin_set : + if admin_set: break print("Waiting for admin settings... retrying in 5 seconds!") await asyncio.sleep(5) - - admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(',')) - admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(',')) - admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(',')) - admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(',')) - admin_set.theme = removeEmptyString(admin_set.theme.split(',')) - admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(',')) + + admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(",")) + admin_set.allowed_users = removeEmptyString(admin_set.allowed_users.split(",")) + admin_set.admin_ext = removeEmptyString(admin_set.admin_ext.split(",")) + admin_set.disabled_ext = removeEmptyString(admin_set.disabled_ext.split(",")) + admin_set.theme = removeEmptyString(admin_set.theme.split(",")) + admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(",")) g().admin_conf = conf.copy(update=admin_set.dict()) - print(f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}") + print( + f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}" + ) + def check_funding_source(app: FastAPI) -> None: @app.on_event("startup") diff --git a/lnbits/commands.py b/lnbits/commands.py index 763a5b90b..86868f1f7 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -5,6 +5,7 @@ import re import warnings import click +from genericpath import exists from loguru import logger from .core import db as core_db @@ -52,6 +53,7 @@ def bundle_vendored(): with open(outputpath, "w") as f: f.write(output) + async def get_admin_settings(): from lnbits.extensions.admin.models import Admin @@ -61,6 +63,7 @@ async def get_admin_settings(): return False async with ext_db.connect() as conn: + if conn.type == SQLITE: exists = await conn.fetchone( "SELECT * FROM sqlite_master WHERE type='table' AND name='admin'" @@ -69,7 +72,7 @@ async def get_admin_settings(): exists = await conn.fetchone( "SELECT * FROM information_schema.tables WHERE table_name = 'admin'" ) - + if not exists: return False @@ -77,6 +80,7 @@ async def get_admin_settings(): return Admin(**row) if row else None + async def migrate_databases(): """Creates the necessary databases if they don't exist already; or migrates them.""" diff --git a/lnbits/config.py b/lnbits/config.py index 37b700fde..cf26ad21e 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -6,17 +6,19 @@ from typing import List, Optional from pydantic import BaseSettings, Field wallets_module = importlib.import_module("lnbits.wallets") -wallet_class = getattr( +wallet_class = getattr( wallets_module, getenv("LNBITS_BACKEND_WALLET_CLASS", "VoidWallet") ) WALLET = wallet_class() + def list_parse_fallback(v): try: return json.loads(v) except Exception as e: - return v.replace(' ','').split(',') + return v.replace(" ", "").split(",") + class Settings(BaseSettings): admin_ui: bool = Field(default=True, env="LNBITS_ADMIN_UI") @@ -24,7 +26,9 @@ class Settings(BaseSettings): admin_users: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_USERS") allowed_users: List[str] = Field(default_factory=list, env="LNBITS_ALLOWED_USERS") admin_ext: List[str] = Field(default_factory=list, env="LNBITS_ADMIN_EXTENSIONS") - disabled_ext: List[str] = Field(default_factory=list, env="LNBITS_DISABLED_EXTENSIONS") + disabled_ext: List[str] = Field( + default_factory=list, env="LNBITS_DISABLED_EXTENSIONS" + ) funding_source: str = Field(default="VoidWallet", env="LNBITS_BACKEND_WALLET_CLASS") # ops data_folder: str = Field(default=None, env="LNBITS_DATA_FOLDER") @@ -37,10 +41,17 @@ class Settings(BaseSettings): denomination: str = Field(default="sats", env="LNBITS_DENOMINATION") # Change theme site_title: str = Field(default="LNbits", env="LNBITS_SITE_TITLE") - site_tagline: str = Field(default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE") + site_tagline: str = Field( + default="free and open-source lightning wallet", env="LNBITS_SITE_TAGLINE" + ) site_description: str = Field(default=None, env="LNBITS_SITE_DESCRIPTION") - default_wallet_name: str = Field(default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME") - theme: List[str] = Field(default=["classic, flamingo, mint, salvador, monochrome, autumn"], env="LNBITS_THEME_OPTIONS") + default_wallet_name: str = Field( + default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME" + ) + theme: List[str] = Field( + default=["classic, flamingo, mint, salvador, monochrome, autumn"], + env="LNBITS_THEME_OPTIONS", + ) custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env @@ -48,7 +59,7 @@ class Settings(BaseSettings): debug: Optional[str] host: Optional[str] port: Optional[str] - lnbits_path: Optional[str] = path.dirname(path.realpath(__file__)) + lnbits_path: Optional[str] = path.dirname(path.realpath(__file__)) # @validator('admin_users', 'allowed_users', 'admin_ext', 'disabled_ext', pre=True) # def validate(cls, val): diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 83648c444..3a1fbdfcf 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -39,6 +39,7 @@ from ..services import pay_invoice, redeem_lnurl_withdraw core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"]) + @core_html_routes.get("/favicon.ico", response_class=FileResponse) async def favicon(): return FileResponse("lnbits/core/static/favicon.ico") diff --git a/lnbits/decorators.py b/lnbits/decorators.py index dd26d8feb..58b025aa3 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -235,7 +235,7 @@ async def check_user_exists(usr: UUID4) -> User: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="User does not exist." ) - + if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users diff --git a/lnbits/helpers.py b/lnbits/helpers.py index 7bd1b54a9..f4255c866 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -157,11 +157,13 @@ def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> s url = f"{base}{endpoint}{url_params}" return url + def removeEmptyString(arr): return list(filter(None, arr)) + def template_renderer(additional_folders: List = []) -> Jinja2Templates: - if(settings.LNBITS_ADMIN_UI): + if settings.LNBITS_ADMIN_UI: _ = g().admin_conf settings.LNBITS_AD_SPACE = _.ad_space settings.LNBITS_HIDE_API = _.hide_api @@ -170,8 +172,8 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates: settings.LNBITS_SITE_TAGLINE = _.site_tagline settings.LNBITS_SITE_DESCRIPTION = _.site_description settings.LNBITS_THEME_OPTIONS = _.theme - settings.LNBITS_CUSTOM_LOGO = _.custom_logo - + settings.LNBITS_CUSTOM_LOGO = _.custom_logo + t = Jinja2Templates( loader=jinja2.FileSystemLoader( ["lnbits/templates", "lnbits/core/templates", *additional_folders] From 635bcf66d6f8cf7a65322a41abed92153039cd9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 11:47:24 +0200 Subject: [PATCH 156/614] format black --- lnbits/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/app.py b/lnbits/app.py index f4e44a0f6..118bea983 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -56,6 +56,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI: "url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE", }, ) + if lnbits.settings.LNBITS_ADMIN_UI: g().admin_conf = conf check_settings(app) From 11393ef7e9ed808d9034baa8515eabcd37918a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 15:29:12 +0200 Subject: [PATCH 157/614] fix AD_SPACE --- lnbits/core/templates/core/wallet.html | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index 4bf6067c0..cc45eb681 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -385,12 +385,9 @@ - {% endif %} {% if AD_SPACE %} {% for ADS in AD_SPACE %} {% set AD = - ADS.split(';') %} + {% endif %} {% if AD_SPACE %} {% for AD in AD_SPACE %} - {% endfor %} {% endif %}
From 0beb112d5b4f938201adfc6fc2814717ddac20a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 15:55:37 +0200 Subject: [PATCH 158/614] fix some javascript errors when adding users --- lnbits/extensions/admin/templates/admin/index.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index d34b90683..1e881cb69 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1221,7 +1221,7 @@ addAdminUser() { let addUser = this.data.admin_users_add let admin_users = this.data.admin.admin_users - if (addUser.length && !admin_users.includes(addUser)) { + if (addUser && addUser.length && !admin_users.includes(addUser)) { admin_users.push(addUser) this.data.admin.admin_users = admin_users this.data.admin_users_add = '' @@ -1234,7 +1234,7 @@ addAllowedUser() { let addUser = this.data.allowed_users_add let allowed_users = this.data.admin.allowed_users - if (addUser.length && !allowed_users.includes(addUser)) { + if (addUser && addUser.length && !allowed_users.includes(addUser)) { allowed_users.push(addUser) this.data.admin.allowed_users = allowed_users this.data.allowed_users_add = '' @@ -1336,7 +1336,6 @@ custom_logo, ad_space } = this.data.admin - //console.log("this", this.data.admin) let data = { admin_users: admin_users.toString(), allowed_users: allowed_users.toString(), From bfff5f3775cd090b454869c434042b4acdb37434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 16:04:55 +0200 Subject: [PATCH 159/614] fix ADMIN_UI=false errors --- lnbits/core/views/generic.py | 3 +++ lnbits/decorators.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 3a1fbdfcf..db4fac430 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -124,6 +124,9 @@ async def wallet( if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users + else: + LNBITS_ADMIN_USERS = [] + LNBITS_ALLOWED_USERS = [] if not user_id: user = await get_user((await create_account()).id) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 58b025aa3..5a3c0a5c3 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -141,6 +141,8 @@ async def get_key_type( if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users + else: + LNBITS_ADMIN_USERS = [] for typenr, WalletChecker in zip( [0, 1], [WalletAdminKeyChecker, WalletInvoiceKeyChecker] @@ -239,6 +241,10 @@ async def check_user_exists(usr: UUID4) -> User: if LNBITS_ADMIN_UI: LNBITS_ADMIN_USERS = g().admin_conf.admin_users LNBITS_ALLOWED_USERS = g().admin_conf.allowed_users + else: + LNBITS_ADMIN_USERS = [] + LNBITS_ALLOWED_USERS = [] + if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS: raise HTTPException( status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized." From cede3317f3655c09b196c5293174a1dbaaaa18ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 16:14:17 +0200 Subject: [PATCH 160/614] prettier --- lnbits/core/templates/core/wallet.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index cc45eb681..5393007d1 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -386,8 +386,7 @@ {% endif %} {% if AD_SPACE %} {% for AD in AD_SPACE %} - - {% endfor %} {% endif %} From b442acc24f02c91971d29fbfe03dda9d5b5fc34c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 22 Sep 2022 18:35:47 +0200 Subject: [PATCH 161/614] fix migration tests --- lnbits/extensions/admin/migrations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 388f5ec64..196c9fc07 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -7,7 +7,7 @@ from lnbits.helpers import urlsafe_short_hash async def get_admin_user(): - if conf.admin_users[0]: + if len(conf.admin_users) > 0: return conf.admin_users[0] from lnbits.core.crud import create_account, get_user From 6db5fb16c85d3fd654afd55ee8d6969d243572bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 26 Sep 2022 16:54:19 +0200 Subject: [PATCH 162/614] change comments to use multiple lines --- .env.example | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index f0e21aa86..673470b7f 100644 --- a/.env.example +++ b/.env.example @@ -3,11 +3,15 @@ PORT=5000 DEBUG=false -LNBITS_ADMIN_USERS="" # User IDs seperated by comma -LNBITS_ADMIN_EXTENSIONS="ngrok, admin" # Extensions only admin can access -LNBITS_ADMIN_UI=false # Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available +# User IDs seperated by comma +LNBITS_ADMIN_USERS="" +# Extensions only admin can access +LNBITS_ADMIN_EXTENSIONS="ngrok, admin" +# Enable Admin GUI, available for the first user in LNBITS_ADMIN_USERS if available +LNBITS_ADMIN_UI=false -LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma +# Restricts access, User IDs seperated by comma +LNBITS_ALLOWED_USERS="" LNBITS_DEFAULT_WALLET_NAME="LNbits wallet" From affec50a3da3ac812b5046c49483676e93962b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 26 Sep 2022 16:59:30 +0200 Subject: [PATCH 163/614] fix merge error --- lnbits/app.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 118bea983..82a8f20e3 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -57,10 +57,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: }, ) - if lnbits.settings.LNBITS_ADMIN_UI: - g().admin_conf = conf - check_settings(app) - g().WALLET = WALLET app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static") app.mount( From 7aba2f989cdd78539ca22c69dc1a2e7d5ceb3d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 27 Sep 2022 14:14:36 +0200 Subject: [PATCH 164/614] use logger in app.py --- lnbits/app.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 82a8f20e3..e07d2ada0 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -31,8 +31,6 @@ from .helpers import ( url_for_vendored, ) from .requestvars import g - -# from .settings import WALLET from .tasks import ( catch_everything_and_restart, check_pending_payments, @@ -119,7 +117,7 @@ def check_settings(app: FastAPI): admin_set = await get_admin_settings() if admin_set: break - print("Waiting for admin settings... retrying in 5 seconds!") + logger.info("Waiting for admin settings... retrying in 5 seconds!") await asyncio.sleep(5) admin_set.admin_users = removeEmptyString(admin_set.admin_users.split(",")) @@ -129,7 +127,7 @@ def check_settings(app: FastAPI): admin_set.theme = removeEmptyString(admin_set.theme.split(",")) admin_set.ad_space = removeEmptyString(admin_set.ad_space.split(",")) g().admin_conf = conf.copy(update=admin_set.dict()) - print( + logger.info( f" ✔️ Access admin user account at: http://{lnbits.settings.HOST}:{lnbits.settings.PORT}/wallet?usr={admin_set.user}" ) From 212b8f9fdd67c73b16f2c0d97cd549bc7bb8f29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 27 Sep 2022 14:14:57 +0200 Subject: [PATCH 165/614] fix config --- lnbits/config.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lnbits/config.py b/lnbits/config.py index cf26ad21e..874effae2 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -17,7 +17,11 @@ def list_parse_fallback(v): try: return json.loads(v) except Exception as e: - return v.replace(" ", "").split(",") + replaced = v.replace(" ", "") + if replaced: + return replaced.split(",") + else: + return [] class Settings(BaseSettings): @@ -48,10 +52,7 @@ class Settings(BaseSettings): default_wallet_name: str = Field( default="LNbits wallet", env="LNBITS_DEFAULT_WALLET_NAME" ) - theme: List[str] = Field( - default=["classic, flamingo, mint, salvador, monochrome, autumn"], - env="LNBITS_THEME_OPTIONS", - ) + theme: List[str] = Field(default_factory=list, env="LNBITS_THEME_OPTIONS") custom_logo: str = Field(default=None, env="LNBITS_CUSTOM_LOGO") ad_space: List[str] = Field(default_factory=list, env="LNBITS_AD_SPACE") # .env @@ -74,4 +75,5 @@ class Settings(BaseSettings): conf = Settings() +print(conf) WALLET = wallet_class() From b6a0a321844ff8a185f728b42f4ede3142334637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 27 Sep 2022 14:17:20 +0200 Subject: [PATCH 166/614] concatenate first migrations script fixup --- lnbits/config.py | 1 - lnbits/extensions/admin/migrations.py | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lnbits/config.py b/lnbits/config.py index 874effae2..fe8dabf9c 100644 --- a/lnbits/config.py +++ b/lnbits/config.py @@ -75,5 +75,4 @@ class Settings(BaseSettings): conf = Settings() -print(conf) WALLET = wallet_class() diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 196c9fc07..2d48a8e4c 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -23,6 +23,8 @@ async def get_admin_user(): async def m001_create_admin_table(db): + + # users/server user = await get_admin_user() admin_users = ",".join(conf.admin_users) @@ -78,7 +80,7 @@ async def m001_create_admin_table(db): await db.execute( """ INSERT INTO admin.admin ( - "user", + "user", admin_users, allowed_users, admin_ext, @@ -126,9 +128,6 @@ async def m001_create_admin_table(db): ), ) - -async def m001_create_funding_table(db): - funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS") # Make the funding table, if it does not already exist From 1eeb9de7de8c2409f9a1d6b828e051ea640870d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 27 Sep 2022 16:37:08 +0200 Subject: [PATCH 167/614] add restart button to frontend --- .../admin/templates/admin/index.html | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 1e881cb69..319ca3f0d 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -65,6 +65,14 @@ :options="data.funding_source" >

+
+ +

Fee reserve

@@ -89,7 +97,7 @@ >
-
+ @@ -1257,6 +1265,24 @@ let spaces = this.data.admin.ad_space this.data.admin.ad_space = spaces.filter(s => s !== ad) }, + restartServer() { + LNbits.api + .request( + 'GET', + '/admin/api/v1/admin/restart/', + this.g.user.wallets[0].adminkey + ) + .then(response => { + this.$q.notify({ + type: 'positive', + message: 'Success! Restarted Server', + icon: null + }) + }) + .catch(function (error) { + LNbits.utils.notifyApiError(error) + }) + }, topupWallet() { LNbits.api .request( From 82e322ae43658bef7735974324fd3bec7919b756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 3 Oct 2022 16:34:52 +0200 Subject: [PATCH 168/614] make extension use new settings --- lnbits/extensions/boltz/boltz.py | 14 ++++++-------- lnbits/extensions/boltz/mempool.py | 15 ++++++--------- lnbits/extensions/boltz/views_api.py | 4 ++-- lnbits/extensions/lndhub/views_api.py | 4 ++-- lnbits/extensions/tpos/views.py | 14 +++++++------- 5 files changed, 23 insertions(+), 28 deletions(-) diff --git a/lnbits/extensions/boltz/boltz.py b/lnbits/extensions/boltz/boltz.py index ac99d4f4a..424d0bf78 100644 --- a/lnbits/extensions/boltz/boltz.py +++ b/lnbits/extensions/boltz/boltz.py @@ -12,7 +12,7 @@ from loguru import logger from lnbits.core.services import create_invoice, pay_invoice from lnbits.helpers import urlsafe_short_hash -from lnbits.settings import BOLTZ_NETWORK, BOLTZ_URL +from lnbits.settings import settings from .crud import update_swap_status from .mempool import ( @@ -33,9 +33,7 @@ from .models import ( ) from .utils import check_balance, get_timestamp, req_wrap -net = NETWORKS[BOLTZ_NETWORK] -logger.trace(f"BOLTZ_URL: {BOLTZ_URL}") -logger.trace(f"Bitcoin Network: {net['name']}") +net = NETWORKS[settings.boltz_network] async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap: @@ -62,7 +60,7 @@ async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap: res = req_wrap( "post", - f"{BOLTZ_URL}/createswap", + f"{settings.boltz_url}/createswap", json={ "type": "submarine", "pairId": "BTC/BTC", @@ -129,7 +127,7 @@ async def create_reverse_swap( res = req_wrap( "post", - f"{BOLTZ_URL}/createswap", + f"{settings.boltz_url}/createswap", json={ "type": "reversesubmarine", "pairId": "BTC/BTC", @@ -409,7 +407,7 @@ def check_boltz_limits(amount): def get_boltz_pairs(): res = req_wrap( "get", - f"{BOLTZ_URL}/getpairs", + f"{settings.boltz_url}/getpairs", headers={"Content-Type": "application/json"}, ) return res.json() @@ -418,7 +416,7 @@ def get_boltz_pairs(): def get_boltz_status(boltzid): res = req_wrap( "post", - f"{BOLTZ_URL}/swapstatus", + f"{settings.boltz_url}/swapstatus", json={"id": boltzid}, ) return res.json() diff --git a/lnbits/extensions/boltz/mempool.py b/lnbits/extensions/boltz/mempool.py index a44c0f02c..a64cadad0 100644 --- a/lnbits/extensions/boltz/mempool.py +++ b/lnbits/extensions/boltz/mempool.py @@ -7,14 +7,11 @@ import websockets from embit.transaction import Transaction from loguru import logger -from lnbits.settings import BOLTZ_MEMPOOL_SPACE_URL, BOLTZ_MEMPOOL_SPACE_URL_WS +from lnbits.settings import settings from .utils import req_wrap -logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL: {BOLTZ_MEMPOOL_SPACE_URL}") -logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL_WS: {BOLTZ_MEMPOOL_SPACE_URL_WS}") - -websocket_url = f"{BOLTZ_MEMPOOL_SPACE_URL_WS}/api/v1/ws" +websocket_url = f"{settings.boltz_mempool_space_url_ws}/api/v1/ws" async def wait_for_websocket_message(send, message_string): @@ -33,7 +30,7 @@ async def wait_for_websocket_message(send, message_string): def get_mempool_tx(address): res = req_wrap( "get", - f"{BOLTZ_MEMPOOL_SPACE_URL}/api/address/{address}/txs", + f"{settings.boltz_mempool_space_url}/api/address/{address}/txs", headers={"Content-Type": "text/plain"}, ) txs = res.json() @@ -70,7 +67,7 @@ def get_fee_estimation() -> int: def get_mempool_fees() -> int: res = req_wrap( "get", - f"{BOLTZ_MEMPOOL_SPACE_URL}/api/v1/fees/recommended", + f"{settings.boltz_mempool_space_url}/api/v1/fees/recommended", headers={"Content-Type": "text/plain"}, ) fees = res.json() @@ -80,7 +77,7 @@ def get_mempool_fees() -> int: def get_mempool_blockheight() -> int: res = req_wrap( "get", - f"{BOLTZ_MEMPOOL_SPACE_URL}/api/blocks/tip/height", + f"{settings.boltz_mempool_space_url}/api/blocks/tip/height", headers={"Content-Type": "text/plain"}, ) return int(res.text) @@ -91,7 +88,7 @@ async def send_onchain_tx(tx: Transaction): logger.debug(f"Boltz - mempool sending onchain tx...") req_wrap( "post", - f"{BOLTZ_MEMPOOL_SPACE_URL}/api/tx", + f"{settings.boltz_mempool_space_url}/api/tx", headers={"Content-Type": "text/plain"}, content=raw, ) diff --git a/lnbits/extensions/boltz/views_api.py b/lnbits/extensions/boltz/views_api.py index a4b7d3181..18ca14cb7 100644 --- a/lnbits/extensions/boltz/views_api.py +++ b/lnbits/extensions/boltz/views_api.py @@ -14,7 +14,7 @@ from starlette.requests import Request from lnbits.core.crud import get_user from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key -from lnbits.settings import BOLTZ_MEMPOOL_SPACE_URL +from lnbits.settings import settings from . import boltz_ext from .boltz import ( @@ -55,7 +55,7 @@ from .utils import check_balance response_model=str, ) async def api_mempool_url(): - return BOLTZ_MEMPOOL_SPACE_URL + return settings.boltz_mempool_space_url # NORMAL SWAP diff --git a/lnbits/extensions/lndhub/views_api.py b/lnbits/extensions/lndhub/views_api.py index 8cbe5a6bf..b2328c392 100644 --- a/lnbits/extensions/lndhub/views_api.py +++ b/lnbits/extensions/lndhub/views_api.py @@ -12,7 +12,7 @@ from lnbits import bolt11 from lnbits.core.crud import delete_expired_invoices, get_payments from lnbits.core.services import create_invoice, pay_invoice from lnbits.decorators import WalletTypeInfo -from lnbits.settings import LNBITS_SITE_TITLE, WALLET +from lnbits.settings import WALLET, settings from . import lndhub_ext from .decorators import check_wallet, require_admin_key @@ -56,7 +56,7 @@ async def lndhub_addinvoice( _, pr = await create_invoice( wallet_id=wallet.wallet.id, amount=int(data.amt), - memo=data.memo or LNBITS_SITE_TITLE, + memo=data.memo or settings.lnbits_site_title, extra={"tag": "lndhub"}, ) except: diff --git a/lnbits/extensions/tpos/views.py b/lnbits/extensions/tpos/views.py index e1f1d21e3..dac129a93 100644 --- a/lnbits/extensions/tpos/views.py +++ b/lnbits/extensions/tpos/views.py @@ -8,7 +8,7 @@ from starlette.responses import HTMLResponse from lnbits.core.models import User from lnbits.decorators import check_user_exists -from lnbits.settings import LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE +from lnbits.settings import settings from . import tpos_ext, tpos_renderer from .crud import get_tpos @@ -50,12 +50,12 @@ async def manifest(tpos_id: str): ) return { - "short_name": LNBITS_SITE_TITLE, - "name": tpos.name + " - " + LNBITS_SITE_TITLE, + "short_name": settings.lnbits_site_title, + "name": tpos.name + " - " + settings.lnbits_site_title, "icons": [ { - "src": LNBITS_CUSTOM_LOGO - if LNBITS_CUSTOM_LOGO + "src": settings.lnbits_custom_logo + if settings.lnbits_custom_logo else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png", "type": "image/png", "sizes": "900x900", @@ -69,9 +69,9 @@ async def manifest(tpos_id: str): "theme_color": "#1F2234", "shortcuts": [ { - "name": tpos.name + " - " + LNBITS_SITE_TITLE, + "name": tpos.name + " - " + settings.lnbits_site_title, "short_name": tpos.name, - "description": tpos.name + " - " + LNBITS_SITE_TITLE, + "description": tpos.name + " - " + settings.lnbits_site_title, "url": "/tpos/" + tpos_id, } ], From 5aa9cdd45631379341f31d17d9484c5e3ad05a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 3 Oct 2022 16:35:26 +0200 Subject: [PATCH 169/614] remove enviroms --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7418de272..92c43dce7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,6 @@ charset-normalizer = "2.0.6" click = "8.0.1" ecdsa = "0.17.0" embit = "0.4.9" -environs = "9.3.3" fastapi = "0.78.0" h11 = "0.12.0" httpcore = "0.15.0" From 6a2c7414783a5edb4b7932355f3fe145053332a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 3 Oct 2022 16:36:14 +0200 Subject: [PATCH 170/614] fix admin --- lnbits/extensions/admin/crud.py | 24 +- lnbits/extensions/admin/migrations.py | 337 +---- lnbits/extensions/admin/models.py | 58 +- .../admin/templates/admin/_tab_funding.html | 158 +++ .../admin/templates/admin/_tab_server.html | 78 ++ .../admin/templates/admin/_tab_theme.html | 122 ++ .../admin/templates/admin/_tab_users.html | 96 ++ .../admin/templates/admin/index.html | 1133 +---------------- lnbits/extensions/admin/views.py | 16 +- lnbits/extensions/admin/views_api.py | 28 +- 10 files changed, 576 insertions(+), 1474 deletions(-) create mode 100644 lnbits/extensions/admin/templates/admin/_tab_funding.html create mode 100644 lnbits/extensions/admin/templates/admin/_tab_server.html create mode 100644 lnbits/extensions/admin/templates/admin/_tab_theme.html create mode 100644 lnbits/extensions/admin/templates/admin/_tab_users.html diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index 0d7019ccf..e4cb5d77e 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -2,10 +2,11 @@ from typing import List from lnbits.core.crud import create_payment from lnbits.helpers import urlsafe_short_hash +from lnbits.settings import Settings from lnbits.tasks import internal_invoice_queue from . import db -from .models import Admin, Funding +from .models import Funding async def update_wallet_balance(wallet_id: str, amount: int) -> str: @@ -23,26 +24,26 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str: ) # manually send this for now await internal_invoice_queue.put(internal_id) - return payment -async def update_admin(user: str, **kwargs) -> Admin: +async def update_settings(user: str, **kwargs) -> Settings: q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) # print("UPDATE", q) await db.execute( - f'UPDATE admin.admin SET {q} WHERE "user" = ?', (*kwargs.values(), user) + f'UPDATE admin.settings SET {q} WHERE "user" = ?', (*kwargs.values(), user) ) - row = await db.fetchone('SELECT * FROM admin.admin WHERE "user" = ?', (user,)) + row = await db.fetchone('SELECT * FROM admin.settings WHERE "user" = ?', (user,)) assert row, "Newly updated settings couldn't be retrieved" - return Admin(**row) if row else None - - -async def get_admin() -> Admin: - row = await db.fetchone("SELECT * FROM admin.admin") - return Admin(**row) if row else None + return Settings(**row) if row else None async def update_funding(data: Funding) -> Funding: + await db.execute( + """ + UPDATE admin.settings SET funding_source = ? WHERE user = ? + """, + (data.backend_wallet, data.user), + ) await db.execute( """ UPDATE admin.funding @@ -69,5 +70,4 @@ async def update_funding(data: Funding) -> Funding: async def get_funding() -> List[Funding]: rows = await db.fetchall("SELECT * FROM admin.funding") - return [Funding(**row) for row in rows] diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 2d48a8e4c..8f6c76a0e 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -1,292 +1,57 @@ -from os import getenv - -from sqlalchemy.exc import OperationalError # type: ignore - -from lnbits.config import conf -from lnbits.helpers import urlsafe_short_hash - - -async def get_admin_user(): - if len(conf.admin_users) > 0: - return conf.admin_users[0] - from lnbits.core.crud import create_account, get_user - - print("Seems like there's no admin users yet. Let's create an account for you!") - account = await create_account() - user = account.id - assert user, "Newly created user couldn't be retrieved" - print( - f"Your newly created account/user id is: {user}. This will be the Super Admin user." - ) - conf.admin_users.insert(0, user) - return user - - -async def m001_create_admin_table(db): - - - # users/server - user = await get_admin_user() - admin_users = ",".join(conf.admin_users) - allowed_users = ",".join(conf.allowed_users) - admin_ext = ",".join(conf.admin_ext) - disabled_ext = ",".join(conf.disabled_ext) - funding_source = conf.funding_source - # operational - data_folder = conf.data_folder - database_url = conf.database_url - force_https = conf.force_https - reserve_fee_min = conf.reserve_fee_min - reserve_fee_pct = conf.reserve_fee_pct - service_fee = conf.service_fee - hide_api = conf.hide_api - denomination = conf.denomination - # Theme'ing - site_title = conf.site_title - site_tagline = conf.site_tagline - site_description = conf.site_description - default_wallet_name = conf.default_wallet_name - theme = ",".join(conf.theme) - custom_logo = conf.custom_logo - ad_space = ",".join(conf.ad_space) - +async def m001_create_admin_settings_table(db): await db.execute( """ - CREATE TABLE IF NOT EXISTS admin.admin ( - "user" TEXT PRIMARY KEY, - admin_users TEXT, - allowed_users TEXT, - admin_ext TEXT, - disabled_ext TEXT, - funding_source TEXT, - data_folder TEXT, - database_url TEXT, - force_https BOOLEAN, - reserve_fee_min INT, - reserve_fee_pct REAL, - service_fee REAL, - hide_api BOOLEAN, - denomination TEXT, - site_title TEXT, - site_tagline TEXT, - site_description TEXT, - default_wallet_name TEXT, - theme TEXT, - custom_logo TEXT, - ad_space TEXT - ); - """ - ) - await db.execute( - """ - INSERT INTO admin.admin ( - "user", - admin_users, - allowed_users, - admin_ext, - disabled_ext, - funding_source, - data_folder, - database_url, - force_https, - reserve_fee_min, - reserve_fee_pct, - service_fee, - hide_api, - denomination, - site_title, - site_tagline, - site_description, - default_wallet_name, - theme, - custom_logo, - ad_space) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - user, - admin_users, - allowed_users, - admin_ext, - disabled_ext, - funding_source, - data_folder, - database_url, - force_https, - reserve_fee_min, - reserve_fee_pct, - service_fee, - hide_api, - denomination, - site_title, - site_tagline, - site_description, - default_wallet_name, - theme, - custom_logo, - ad_space, - ), - ) - - funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS") - - # Make the funding table, if it does not already exist - await db.execute( - """ - CREATE TABLE IF NOT EXISTS admin.funding ( - id TEXT PRIMARY KEY, - backend_wallet TEXT, - endpoint TEXT, + CREATE TABLE IF NOT EXISTS admin.settings ( + lnbits_admin_ui TEXT, + debug TEXT, + host TEXT, port INT, - read_key TEXT, - invoice_key TEXT, - admin_key TEXT, - cert TEXT, - balance INT, - selected INT + lnbits_path TEXT, + lnbits_commit TEXT, + lnbits_admin_users TEXT, + lnbits_allowed_users TEXT, + lnbits_allowed_funding_sources TEXT, + lnbits_admin_extensions TEXT, + lnbits_disabled_extensions TEXT, + lnbits_site_title TEXT, + lnbits_site_tagline TEXT, + lnbits_site_description TEXT, + lnbits_default_wallet_name TEXT, + lnbits_theme_options TEXT, + lnbits_custom_logo TEXT, + lnbits_ad_space TEXT, + lnbits_data_folder TEXT, + lnbits_database_url TEXT, + lnbits_force_https TEXT, + lnbits_reserve_fee_min TEXT, + lnbits_reserve_fee_percent TEXT, + lnbits_service_fee TEXT, + lnbits_hide_api TEXT, + lnbits_denomination TEXT, + lnbits_backend_wallet_class TEXT, + fake_wallet_secret TEXT, + lnbits_endpoint TEXT, + lnbits_key TEXT, + cliche_endpoint TEXT, + corelightning_rpc TEXT, + eclair_url TEXT, + eclair_pass TEXT, + lnd_rest_endpoint TEXT, + lnd_rest_cert TEXT, + lnd_rest_macaroon TEXT, + lnpay_api_endpoint TEXT, + lnpay_api_key TEXT, + lnpay_wallet_key TEXT, + lntxbot_api_endpoint TEXT, + lntxbot_key TEXT, + opennode_api_endpoint TEXT, + opennode_key TEXT, + spark_url TEXT, + spark_token TEXT, + boltz_network TEXT, + boltz_url TEXT, + boltz_mempool_space_url TEXT, + boltz_mempool_space_url_ws TEXT ); """ ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, selected) - VALUES (?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "CLightningWallet", - getenv("CLIGHTNING_RPC"), - 1 if funding_wallet == "CLightningWallet" else 0, - ), - ) - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "SparkWallet", - getenv("SPARK_URL"), - getenv("SPARK_TOKEN"), - 1 if funding_wallet == "SparkWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LnbitsWallet", - getenv("LNBITS_ENDPOINT"), - getenv("LNBITS_KEY"), - 1 if funding_wallet == "LnbitsWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, port, admin_key, cert, selected) - VALUES (?, ?, ?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LndWallet", - getenv("LND_GRPC_ENDPOINT"), - getenv("LND_GRPC_PORT"), - getenv("LND_GRPC_MACAROON"), - getenv("LND_GRPC_CERT"), - 1 if funding_wallet == "LndWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected) - VALUES (?, ?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LndRestWallet", - getenv("LND_REST_ENDPOINT"), - getenv("LND_REST_MACAROON"), - getenv("LND_REST_CERT"), - 1 if funding_wallet == "LndWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, cert, selected) - VALUES (?, ?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LNPayWallet", - getenv("LNPAY_API_ENDPOINT"), - getenv("LNPAY_WALLET_KEY"), - getenv("LNPAY_API_KEY"), # this is going in as the cert - 1 if funding_wallet == "LNPayWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "LntxbotWallet", - getenv("LNTXBOT_API_ENDPOINT"), - getenv("LNTXBOT_KEY"), - 1 if funding_wallet == "LntxbotWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "OpenNodeWallet", - getenv("OPENNODE_API_ENDPOINT"), - getenv("OPENNODE_KEY"), - 1 if funding_wallet == "OpenNodeWallet" else 0, - ), - ) - - await db.execute( - """ - INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - VALUES (?, ?, ?, ?, ?) - """, - ( - urlsafe_short_hash(), - "SparkWallet", - getenv("SPARK_URL"), - getenv("SPARK_TOKEN"), - 1 if funding_wallet == "SparkWallet" else 0, - ), - ) - - ## PLACEHOLDER FOR ECLAIR WALLET - # await db.execute( - # """ - # INSERT INTO admin.funding (id, backend_wallet, endpoint, admin_key, selected) - # VALUES (?, ?, ?, ?, ?) - # """, - # ( - # urlsafe_short_hash(), - # "EclairWallet", - # getenv("ECLAIR_URL"), - # getenv("ECLAIR_PASS"), - # 1 if funding_wallet == "EclairWallet" else 0, - # ), - # ) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 6e95d68fb..ef57cadd3 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -29,36 +29,36 @@ class UpdateAdminSettings(BaseModel): ad_space: str = Query(None) -class Admin(BaseModel): - # users - user: str - admin_users: Optional[str] - allowed_users: Optional[str] - admin_ext: Optional[str] - disabled_ext: Optional[str] - funding_source: Optional[str] - # ops - data_folder: Optional[str] - database_url: Optional[str] - force_https: bool = Field(default=True) - reserve_fee_min: Optional[int] - reserve_fee_pct: Optional[float] - service_fee: float = Optional[float] - hide_api: bool = Field(default=False) - # Change theme - site_title: Optional[str] - site_tagline: Optional[str] - site_description: Optional[str] - default_wallet_name: Optional[str] - denomination: str = Field(default="sats") - theme: Optional[str] - custom_logo: Optional[str] - ad_space: Optional[str] +# class Admin(BaseModel): +# # users +# user: str +# admin_users: Optional[str] +# allowed_users: Optional[str] +# admin_ext: Optional[str] +# disabled_ext: Optional[str] +# funding_source: Optional[str] +# # ops +# data_folder: Optional[str] +# database_url: Optional[str] +# force_https: bool = Field(default=True) +# reserve_fee_min: Optional[int] +# reserve_fee_pct: Optional[float] +# service_fee: float = Optional[float] +# hide_api: bool = Field(default=False) +# # Change theme +# site_title: Optional[str] +# site_tagline: Optional[str] +# site_description: Optional[str] +# default_wallet_name: Optional[str] +# denomination: str = Field(default="sats") +# theme: Optional[str] +# custom_logo: Optional[str] +# ad_space: Optional[str] - @classmethod - def from_row(cls, row: Row) -> "Admin": - data = dict(row) - return cls(**data) +# @classmethod +# def from_row(cls, row: Row) -> "Admin": +# data = dict(row) +# return cls(**data) class Funding(BaseModel): diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html new file mode 100644 index 000000000..2ed0aae27 --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html @@ -0,0 +1,158 @@ + + +
Wallets Management
+
+
+
+
+

Funding Source Info

+
    + {%raw%} +
  • + Funding Source: {{data.settings.lnbits_backend_wallet_class}} +
  • +
  • Balance: {{data.balance / 1000}} sats
  • + {%endraw%} +
+
+
+
+
+
+
+
+

Active Funding (Requires server restart)

+ +
+
+ +
+
+
+

Fee reserve

+
+
+ +
+
+ +
+
+
+
+
+
+

TopUp a wallet

+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+

Funding Sources

+ {% raw %} + + + + + + + + + + + + + + {% endraw %} +
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_server.html b/lnbits/extensions/admin/templates/admin/_tab_server.html new file mode 100644 index 000000000..2924e6a40 --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/_tab_server.html @@ -0,0 +1,78 @@ + + +
Server Management
+
+
+
+
+

Server Info

+
    + {%raw%} +
  • + SQlite: {{data.settings.lnbits_data_folder}} +
  • +
  • + Postgres: {{data.settings.lnbits_database_url}} +
  • + {%endraw%} +
+
+
+
+
+
+

Service Fee

+ +
+
+
+

Miscelaneous

+ + + Force HTTPS + Prefer secure URLs + + + + + + + + Hide API + Hides wallet api, extensions can choose to honor + + + + + +
+
+
+
+ +
+ Save +
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_theme.html b/lnbits/extensions/admin/templates/admin/_tab_theme.html new file mode 100644 index 000000000..41dc04477 --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/_tab_theme.html @@ -0,0 +1,122 @@ + + +
UI Management
+
+
+
+
+

Site Title

+ +
+
+
+

Site Tagline

+ +
+
+
+
+

Site Description

+ +
+
+
+
+

Default Wallet Name

+ +
+
+
+

Denomination

+ +
+
+
+
+
+

Themes

+ +
+
+
+

Advertisement Slots

+ + + +
+ {% raw %} + + {{ space.slice(0, 8) + " ... " + space.slice(-8) }} + + {% endraw %} +
+
+
+
+
+
+

Custom Logo

+ +
+
+
+
+ +
+ Save +
+
+
diff --git a/lnbits/extensions/admin/templates/admin/_tab_users.html b/lnbits/extensions/admin/templates/admin/_tab_users.html new file mode 100644 index 000000000..3eb53ac48 --- /dev/null +++ b/lnbits/extensions/admin/templates/admin/_tab_users.html @@ -0,0 +1,96 @@ + + +
User Management
+
+

+ Super Admin: {% raw %}{{this.data.settings.lnbits_admin_users[0]}}{% + endraw %} +

+
+
+

Admin Users

+ + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+

Allowed Users

+ + + +
+ {% raw %} + + {{ user }} + + {% endraw %} +
+
+
+
+
+

Admin Extensions

+ +
+
+
+

Disabled Extensions

+ +
+
+
+
+ Save +
+
+
diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 319ca3f0d..87e893215 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -29,1118 +29,16 @@ - - - -
Wallets Management
-
-
-
-
-

Funding Source Info

-
    - {%raw%} -
  • Funding Source: {{data.admin.funding_source}}
  • -
  • Balance: {{data.admin.balance / 1000}} sats
  • - {%endraw%} -
-
-
-
-
-
-
-
-

- Active Funding - (Requires server restart) -

- -
-
- -
-
-
-

Fee reserve

-
-
- -
-
- -
-
- -
-
-
-
- -

TopUp a wallet

-
-
- -
-
-
- -
-
-
- -
- -
-
-
-

Funding Sources

- {% raw %} - - - - - - - - - - - - - - {% endraw %} - -
-
-
- - -
User Management
-
-

- Super Admin: {% raw %}{{this.data.admin.user}}{% endraw %} -

-
-
-

Admin Users

- - - -
- {% raw %} - - {{ user }} - - {% endraw %} -
-
-
-
-

Allowed Users

- - - -
- {% raw %} - - {{ user }} - - {% endraw %} -
-
-
-
-
-

Admin Extensions

- -
-
-
-

Disabled Extensions

- -
-
-
-
- Save -
-
-
- - -
Server Management
-
-
-
-
-

Server Info

-
    - {%raw%} -
  • - SQlite: {{data.admin.data_folder}} -
  • -
  • - Postgres: {{data.admin.database_url}} -
  • - {%endraw%} -
-
-
-
-
-
-

Service Fee

- -
-
-
-

Miscelaneous

- - - Force HTTPS - Prefer secure URLs - - - - - - - - Hide API - Hides wallet api, extensions can choose to - honor - - - - - -
-
-
-
- -
- Save -
-
-
- - -
UI Management
-
-
-
-
-

Site Title

- -
-
-
-

Site Tagline

- -
-
-
-
-

Site Description

- -
-
-
-
-

Default Wallet Name

- -
-
-
-

Denomination

- -
-
-
-
-
-

Themes

- -
-
-
-

Advertisement Slots

- - - -
- {% raw %} - - {{ space.slice(0, 8) + " ... " + space.slice(-8) }} - - {% endraw %} -
-
-
-
-
-
-

Custom Logo

- -
-
-
-
- -
- Save -
-
-
+ {% include "admin/_tab_funding.html" %} {% include + "admin/_tab_users.html" %} {% include "admin/_tab_server.html" %} {% + include "admin/_tab_theme.html" %}
- - -
- -
{% endblock %} {% block scripts %} {{ window_vars(user) }} {% endblock %} From 04b37458983094ff9deec4ba010ec2a93c002cd9 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Fri, 7 Oct 2022 19:24:07 +0100 Subject: [PATCH 196/614] make saving possible (possible will change in future) --- .../admin/templates/admin/index.html | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 72352651b..18df16a97 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -121,8 +121,11 @@ created: function () { this.settings = JSON.parse('{{ settings|tojson|safe }}') //DB data this.balance = +'{{ balance|safe }}' - this.formData = this.settings //model + this.formData = _.clone(this.settings) //model + //this.formData.lnbits_ad_space = "hdh" console.log(this.formData) + console.log(_.isEqual(this.settings, this.formData)) + }, methods: { addAdminUser() { @@ -206,18 +209,27 @@ }, updateSettings() { let data = { - ...this.settings, - ...this.formData + lnbits_backend_wallet_class: this.formData.lnbits_backend_wallet_class, + lnbits_admin_users: this.formData.lnbits_admin_users.toString(), + lnbits_allowed_users: this.formData.lnbits_allowed_users.toString(), + lnbits_admin_ext: this.formData.lnbits_admin_ext, + lnbits_disabled_ext: this.formData.lnbits_disabled_ext, + lnbits_funding_source: this.formData.lnbits_funding_source, + lnbits_force_https: this.formData.lnbits_force_https, + lnbits_reserve_fee_min: this.formData.lnbits_reserve_fee_min, + lnbits_reserve_fee_percent: this.formData.lnbits_reserve_fee_percent, + lnbits_service_fee: this.formData.lnbits_service_fee, + lnbits_hide_api: this.formData.lnbits_hide_api, + lnbits_site_title: this.formData.lnbits_site_title, + lnbits_site_tagline: this.formData.lnbits_site_tagline, + lnbits_site_description: this.formData.lnbits_site_description, + lnbits_default_wallet_name: this.formData.lnbits_default_wallet_name, + lnbits_denomination: this.formData.lnbits_denomination, + lnbits_theme: this.formData.lnbits_theme, + lnbits_custom_logo: this.formData.lnbits_custom_logo, + lnbits_ad_space: this.formData.lnbits_ad_space.toString() } - /* - const formElement = document.getElementById('settings_form') - const formData = new FormData(formElement) - const data = {} - formData.forEach((value, key) => (data[key] = value)) - // only for debugging - for (const [key, value] of formData) { - console.log(`${key}: ${value}\n`) - }*/ + console.log(data) LNbits.api .request( 'PUT', From cc42df12f4d3927c854675f114aff0b75bef6d19 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Fri, 7 Oct 2022 19:44:03 +0100 Subject: [PATCH 197/614] some more refining --- lnbits/extensions/admin/models.py | 10 +++++----- .../extensions/admin/templates/admin/_tab_users.html | 2 ++ lnbits/extensions/admin/templates/admin/index.html | 12 +++++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 13a6cd233..45cd990d4 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -4,10 +4,10 @@ from pydantic import BaseModel class UpdateSettings(BaseModel): lnbits_backend_wallet_class: str = Query(None) - lnbits_admin_users: str = Query(None) - lnbits_allowed_users: str = Query(None) - lnbits_admin_ext: str = Query(None) - lnbits_disabled_ext: str = Query(None) + lnbits_admin_users: str = Query(None) #this should be List[str] ?? + lnbits_allowed_users: str = Query(None) #this should be List[str] ?? + lnbits_admin_ext: str = Query(None) #this should be List[str] ?? + lnbits_disabled_ext: str = Query(None) #this should be List[str] ?? lnbits_funding_source: str = Query(None) lnbits_force_https: bool = Query(None) lnbits_reserve_fee_min: int = Query(None, ge=0) @@ -21,4 +21,4 @@ class UpdateSettings(BaseModel): lnbits_denomination: str = Query(None) lnbits_theme: str = Query(None) lnbits_custom_logo: str = Query(None) - lnbits_ad_space: str = Query(None) + lnbits_ad_space: str = Query(None) #this should be List[str] ?? diff --git a/lnbits/extensions/admin/templates/admin/_tab_users.html b/lnbits/extensions/admin/templates/admin/_tab_users.html index c396ba7d6..08b08a628 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_users.html +++ b/lnbits/extensions/admin/templates/admin/_tab_users.html @@ -71,6 +71,7 @@ multiple hint="Extensions only user with admin privileges can use" label="Admin extensions" + :options="g.extensions.map(e => e.name)" >
@@ -79,6 +80,7 @@
- + Date: Mon, 10 Oct 2022 12:17:35 +0100 Subject: [PATCH 198/614] get saved data and alert when data changed --- .../admin/templates/admin/index.html | 18 +++++++----------- lnbits/extensions/admin/views_api.py | 5 +++-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 4754656d7..4e401cb40 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -3,7 +3,7 @@
- + { + this.settings = response.data.settings + this.formData = _.clone(this.settings) this.$q.notify({ type: 'positive', message: 'Success! Settings changed!', diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index c81205643..c2079e377 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -43,8 +43,9 @@ async def api_update_settings( user: User = Depends(check_admin), data: UpdateSettings = Body(...), ): - await update_settings(data) - return {"status": "Success"} + settings = await update_settings(data) + logger.debug(settings) + return {"status": "Success", "settings": settings.dict()} @admin_ext.delete("/api/v1/settings/", status_code=HTTPStatus.OK) From 001f6589bb3c173c9ada0f86ec0a2eca08f3b051 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Mon, 10 Oct 2022 12:23:19 +0100 Subject: [PATCH 199/614] cleanup and typing fix for data --- lnbits/extensions/admin/models.py | 12 +++++----- .../admin/templates/admin/index.html | 22 ++----------------- lnbits/extensions/admin/views_api.py | 1 - 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 45cd990d4..94fa56bb7 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -1,13 +1,15 @@ +from typing import List + from fastapi import Query from pydantic import BaseModel class UpdateSettings(BaseModel): lnbits_backend_wallet_class: str = Query(None) - lnbits_admin_users: str = Query(None) #this should be List[str] ?? - lnbits_allowed_users: str = Query(None) #this should be List[str] ?? - lnbits_admin_ext: str = Query(None) #this should be List[str] ?? - lnbits_disabled_ext: str = Query(None) #this should be List[str] ?? + lnbits_admin_users: List[str] = Query(None) + lnbits_allowed_users: List[str] = Query(None) + lnbits_admin_ext: List[str] = Query(None) + lnbits_disabled_ext: List[str] = Query(None) lnbits_funding_source: str = Query(None) lnbits_force_https: bool = Query(None) lnbits_reserve_fee_min: int = Query(None, ge=0) @@ -21,4 +23,4 @@ class UpdateSettings(BaseModel): lnbits_denomination: str = Query(None) lnbits_theme: str = Query(None) lnbits_custom_logo: str = Query(None) - lnbits_ad_space: str = Query(None) #this should be List[str] ?? + lnbits_ad_space: List[str] = Query(None) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 4e401cb40..d8111595f 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -209,26 +209,8 @@ }) }, updateSettings() { - let data = { - lnbits_backend_wallet_class: this.formData.lnbits_backend_wallet_class, - lnbits_admin_users: this.formData.lnbits_admin_users.toString(), - lnbits_allowed_users: this.formData.lnbits_allowed_users.toString(), - lnbits_admin_ext: this.formData.lnbits_admin_ext, - lnbits_disabled_ext: this.formData.lnbits_disabled_ext, - lnbits_funding_source: this.formData.lnbits_funding_source, - lnbits_force_https: this.formData.lnbits_force_https, - lnbits_reserve_fee_min: this.formData.lnbits_reserve_fee_min, - lnbits_reserve_fee_percent: this.formData.lnbits_reserve_fee_percent, - lnbits_service_fee: this.formData.lnbits_service_fee, - lnbits_hide_api: this.formData.lnbits_hide_api, - lnbits_site_title: this.formData.lnbits_site_title, - lnbits_site_tagline: this.formData.lnbits_site_tagline, - lnbits_site_description: this.formData.lnbits_site_description, - lnbits_default_wallet_name: this.formData.lnbits_default_wallet_name, - lnbits_denomination: this.formData.lnbits_denomination, - lnbits_theme: this.formData.lnbits_theme, - lnbits_custom_logo: this.formData.lnbits_custom_logo, - lnbits_ad_space: this.formData.lnbits_ad_space.toString() + let data = { + ...this.formData } LNbits.api .request( diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index c2079e377..19b52e352 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -44,7 +44,6 @@ async def api_update_settings( data: UpdateSettings = Body(...), ): settings = await update_settings(data) - logger.debug(settings) return {"status": "Success", "settings": settings.dict()} From 1cc54ff4b7e19366499743fc9cb881bb58ccf2c6 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 12 Oct 2022 19:04:46 +0100 Subject: [PATCH 200/614] add funding sources options --- lnbits/app.py | 1 + lnbits/extensions/admin/models.py | 41 +++- .../admin/templates/admin/_tab_funding.html | 25 +- .../admin/templates/admin/index.html | 217 +++++++++++++++++- 4 files changed, 259 insertions(+), 25 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index a8371950b..50f218b72 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -84,6 +84,7 @@ async def check_funding_source() -> None: def signal_handler(signal, frame): logger.debug(f"SIGINT received, terminating LNbits.") sys.exit(1) + signal.signal(signal.SIGINT, signal_handler) WALLET = get_wallet_class() diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 94fa56bb7..d9d2b22ff 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -6,10 +6,10 @@ from pydantic import BaseModel class UpdateSettings(BaseModel): lnbits_backend_wallet_class: str = Query(None) - lnbits_admin_users: List[str] = Query(None) - lnbits_allowed_users: List[str] = Query(None) - lnbits_admin_ext: List[str] = Query(None) - lnbits_disabled_ext: List[str] = Query(None) + lnbits_admin_users: List[str] = Query(None) + lnbits_allowed_users: List[str] = Query(None) + lnbits_admin_ext: List[str] = Query(None) + lnbits_disabled_ext: List[str] = Query(None) lnbits_funding_source: str = Query(None) lnbits_force_https: bool = Query(None) lnbits_reserve_fee_min: int = Query(None, ge=0) @@ -23,4 +23,35 @@ class UpdateSettings(BaseModel): lnbits_denomination: str = Query(None) lnbits_theme: str = Query(None) lnbits_custom_logo: str = Query(None) - lnbits_ad_space: List[str] = Query(None) + lnbits_ad_space: List[str] = Query(None) + + # funding sources + fake_wallet_secret: str = Query(None) + lnbits_endpoint: str = Query(None) + lnbits_key: str = Query(None) + cliche_endpoint: str = Query(None) + corelightning_rpc: str = Query(None) + eclair_url: str = Query(None) + eclair_pass: str = Query(None) + lnd_rest_endpoint: str = Query(None) + lnd_rest_cert: str = Query(None) + lnd_rest_macaroon: str = Query(None) + lnd_rest_macaroon_encrypted: str = Query(None) + lnd_cert: str = Query(None) + lnd_admin_macaroon: str = Query(None) + lnd_invoice_macaroon: str = Query(None) + lnd_grpc_endpoint: str = Query(None) + lnd_grpc_cert: str = Query(None) + lnd_grpc_port: int = Query(None, ge=0) + lnd_grpc_admin_macaroon: str = Query(None) + lnd_grpc_invoice_macaroon: str = Query(None) + lnd_grpc_macaroon_encrypted: str = Query(None) + lnpay_api_endpoint: str = Query(None) + lnpay_api_key: str = Query(None) + lnpay_wallet_key: str = Query(None) + lntxbot_api_endpoint: str = Query(None) + lntxbot_key: str = Query(None) + opennode_api_endpoint: str = Query(None) + opennode_key: str = Query(None) + spark_url: str = Query(None) + spark_token: str = Query(None) diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html index 8b5456f19..a523d4e59 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_funding.html +++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html @@ -8,9 +8,7 @@

Funding Source Info

    {%raw%} -
  • - Funding Source: {{settings.lnbits_backend_wallet_class}} -
  • +
  • Funding Source: {{settings.lnbits_backend_wallet_class}}
  • Balance: {{balance / 1000}} sats
  • {%endraw%}
@@ -60,21 +58,30 @@
-

Funding Sources

+

Funding Sources (Requires server restart)

- + - - + diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index d8111595f..ccaddda43 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -3,7 +3,13 @@
- + u !== user - ) + this.settings.lnbits_admin_users = admin_users.filter(u => u !== user) }, addAllowedUser() { let addUser = this.formData.allowed_users_add @@ -155,7 +335,9 @@ }, removeAllowedUser(user) { let allowed_users = this.settings.lnbits_allowed_users - this.settings.lnbits_allowed_users = allowed_users.filter(u => u !== user) + this.settings.lnbits_allowed_users = allowed_users.filter( + u => u !== user + ) }, addAdSpace() { let adSpace = this.formData.ad_space_add @@ -208,8 +390,19 @@ LNbits.utils.notifyApiError(error) }) }, + updateFundingData(){ + this.settings.lnbits_allowed_funding_sources.map(f => { + let opts = this.funding_sources.get(f) + if (!opts) return + + Object.keys(opts).forEach(e => { + opts[e].value = this.settings[e] + }) + }) + console.log("funding", this.funding_sources) + }, updateSettings() { - let data = { + let data = { ...this.formData } LNbits.api @@ -222,11 +415,13 @@ .then(response => { this.settings = response.data.settings this.formData = _.clone(this.settings) + this.updateFundingData() this.$q.notify({ type: 'positive', message: 'Success! Settings changed!', icon: null }) + console.log(this.settings) }) .catch(function (error) { LNbits.utils.notifyApiError(error) @@ -262,7 +457,7 @@ LNbits.utils.notifyApiError(error) }) } - }, + } }) {% endblock %} From 761fc427defc590913124f1ad2f1b518633fbad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 10 Oct 2022 23:27:46 +0200 Subject: [PATCH 201/614] add callback for saas app --- lnbits/app.py | 2 +- lnbits/extensions/admin/migrations.py | 3 +++ lnbits/extensions/admin/views_api.py | 5 ----- lnbits/settings.py | 32 +++++++++++++++++++++++---- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 50f218b72..49ad8d77d 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -64,7 +64,7 @@ def create_app() -> FastAPI: # TODO: why those 2? g().config = settings - # g().base_url = f"http://{settings.host}:{settings.port}" + g().base_url = f"http://{settings.host}:{settings.port}" app.add_middleware(GZipMiddleware, minimum_size=1000) diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index c4bc98d84..ea698c27b 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -6,6 +6,9 @@ async def m001_create_admin_settings_table(db): debug TEXT, host TEXT, port INTEGER, + lnbits_saas_instance_id TEXT, + lnbits_saas_callback TEXT, + lnbits_saas_secret TEXT, lnbits_path TEXT, lnbits_commit TEXT, lnbits_admin_users TEXT, diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 19b52e352..ae2959bc8 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -53,8 +53,3 @@ async def api_delete_settings( ): await delete_settings() return {"status": "Success"} - - -@admin_ext.get("/api/v1/backup/", status_code=HTTPStatus.OK) -async def api_backup(user: User = Depends(check_admin)): - return {"status": "not implemented"} diff --git a/lnbits/settings.py b/lnbits/settings.py index ffcdcc0a9..f183211c4 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,6 +1,7 @@ import importlib import json import subprocess +import httpx from os import path from sqlite3 import Row from typing import List, Optional @@ -9,6 +10,7 @@ from loguru import logger from pydantic import BaseSettings, Field, validator + def list_parse_fallback(v): try: return json.loads(v) @@ -34,6 +36,11 @@ class Settings(BaseSettings): lnbits_path: str = Field(default=".") lnbits_commit: str = Field(default="unknown") + # saas + lnbits_saas_callback: Optional[str] = Field(default=None) + lnbits_saas_secret: Optional[str] = Field(default=None) + lnbits_saas_instance_id: Optional[str] = Field(default=None) + # users lnbits_admin_users: List[str] = Field(default=[]) lnbits_allowed_users: List[str] = Field(default=[]) @@ -230,11 +237,28 @@ async def check_admin_settings(): http = "https" if settings.lnbits_force_https else "http" user = settings.lnbits_admin_users[0] - logger.warning( - f" ✔️ Access admin user account at: {http}://{settings.host}:{settings.port}/wallet?usr={user}" - ) + + admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}" + logger.warning(f"✔️ Access admin user account at: {admin_url}") + + if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id: + with httpx.Client() as client: + headers = { + "Content-Type": "application/json; charset=utf-8", + "X-API-KEY": settings.lnbits_saas_secret + } + payload = { + "instance_id": settings.lnbits_saas_instance_id, + "adminuser": user + } + try: + r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload) + logger.warning("sent admin user to saas application") + except: + logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}") + except: - logger.warning("admin.settings tables does not exist.") + logger.error("admin.settings tables does not exist.") raise From 620fd2569655aa0f45b486201bcac75654f28326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 12 Oct 2022 13:08:59 +0200 Subject: [PATCH 202/614] bugfixes and fix topup wallet --- .../admin/templates/admin/index.html | 6 ++-- lnbits/extensions/admin/views_api.py | 36 ++++++++++--------- lnbits/server.py | 1 + lnbits/settings.py | 29 ++++++++++----- lnbits/wallets/fake.py | 2 +- 5 files changed, 44 insertions(+), 30 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index ccaddda43..575b377f2 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -369,10 +369,10 @@ topupWallet() { LNbits.api .request( - 'POST', + 'PUT', '/admin/api/v1/topup/?usr=' + this.g.user.id, - this.wallet.id, - this.wallet.amount + this.g.user.wallets[0].adminkey, + this.wallet ) .then(response => { this.$q.notify({ diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index ae2959bc8..63ed5b3ca 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -1,7 +1,6 @@ from http import HTTPStatus -from fastapi import Body, Depends, Request -from loguru import logger +from fastapi import Body, Depends, Query from starlette.exceptions import HTTPException from lnbits.core.crud import get_wallet @@ -9,47 +8,50 @@ from lnbits.core.models import User from lnbits.decorators import check_admin from lnbits.extensions.admin import admin_ext from lnbits.extensions.admin.models import UpdateSettings -from lnbits.requestvars import g from lnbits.server import server_restart -from lnbits.settings import settings from .crud import delete_settings, update_settings, update_wallet_balance -@admin_ext.get("/api/v1/restart/", status_code=HTTPStatus.OK) -async def api_restart_server(user: User = Depends(check_admin)): +@admin_ext.get( + "/api/v1/restart/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) +async def api_restart_server() -> dict[str, str]: server_restart.set() return {"status": "Success"} -@admin_ext.put("/api/v1/topup/", status_code=HTTPStatus.OK) +@admin_ext.put( + "/api/v1/topup/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) async def api_update_balance( - wallet_id, topup_amount: int, user: User = Depends(check_admin) -): + id: str = Body(...), amount: int = Body(...) +) -> dict[str, str]: try: - wallet = await get_wallet(wallet_id) + await get_wallet(id) except: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="wallet does not exist." ) - await update_wallet_balance(wallet_id=wallet_id, amount=int(topup_amount)) + await update_wallet_balance(wallet_id=id, amount=int(amount)) return {"status": "Success"} -@admin_ext.put("/api/v1/settings/", status_code=HTTPStatus.OK) +@admin_ext.put( + "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) async def api_update_settings( - user: User = Depends(check_admin), data: UpdateSettings = Body(...), ): settings = await update_settings(data) return {"status": "Success", "settings": settings.dict()} -@admin_ext.delete("/api/v1/settings/", status_code=HTTPStatus.OK) -async def api_delete_settings( - user: User = Depends(check_admin), -): +@admin_ext.delete( + "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] +) +async def api_delete_settings() -> dict[str, str]: await delete_settings() return {"status": "Success"} diff --git a/lnbits/server.py b/lnbits/server.py index 79af8112f..6d4cd2e70 100644 --- a/lnbits/server.py +++ b/lnbits/server.py @@ -52,6 +52,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload: port=port, host=host, reload=reload, + forwarded_allow_ips="*", ssl_keyfile=ssl_keyfile, ssl_certfile=ssl_certfile, **d diff --git a/lnbits/settings.py b/lnbits/settings.py index f183211c4..61dbd6f29 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -1,20 +1,19 @@ import importlib import json import subprocess -import httpx from os import path from sqlite3 import Row from typing import List, Optional +import httpx from loguru import logger from pydantic import BaseSettings, Field, validator - def list_parse_fallback(v): try: return json.loads(v) - except Exception as e: + except Exception: replaced = v.replace(" ", "") if replaced: return replaced.split(",") @@ -238,24 +237,36 @@ async def check_admin_settings(): http = "https" if settings.lnbits_force_https else "http" user = settings.lnbits_admin_users[0] - admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={user}" + admin_url = ( + f"{http}://{settings.host}:{settings.port}/wallet?usr={user}" + ) logger.warning(f"✔️ Access admin user account at: {admin_url}") - if settings.lnbits_saas_callback and settings.lnbits_saas_secret and settings.lnbits_saas_instance_id: + if ( + settings.lnbits_saas_callback + and settings.lnbits_saas_secret + and settings.lnbits_saas_instance_id + ): with httpx.Client() as client: headers = { "Content-Type": "application/json; charset=utf-8", - "X-API-KEY": settings.lnbits_saas_secret + "X-API-KEY": settings.lnbits_saas_secret, } payload = { "instance_id": settings.lnbits_saas_instance_id, - "adminuser": user + "adminuser": user, } try: - r = client.post(settings.lnbits_saas_callback, headers=headers, json=payload) + client.post( + settings.lnbits_saas_callback, + headers=headers, + json=payload, + ) logger.warning("sent admin user to saas application") except: - logger.error(f"error sending admin user to saas: {settings.lnbits_saas_callback}") + logger.error( + f"error sending admin user to saas: {settings.lnbits_saas_callback}" + ) except: logger.error("admin.settings tables does not exist.") diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index 73458e8c4..94ff5f48b 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -19,7 +19,6 @@ from .base import ( class FakeWallet(Wallet): - queue: asyncio.Queue = asyncio.Queue(0) secret: str = settings.fake_wallet_secret privkey: str = hashlib.pbkdf2_hmac( "sha256", @@ -98,6 +97,7 @@ class FakeWallet(Wallet): return PaymentStatus(None) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value: Invoice = await self.queue.get() yield value.payment_hash From 9dbcd89c6ebdba4db6c0bc7d70d570a8ee5fffaa Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Thu, 13 Oct 2022 15:52:02 +0100 Subject: [PATCH 203/614] added tooltips, moved reset, and warnings --- .../admin/templates/admin/index.html | 97 ++++++++++++------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 575b377f2..7d268301b 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -1,8 +1,14 @@ {% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block page %}
-
- +
+ + Save your changes - - - + + + Restart the server for changes to take effect + + + + + Add funds to a wallet. + + > --> + + Delete all settings and reset to defaults. +
@@ -121,6 +136,7 @@ show: false }, tab: 'funding', + needsRestart: false, funding_sources: new Map([ ['VoidWallet', null], [ @@ -302,13 +318,12 @@ this.balance = +'{{ balance|safe }}' this.formData = _.clone(this.settings) //model this.updateFundingData() - console.log(this.settings) }, computed: { checkChanges() { return !_.isEqual(this.settings, this.formData) - }, + } }, methods: { addAdminUser() { @@ -361,6 +376,7 @@ message: 'Success! Restarted Server', icon: null }) + this.needsRestart = false }) .catch(function (error) { LNbits.utils.notifyApiError(error) @@ -390,16 +406,15 @@ LNbits.utils.notifyApiError(error) }) }, - updateFundingData(){ + updateFundingData() { this.settings.lnbits_allowed_funding_sources.map(f => { let opts = this.funding_sources.get(f) if (!opts) return - + Object.keys(opts).forEach(e => { opts[e].value = this.settings[e] }) }) - console.log("funding", this.funding_sources) }, updateSettings() { let data = { @@ -415,31 +430,41 @@ .then(response => { this.settings = response.data.settings this.formData = _.clone(this.settings) + this.needsRestart = true this.updateFundingData() this.$q.notify({ type: 'positive', message: 'Success! Settings changed!', icon: null }) - console.log(this.settings) }) .catch(function (error) { LNbits.utils.notifyApiError(error) }) }, deleteSettings() { - LNbits.api - .request('DELETE', '/admin/api/v1/settings/?usr=' + this.g.user.id) - .then(response => { - this.$q.notify({ - type: 'positive', - message: - 'Success! Restored settings to defaults, restart required!', - icon: null - }) - }) - .catch(function (error) { - LNbits.utils.notifyApiError(error) + LNbits.utils + .confirmDialog( + 'Are you sure you want to restore settings to default?' + ) + .onOk(() => { + LNbits.api + .request( + 'DELETE', + '/admin/api/v1/settings/?usr=' + this.g.user.id + ) + .then(response => { + this.$q.notify({ + type: 'positive', + message: + 'Success! Restored settings to defaults, restart required!', + icon: null + }) + this.needsRestart = true + }) + .catch(function (error) { + LNbits.utils.notifyApiError(error) + }) }) }, downloadBackup() { From 31a7a87774e5fcd87962440bc8c20510f607a328 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Thu, 13 Oct 2022 15:52:26 +0100 Subject: [PATCH 204/614] moved to columns --- .../admin/templates/admin/_tab_funding.html | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lnbits/extensions/admin/templates/admin/_tab_funding.html b/lnbits/extensions/admin/templates/admin/_tab_funding.html index a523d4e59..a69ecb47e 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_funding.html +++ b/lnbits/extensions/admin/templates/admin/_tab_funding.html @@ -27,38 +27,38 @@ :options="settings.lnbits_allowed_funding_sources" >
-
-

Fee reserve

-
-
- -
-
- -
+
+
+
+
+

Fee reserve

+
+
+ + +
+
+
-
-
-
-

Funding Sources (Requires server restart)

+

+ Funding Sources (Requires server restart) +

Date: Thu, 13 Oct 2022 15:52:39 +0100 Subject: [PATCH 205/614] themes not displaying fixed --- lnbits/extensions/admin/templates/admin/_tab_theme.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/admin/templates/admin/_tab_theme.html b/lnbits/extensions/admin/templates/admin/_tab_theme.html index 46bf83e9c..c327733ff 100644 --- a/lnbits/extensions/admin/templates/admin/_tab_theme.html +++ b/lnbits/extensions/admin/templates/admin/_tab_theme.html @@ -63,7 +63,7 @@

Themes

Date: Fri, 21 Oct 2022 10:00:47 +0200 Subject: [PATCH 206/614] add loop to uvicorn --- lnbits/server.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lnbits/server.py b/lnbits/server.py index 6d4cd2e70..eb7c12b16 100644 --- a/lnbits/server.py +++ b/lnbits/server.py @@ -1,12 +1,7 @@ -import asyncio - import uvloop - uvloop.install() -import contextlib import multiprocessing as mp -import sys import time import click @@ -49,6 +44,7 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload: while True: config = uvicorn.Config( "lnbits.__main__:app", + loop="uvloop", port=port, host=host, reload=reload, @@ -65,9 +61,10 @@ def main(ctx, port: int, host: str, ssl_keyfile: str, ssl_certfile: str, reload: server_restart.clear() server.should_exit = True server.force_exit = True + time.sleep(3) process.terminate() process.join() - time.sleep(3) + time.sleep(1) server_restart = mp.Event() From b14b9f3b3a48f5a2544864783de7129e8ae5bfb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 21 Oct 2022 11:13:40 +0200 Subject: [PATCH 207/614] add get settings endpoint with only values you can also save --- lnbits/app.py | 6 +---- lnbits/extensions/admin/crud.py | 12 +++++++++- lnbits/extensions/admin/models.py | 6 ++++- .../admin/templates/admin/index.html | 22 +++++++++++++++---- lnbits/extensions/admin/views_api.py | 7 +++++- lnbits/server.py | 1 + 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 49ad8d77d..959a81689 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -62,10 +62,6 @@ def create_app() -> FastAPI: CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"] ) - # TODO: why those 2? - g().config = settings - g().base_url = f"http://{settings.host}:{settings.port}" - app.add_middleware(GZipMiddleware, minimum_size=1000) register_startup(app) @@ -174,7 +170,7 @@ def register_assets(app: FastAPI): @app.on_event("startup") async def vendored_assets_variable(): - if g().config.debug: + if settings.debug: g().VENDORED_JS = map(url_for_vendored, get_js_vendored()) g().VENDORED_CSS = map(url_for_vendored, get_css_vendored()) else: diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index cc937b5e9..2ce916127 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -6,7 +6,7 @@ from lnbits.settings import Settings, read_only_variables from lnbits.tasks import internal_invoice_queue from . import db -from .models import UpdateSettings +from .models import AdminSettings, UpdateSettings async def update_wallet_balance(wallet_id: str, amount: int) -> str: @@ -26,6 +26,16 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str: await internal_invoice_queue.put(internal_id) +async def get_settings() -> AdminSettings: + row = await db.fetchone("SELECT * FROM admin.settings") + all_settings = Settings(**row) + settings = AdminSettings() + for key, value in row.items(): + if hasattr(settings, key): + setattr(settings, key, getattr(all_settings, key)) + return settings + + async def update_settings(data: UpdateSettings) -> Settings: fields = [] for key, value in data.dict(exclude_none=True).items(): diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index d9d2b22ff..318116594 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional from fastapi import Query from pydantic import BaseModel @@ -55,3 +55,7 @@ class UpdateSettings(BaseModel): opennode_key: str = Query(None) spark_url: str = Query(None) spark_token: str = Query(None) + + +class AdminSettings(UpdateSettings): + lnbits_allowed_funding_sources: Optional[List[str]] diff --git a/lnbits/extensions/admin/templates/admin/index.html b/lnbits/extensions/admin/templates/admin/index.html index 7d268301b..103912615 100644 --- a/lnbits/extensions/admin/templates/admin/index.html +++ b/lnbits/extensions/admin/templates/admin/index.html @@ -314,11 +314,8 @@ } }, created: function () { - this.settings = JSON.parse('{{ settings|tojson|safe }}') //DB data + this.getSettings() this.balance = +'{{ balance|safe }}' - this.formData = _.clone(this.settings) //model - this.updateFundingData() - console.log(this.settings) }, computed: { checkChanges() { @@ -416,6 +413,23 @@ }) }) }, + getSettings() { + LNbits.api + .request( + 'GET', + '/admin/api/v1/settings/?usr=' + this.g.user.id, + this.g.user.wallets[0].adminkey + ) + .then(response => { + this.settings = response.data + this.formData = _.clone(this.settings) + this.updateFundingData() + console.log(this.settings) + }) + .catch(function (error) { + LNbits.utils.notifyApiError(error) + }) + }, updateSettings() { let data = { ...this.formData diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 63ed5b3ca..57d62ed49 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -10,7 +10,7 @@ from lnbits.extensions.admin import admin_ext from lnbits.extensions.admin.models import UpdateSettings from lnbits.server import server_restart -from .crud import delete_settings, update_settings, update_wallet_balance +from .crud import delete_settings, get_settings, update_settings, update_wallet_balance @admin_ext.get( @@ -21,6 +21,11 @@ async def api_restart_server() -> dict[str, str]: return {"status": "Success"} +@admin_ext.get("/api/v1/settings/", dependencies=[Depends(check_admin)]) +async def api_get_settings() -> UpdateSettings: + return await get_settings() + + @admin_ext.put( "/api/v1/topup/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] ) diff --git a/lnbits/server.py b/lnbits/server.py index eb7c12b16..ecf7ff62c 100644 --- a/lnbits/server.py +++ b/lnbits/server.py @@ -1,4 +1,5 @@ import uvloop + uvloop.install() import multiprocessing as mp From 8fbf10909961c13b6c39008ae4ce3aaa750b9a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 25 Oct 2022 09:25:37 +0200 Subject: [PATCH 208/614] fix poetry lockCCC --- poetry.lock | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/poetry.lock b/poetry.lock index 459683900..bbce3c5cb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -258,24 +258,6 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "environs" -version = "9.3.3" -description = "simplified environment variable parsing" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -marshmallow = ">=3.0.0" -python-dotenv = "*" - -[package.extras] -dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] -django = ["dj-database-url", "dj-email-url", "django-cache-url"] -lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"] -tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] - [[package]] name = "fastapi" version = "0.78.0" @@ -1051,7 +1033,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black ( [metadata] lock-version = "1.1" python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7" -content-hash = "c4a01d5bfc24a8008348b6bd954717354554310afaaecbfc2a14222ad25aca42" +content-hash = "e798b36b5941b43ee249bc196fcfb28d8ee712947336d21467651c672ba0106b" [metadata.files] aiofiles = [ @@ -1307,10 +1289,6 @@ enum34 = [ {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, ] -environs = [ - {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"}, - {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"}, -] fastapi = [ {file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"}, {file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"}, From 678c269a9124a967300a34d75542ed0c0c35150a Mon Sep 17 00:00:00 2001 From: Gene Takavic Date: Fri, 18 Nov 2022 16:57:39 +0100 Subject: [PATCH 209/614] wipe card --- .../extensions/boltcards/static/js/index.js | 16 ++- .../boltcards/templates/boltcards/index.html | 102 ++++++++++++++---- 2 files changed, 97 insertions(+), 21 deletions(-) diff --git a/lnbits/extensions/boltcards/static/js/index.js b/lnbits/extensions/boltcards/static/js/index.js index e13c14fb6..1949050b3 100644 --- a/lnbits/extensions/boltcards/static/js/index.js +++ b/lnbits/extensions/boltcards/static/js/index.js @@ -150,6 +150,7 @@ new Vue({ }, qrCodeDialog: { show: false, + wipe: false, data: null } } @@ -260,9 +261,10 @@ new Vue({ }) }) }, - openQrCodeDialog(cardId) { + openQrCodeDialog(cardId, wipe) { var card = _.findWhere(this.cards, {id: cardId}) this.qrCodeDialog.data = { + id: card.id, link: window.location.origin + '/boltcards/api/v1/auth?a=' + card.otp, name: card.card_name, uid: card.uid, @@ -274,6 +276,18 @@ new Vue({ k4: card.k2, webhook_url: card.webhook_url } + this.qrCodeDialog.data_wipe = JSON.stringify({ + action: 'wipe', + id: 1, + k0: card.k0, + k1: card.k1, + k2: card.k2, + k3: card.k1, + k4: card.k2, + uid: card.uid, + version: 1 + }) + this.qrCodeDialog.wipe = wipe this.qrCodeDialog.show = true }, addCardOpen: function () { diff --git a/lnbits/extensions/boltcards/templates/boltcards/index.html b/lnbits/extensions/boltcards/templates/boltcards/index.html index 7b9713e29..b80e9685f 100644 --- a/lnbits/extensions/boltcards/templates/boltcards/index.html +++ b/lnbits/extensions/boltcards/templates/boltcards/index.html @@ -48,6 +48,7 @@ +