diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml index 4e6cfafc5..bcb83de53 100644 --- a/.github/workflows/regtest.yml +++ b/.github/workflows/regtest.yml @@ -36,7 +36,6 @@ jobs: run: | git clone https://github.com/lnbits/legend-regtest-enviroment.git docker cd docker - git checkout cln-24-11 chmod +x ./tests ./tests sudo chmod -R a+rwx . diff --git a/docs/guide/installation.md b/docs/guide/installation.md index d71d54ed5..db763bd6a 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -12,11 +12,15 @@ Note that by default LNbits uses SQLite as its database, which is simple and eff ## Option 1 (recommended): Poetry -It is recommended to use the latest version of Poetry. Make sure you have Python version 3.9 or higher installed. +It is recommended to use the latest version of Poetry. Make sure you have Python version `3.12` installed. -### Verify Python version +### Install Python 3.12 ```sh +sudo add-apt-repository ppa:deadsnakes/ppa +sudo apt update +sudo apt install python3.12 +sudo apt-get install python3.12-dev # ensure correct headers needed for secp256k1 python3 --version ``` @@ -28,16 +32,16 @@ curl -sSL https://install.python-poetry.org | python3 - export PATH="/home/user/.local/bin:$PATH" ``` +### install LNbits + ```sh git clone https://github.com/lnbits/lnbits.git cd lnbits +poetry env use 3.12 git checkout main - poetry install --only main - cp .env.example .env -# set funding source amongst other options -nano .env +# Optional: to set funding source amongst other options via the env `nano .env` ``` #### Running the server @@ -49,9 +53,16 @@ poetry run lnbits # Note that you have to add the line DEBUG=true in your .env file, too. ``` +#### LNbits-cli + +```sh +# A very useful terminal client for getting the supersuer ID, updating extensions, etc +poetry run lnbits-cli --help +``` + #### Updating the server -``` +```sh cd lnbits # Stop LNbits with `ctrl + x` git pull diff --git a/lnbits/core/crud/payments.py b/lnbits/core/crud/payments.py index 979c7b027..205282972 100644 --- a/lnbits/core/crud/payments.py +++ b/lnbits/core/crud/payments.py @@ -123,15 +123,20 @@ async def get_payments_paginated( clause.append("wallet_id = :wallet_id") if complete and pending: - pass + clause.append( + f"(status = '{PaymentState.SUCCESS}' OR status = '{PaymentState.PENDING}')" + ) elif complete: clause.append( - f"((amount > 0 AND status = '{PaymentState.SUCCESS}') OR amount < 0)" + f""" + ( + status = '{PaymentState.SUCCESS}' + OR (amount < 0 AND status = '{PaymentState.PENDING}') + ) + """ ) elif pending: clause.append(f"status = '{PaymentState.PENDING}'") - else: - pass if outgoing and incoming: pass @@ -289,8 +294,14 @@ async def get_payments_history( values = { "wallet_id": wallet_id, } + # count outgoing payments if they are still pending where = [ - f"wallet_id = :wallet_id AND (status = '{PaymentState.SUCCESS}' OR amount < 0)" + f""" + wallet_id = :wallet_id AND ( + status = '{PaymentState.SUCCESS}' + OR (amount < 0 AND status = '{PaymentState.PENDING}') + ) + """ ] transactions: list[dict] = await db.fetchall( f""" diff --git a/lnbits/core/models/audit.py b/lnbits/core/models/audit.py index 263662a56..8e251a3b3 100644 --- a/lnbits/core/models/audit.py +++ b/lnbits/core/models/audit.py @@ -51,8 +51,8 @@ class AuditFilters(FilterModel): class AuditCountStat(BaseModel): - field: str - total: float + field: str = "" + total: float = 0 class AuditStats(BaseModel): diff --git a/lnbits/core/templates/admin/index.html b/lnbits/core/templates/admin/index.html index 9970d6cd1..224c3e917 100644 --- a/lnbits/core/templates/admin/index.html +++ b/lnbits/core/templates/admin/index.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block page %}
-
+
+
Components diff --git a/lnbits/core/templates/core/extensions.html b/lnbits/core/templates/core/extensions.html index 209cfe146..b4f2f2551 100644 --- a/lnbits/core/templates/core/extensions.html +++ b/lnbits/core/templates/core/extensions.html @@ -1,21 +1,5 @@ {% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {{ window_vars(user, extensions) }}{% block page %} -
-
-

-
- -
- - - -
-
@@ -36,6 +20,25 @@ v-text="$t('only_admins_can_install')" > + + + + {% endif %} + +
+ + +
+ + + +
+
+
+
+ + {{LNBITS_DENOMINATION}} +
+
+
+
+
+
dict[str, float]: - rate, price = await get_fiat_rate_satoshis(currency) + rate, price = await get_fiat_rate_and_price_satoshis(currency) return {"rate": rate, "price": price} diff --git a/lnbits/core/views/payment_api.py b/lnbits/core/views/payment_api.py index e27773a9d..98e1a8bb5 100644 --- a/lnbits/core/views/payment_api.py +++ b/lnbits/core/views/payment_api.py @@ -108,8 +108,6 @@ async def api_payments_paginated( await update_pending_payments(key_info.wallet.id) page = await get_payments_paginated( wallet_id=key_info.wallet.id, - pending=True, - complete=True, filters=filters, ) return page diff --git a/lnbits/static/js/wallet.js b/lnbits/static/js/wallet.js index aa0fffeab..a9fd62932 100644 --- a/lnbits/static/js/wallet.js +++ b/lnbits/static/js/wallet.js @@ -58,7 +58,8 @@ window.app = Vue.createApp({ inkeyHidden: true, adminkeyHidden: true, hasNfc: false, - nfcReaderAbortController: null + nfcReaderAbortController: null, + primaryColor: this.$q.localStorage.getItem('lnbits.primaryColor') } }, computed: { @@ -672,6 +673,11 @@ window.app = Vue.createApp({ watch: { updatePayments() { this.updateFiatBalance() + }, + '$q.screen.gt.sm'(value) { + if (value == true) { + this.mobileSimple = false + } } }, mounted() { diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py index cc8d46d49..a71ebbd46 100644 --- a/lnbits/utils/exchange_rates.py +++ b/lnbits/utils/exchange_rates.py @@ -247,7 +247,7 @@ async def btc_price(currency: str) -> float: return sum(rates_values) / len(rates_values) -async def get_fiat_rate_satoshis(currency: str) -> tuple[float, float]: +async def get_fiat_rate_and_price_satoshis(currency: str) -> tuple[float, float]: price = await cache.save_result( lambda: btc_price(currency), f"btc-price-{currency}", @@ -256,11 +256,16 @@ async def get_fiat_rate_satoshis(currency: str) -> tuple[float, float]: return float(100_000_000 / price), price +async def get_fiat_rate_satoshis(currency: str) -> float: + rate, _ = await get_fiat_rate_and_price_satoshis(currency) + return rate + + async def fiat_amount_as_satoshis(amount: float, currency: str) -> int: - rate, _ = await get_fiat_rate_satoshis(currency) + rate = await get_fiat_rate_satoshis(currency) return int(amount * (rate)) async def satoshis_amount_as_fiat(amount: float, currency: str) -> float: - rate, _ = await get_fiat_rate_satoshis(currency) + rate = await get_fiat_rate_satoshis(currency) return float(amount / rate) diff --git a/pyproject.toml b/pyproject.toml index e8ea7d889..46152190f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "lnbits" -version = "1.0.0-rc6" +version = "1.0.0-rc7" description = "LNbits, free and open-source Lightning wallet and accounts system." authors = ["Alan Bits "] readme = "README.md" diff --git a/tests/unit/test_crud_payments.py b/tests/unit/test_crud_payments.py new file mode 100644 index 000000000..b177ed730 --- /dev/null +++ b/tests/unit/test_crud_payments.py @@ -0,0 +1,66 @@ +import pytest + +from lnbits.core.crud import create_wallet, get_payments, update_payment +from lnbits.core.models import PaymentState +from lnbits.core.services import create_user_account, update_wallet_balance + + +async def update_payments(payments): + payments[0].status = PaymentState.FAILED + await update_payment(payments[0]) + payments[1].status = PaymentState.PENDING + await update_payment(payments[1]) + payments[2].status = PaymentState.PENDING + await update_payment(payments[2]) + + +@pytest.mark.anyio +async def test_crud_get_payments(app): + + user = await create_user_account() + wallet = await create_wallet(user_id=user.id) + + for _ in range(11): + await update_wallet_balance(wallet, 10) + wallet.balance_msat += 10 * 1000 + await update_wallet_balance(wallet, -10) + wallet.balance_msat += -10 * 1000 + + payments = await get_payments(wallet_id=wallet.id) + assert len(payments) == 22, "should return 22 successful payments" + + payments = await get_payments(wallet_id=wallet.id, incoming=True) + assert len(payments) == 11, "should return 11 successful incoming payments" + await update_payments(payments) + + payments = await get_payments(wallet_id=wallet.id, outgoing=True) + assert len(payments) == 11, "should return 11 successful outgoing payments" + await update_payments(payments) + + payments = await get_payments(wallet_id=wallet.id, pending=True) + assert len(payments) == 4, "should return 4 pending payments" + + # function signature should have Optional[bool] for complete and pending to make + # this distinction possible + payments = await get_payments(wallet_id=wallet.id, pending=False) + assert len(payments) == 22, "should return all payments" + + payments = await get_payments(wallet_id=wallet.id, complete=True, pending=True) + assert len(payments) == 20, "should return 4 pending and 16 complete payments" + + payments = await get_payments(wallet_id=wallet.id, complete=True, outgoing=True) + assert ( + len(payments) == 10 + ), "should return 8 complete outgoing payments and 2 pending outgoing payments" + + payments = await get_payments(wallet_id=wallet.id) + assert len(payments) == 22, "should return all payments" + + payments = await get_payments(wallet_id=wallet.id, complete=True) + assert ( + len(payments) == 18 + ), "should return 14 successful payment and 4 pending payments" + + # both false should return failed payments + # payments = await get_payments(wallet_id=wallet.id, complete=False, pending=False) + # assert len(payments) == 2, "should return 2 failed payment"