Compare commits
38
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
613f88faf2 | ||
|
|
6a2ade1695 | ||
|
|
8211f95bf1 | ||
|
|
3857402471 | ||
|
|
0f0e756c37 | ||
|
|
2e82b255e7 | ||
|
|
dda3685a81 | ||
|
|
301c588112 | ||
|
|
3b47db1b35 | ||
|
|
a2e5d1507c | ||
|
|
9f9057cca6 | ||
|
|
3d919aa1a8 | ||
|
|
1bedc428a5 | ||
|
|
43abd4ed82 | ||
|
|
742efd7dd9 | ||
|
|
7446dfc29c | ||
|
|
396fe72a3f | ||
|
|
77f112cd5e | ||
|
|
0003574d21 | ||
|
|
22e3ec3672 | ||
|
|
2e77dcf393 | ||
|
|
0b8b530ca6 | ||
|
|
193217d157 | ||
|
|
33fef98d9e | ||
|
|
8649129156 | ||
|
|
427894d106 | ||
|
|
d5cde46ed6 | ||
|
|
cc0ea50be0 | ||
|
|
633a0d2d2d | ||
|
|
21c007d2b3 | ||
|
|
b2d6243697 | ||
|
|
6b5a77fb3b | ||
|
|
cc8fb68b02 | ||
|
|
658da6b28e | ||
|
|
7d734ecb74 | ||
|
|
9177dd195b | ||
|
|
15faab4f38 | ||
|
|
2cce687865 |
@@ -1,9 +1,5 @@
|
||||
name: LNbits CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
pull_request:
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -43,4 +43,4 @@ ENV LNBITS_HOST="0.0.0.0"
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["sh", "-c", "uv run lnbits --port $LNBITS_PORT --host $LNBITS_HOST --forwarded-allow-ips='*'"]
|
||||
CMD ["sh", "-c", "uv --offline run lnbits --port $LNBITS_PORT --host $LNBITS_HOST --forwarded-allow-ips='*'"]
|
||||
|
||||
@@ -105,7 +105,8 @@ async def get_settings_field(
|
||||
)
|
||||
if not row:
|
||||
return None
|
||||
return SettingsField(id=row["id"], value=json.loads(row["value"]), tag=row["tag"])
|
||||
value = json.loads(row["value"]) if row["value"] else None
|
||||
return SettingsField(id=row["id"], value=value, tag=row["tag"])
|
||||
|
||||
|
||||
async def set_settings_field(id_: str, value: Any | None, tag: str | None = "core"):
|
||||
|
||||
@@ -173,6 +173,12 @@ async def check_admin_settings():
|
||||
if account and account.extra and account.extra.provider == "env":
|
||||
settings.first_install = True
|
||||
|
||||
if settings.has_first_install_token_changed():
|
||||
logger.warning("First install token is changed. Resetting admin settings.")
|
||||
new_settings = await init_admin_settings()
|
||||
settings.super_user = new_settings.super_user
|
||||
settings.first_install = True
|
||||
|
||||
logger.success(
|
||||
"✔️ Admin UI is enabled. run `uv run lnbits-cli superuser` "
|
||||
"to get the superuser."
|
||||
|
||||
@@ -12,6 +12,7 @@ from fastapi.responses import JSONResponse, RedirectResponse
|
||||
from fastapi_sso.sso.base import OpenID, SSOBase
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.core.crud.settings import set_settings_field
|
||||
from lnbits.core.crud.users import (
|
||||
get_user_access_control_lists,
|
||||
update_user_access_control_list,
|
||||
@@ -548,6 +549,13 @@ async def first_install(data: UpdateSuperuserPassword) -> JSONResponse:
|
||||
account.hash_password(data.password)
|
||||
await update_account(account)
|
||||
settings.first_install = False
|
||||
|
||||
# only confrm it after the super user has been successfully updated
|
||||
if settings.first_install_token:
|
||||
settings.first_install_token_confirmed = data.first_install_token
|
||||
await set_settings_field(
|
||||
"first_install_token_confirmed", data.first_install_token
|
||||
)
|
||||
return _auth_success_response(account.username, account.id, account.email)
|
||||
|
||||
|
||||
|
||||
@@ -447,6 +447,7 @@ class SecuritySettings(LNbitsSettings):
|
||||
|
||||
lnbits_max_outgoing_payment_amount_sats: int = Field(default=10_000_000, ge=0)
|
||||
lnbits_max_incoming_payment_amount_sats: int = Field(default=10_000_000, ge=0)
|
||||
first_install_token_confirmed: str | None = Field(default=None)
|
||||
|
||||
def is_wallet_max_balance_exceeded(self, amount):
|
||||
return (
|
||||
@@ -1030,6 +1031,13 @@ class EnvSettings(LNbitsSettings):
|
||||
def has_default_extension_path(self) -> bool:
|
||||
return self.lnbits_extensions_path == "lnbits"
|
||||
|
||||
def has_first_install_token_changed(self) -> bool:
|
||||
if not self.first_install_token:
|
||||
return False
|
||||
if not settings.first_install_token_confirmed:
|
||||
return False
|
||||
return self.first_install_token != settings.first_install_token_confirmed
|
||||
|
||||
def check_auth_secret_key(self):
|
||||
if self.auth_secret_key:
|
||||
return
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -165,5 +165,26 @@ window.app.component('lnbits-qrcode', {
|
||||
this.$refs.qrCode.$el.style.maxWidth = this.maxWidth + 'px'
|
||||
this.$refs.qrCode.$el.setAttribute('width', '100%')
|
||||
this.$refs.qrCode.$el.removeAttribute('height')
|
||||
},
|
||||
computed: {
|
||||
optimizedValue() {
|
||||
const separatorIndex = this.value.indexOf(':')
|
||||
const type =
|
||||
separatorIndex === -1 ? '' : this.value.substring(0, separatorIndex)
|
||||
const value =
|
||||
separatorIndex === -1
|
||||
? this.value
|
||||
: this.value.substring(separatorIndex + 1)
|
||||
|
||||
if (this.utils.isValidBech32(value)) {
|
||||
const normalizedValue = value.toUpperCase()
|
||||
if (type) {
|
||||
return `${type.toUpperCase()}:${normalizedValue}`
|
||||
}
|
||||
return normalizedValue
|
||||
}
|
||||
|
||||
return this.value
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -160,6 +160,46 @@ window._lnbitsUtils = {
|
||||
return null
|
||||
}
|
||||
},
|
||||
isValidBech32(value) {
|
||||
if (typeof value !== 'string') {
|
||||
return false
|
||||
}
|
||||
|
||||
const candidate = value.trim()
|
||||
if (
|
||||
!candidate ||
|
||||
(candidate !== candidate.toLowerCase() &&
|
||||
candidate !== candidate.toUpperCase())
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
const normalized = candidate.toLowerCase()
|
||||
const splitPosition = normalized.lastIndexOf('1')
|
||||
if (splitPosition <= 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
const humanReadablePart = normalized.substring(0, splitPosition)
|
||||
const data = normalized.substring(splitPosition + 1)
|
||||
if (data.length < 6) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
typeof bech32ToFiveBitArray !== 'function' ||
|
||||
typeof verify_checksum !== 'function'
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
const words = bech32ToFiveBitArray(data)
|
||||
if (words.some(word => word < 0)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return verify_checksum(humanReadablePart, words)
|
||||
},
|
||||
async notifyApiError(error) {
|
||||
if (!error.response) {
|
||||
return console.error(error)
|
||||
|
||||
@@ -82,19 +82,21 @@
|
||||
<!-- scripts libraries -->
|
||||
{% for url in INCLUDED_JS %}
|
||||
<script src="{{ static_url_for('static', url) }}"></script>
|
||||
{% endfor %}
|
||||
{% endfor %} {% if user %}
|
||||
<!-- user init -->
|
||||
{% if user %}
|
||||
<script>
|
||||
window.g.user = LNbits.map.user(JSON.parse({{ user | tojson | safe }}));
|
||||
{% if not public %}
|
||||
window.g.isPublicPage = false
|
||||
{% endif %}
|
||||
window.app = Vue.createApp({
|
||||
el: '#vue',
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
<!-- app init -->
|
||||
<script>
|
||||
window.app = Vue.createApp({
|
||||
el: '#vue'
|
||||
})
|
||||
</script>
|
||||
<!-- scripts from extensions -->
|
||||
{% block scripts %}{% endblock %}
|
||||
<!-- components js -->
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
>
|
||||
<qrcode-vue
|
||||
ref="qrCode"
|
||||
:value="value"
|
||||
:value="optimizedValue"
|
||||
:margin="margin"
|
||||
:size="size"
|
||||
level="Q"
|
||||
|
||||
@@ -15,7 +15,11 @@ from lnbits.settings import settings
|
||||
def log_server_info():
|
||||
logger.info("LNbits Info")
|
||||
if settings.first_install:
|
||||
logger.success("This is a fresh install of LNbits.")
|
||||
if settings.has_first_install_token_changed():
|
||||
logger.success("This is a first install token reset.")
|
||||
else:
|
||||
logger.success("This is a fresh install of LNbits.")
|
||||
|
||||
if settings.first_install_token:
|
||||
logger.success(
|
||||
f"FIRST_INSTALL_TOKEN: `{settings.first_install_token}`. "
|
||||
|
||||
+122
-23
@@ -80,22 +80,40 @@ class SparkL2Wallet(Wallet):
|
||||
|
||||
async def status(self) -> StatusResponse:
|
||||
try:
|
||||
res = await self._request("POST", "/v1/balance")
|
||||
status = res.get("status")
|
||||
r = await self.client.post("/v1/balance", timeout=30)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
if not isinstance(data, dict) or len(data) == 0:
|
||||
return StatusResponse("no data", 0)
|
||||
|
||||
error_message = self._extract_error_message(data)
|
||||
if error_message:
|
||||
return StatusResponse(self._server_error_message(error_message), 0)
|
||||
|
||||
status = data.get("status")
|
||||
if status == "missing_mnemonic":
|
||||
await self._check_sidecar_mnemonic()
|
||||
return StatusResponse("Spark sidecar mnemonic not set", 0)
|
||||
|
||||
balance_msat = res.get("balance_msat")
|
||||
balance_msat = data.get("balance_msat")
|
||||
if balance_msat is not None:
|
||||
return StatusResponse(None, int(balance_msat))
|
||||
balance_sats = res.get("balance_sats")
|
||||
balance_sats = data.get("balance_sats")
|
||||
if balance_sats is None:
|
||||
return StatusResponse("Spark sidecar: missing balance.", 0)
|
||||
return StatusResponse("no data", 0)
|
||||
return StatusResponse(None, int(balance_sats) * 1000)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.warning(e)
|
||||
return StatusResponse("Server error: 'invalid json response'", 0)
|
||||
except httpx.HTTPStatusError as e:
|
||||
logger.warning(e)
|
||||
error_message = self._extract_http_error_message(e.response)
|
||||
if error_message:
|
||||
return StatusResponse(self._server_error_message(error_message), 0)
|
||||
return StatusResponse(self._connect_error_message(), 0)
|
||||
except Exception as e:
|
||||
logger.warning(e)
|
||||
return StatusResponse(f"Spark sidecar status error: {e}", 0)
|
||||
return StatusResponse(self._connect_error_message(), 0)
|
||||
|
||||
async def create_invoice(
|
||||
self,
|
||||
@@ -121,13 +139,28 @@ class SparkL2Wallet(Wallet):
|
||||
"description_hash": description_hash_hex,
|
||||
"expiry_seconds": expiry_secs,
|
||||
}
|
||||
res = await self._request("POST", "/v1/invoices", payload)
|
||||
bolt11 = res.get("payment_request")
|
||||
checking_id = res.get("checking_id")
|
||||
r = await self.client.post("/v1/invoices", json=payload, timeout=30)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
|
||||
if not isinstance(data, dict):
|
||||
return InvoiceResponse(
|
||||
ok=False, error_message=self._server_error_message(r.text)
|
||||
)
|
||||
|
||||
error_message = self._extract_error_message(data)
|
||||
if error_message:
|
||||
return InvoiceResponse(
|
||||
ok=False,
|
||||
error_message=self._server_error_message(error_message),
|
||||
)
|
||||
|
||||
bolt11 = data.get("payment_request")
|
||||
checking_id = data.get("checking_id")
|
||||
if not bolt11 or not checking_id:
|
||||
return InvoiceResponse(
|
||||
ok=False,
|
||||
error_message="Spark sidecar invoice response missing fields.",
|
||||
error_message="Server error: 'missing required fields'",
|
||||
)
|
||||
self.pending_invoices.append(checking_id)
|
||||
|
||||
@@ -135,10 +168,24 @@ class SparkL2Wallet(Wallet):
|
||||
ok=True,
|
||||
payment_request=bolt11,
|
||||
checking_id=checking_id,
|
||||
preimage=res.get("preimage", None),
|
||||
preimage=data.get("preimage", None),
|
||||
)
|
||||
except json.JSONDecodeError:
|
||||
return InvoiceResponse(
|
||||
ok=False, error_message="Server error: 'invalid json response'"
|
||||
)
|
||||
except httpx.HTTPStatusError as e:
|
||||
logger.warning(e)
|
||||
error_message = self._extract_http_error_message(e.response)
|
||||
if error_message:
|
||||
return InvoiceResponse(
|
||||
ok=False,
|
||||
error_message=self._server_error_message(error_message),
|
||||
)
|
||||
return InvoiceResponse(ok=False, error_message=self._connect_error_message())
|
||||
except Exception as e:
|
||||
return InvoiceResponse(ok=False, error_message=str(e))
|
||||
logger.warning(e)
|
||||
return InvoiceResponse(ok=False, error_message=self._connect_error_message())
|
||||
|
||||
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
|
||||
try:
|
||||
@@ -158,27 +205,53 @@ class SparkL2Wallet(Wallet):
|
||||
"max_fee_sats": max_fee_sats,
|
||||
"payment_hash": payment_hash,
|
||||
}
|
||||
res = await self._request("POST", "/v1/payments", payload)
|
||||
checking_id = payment_hash or res.get("checking_id")
|
||||
r = await self.client.post("/v1/payments", json=payload, timeout=30)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
|
||||
if not isinstance(data, dict):
|
||||
return PaymentResponse(error_message=self._server_error_message(r.text))
|
||||
|
||||
error_message = self._extract_error_message(data)
|
||||
if error_message:
|
||||
return PaymentResponse(error_message=error_message)
|
||||
|
||||
if len(data) == 0:
|
||||
return PaymentResponse(
|
||||
error_message="Server error: 'missing required fields'"
|
||||
)
|
||||
|
||||
status = data.get("status")
|
||||
fee_msat = data.get("fee_msat")
|
||||
preimage = data.get("preimage")
|
||||
ok = self._map_payment_ok(status) if status else None
|
||||
if ok is False:
|
||||
return PaymentResponse(ok=False)
|
||||
|
||||
checking_id = payment_hash or data.get("checking_id")
|
||||
if not checking_id:
|
||||
return PaymentResponse(
|
||||
ok=False,
|
||||
error_message="Spark sidecar payment response missing checking_id.",
|
||||
error_message="Server error: 'missing required fields'"
|
||||
)
|
||||
status = res.get("status")
|
||||
fee_msat = res.get("fee_msat")
|
||||
ok = None
|
||||
if status:
|
||||
ok = self._map_payment_ok(status)
|
||||
|
||||
return PaymentResponse(
|
||||
ok=ok,
|
||||
checking_id=checking_id,
|
||||
fee_msat=int(fee_msat) if fee_msat is not None else None,
|
||||
preimage=res.get("preimage"),
|
||||
preimage=preimage,
|
||||
)
|
||||
except json.JSONDecodeError:
|
||||
return PaymentResponse(error_message="Server error: 'invalid json response'")
|
||||
except httpx.HTTPStatusError as e:
|
||||
logger.warning(e)
|
||||
error_message = self._extract_http_error_message(e.response)
|
||||
if error_message:
|
||||
return PaymentResponse(error_message=error_message)
|
||||
return PaymentResponse(error_message=self._connect_error_message())
|
||||
|
||||
except Exception as e:
|
||||
return PaymentResponse(ok=False, error_message=str(e))
|
||||
logger.warning(e)
|
||||
return PaymentResponse(error_message=self._connect_error_message())
|
||||
|
||||
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
||||
try:
|
||||
@@ -330,6 +403,32 @@ class SparkL2Wallet(Wallet):
|
||||
return False
|
||||
return None
|
||||
|
||||
def _connect_error_message(self) -> str:
|
||||
return f"Unable to connect to {self.endpoint}."
|
||||
|
||||
@staticmethod
|
||||
def _server_error_message(message: str) -> str:
|
||||
return f"Server error: '{message}'"
|
||||
|
||||
@staticmethod
|
||||
def _extract_error_message(data: Any) -> str | None:
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
for key in ("error", "message", "detail", "reason"):
|
||||
value = data.get(key)
|
||||
if value:
|
||||
return str(value)
|
||||
return None
|
||||
|
||||
def _extract_http_error_message(self, response: httpx.Response | None) -> str | None:
|
||||
if response is None:
|
||||
return None
|
||||
try:
|
||||
data = response.json()
|
||||
except Exception:
|
||||
return None
|
||||
return self._extract_error_message(data)
|
||||
|
||||
async def _check_sidecar_mnemonic(self):
|
||||
if settings.spark_l2_mnemonic:
|
||||
valid = mnemonic_is_valid(settings.spark_l2_mnemonic)
|
||||
|
||||
Generated
+3
-3
@@ -1498,9 +1498,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "lnbits"
|
||||
version = "1.5.2"
|
||||
version = "1.5.3"
|
||||
requires-python = ">=3.10,<3.13"
|
||||
description = "LNbits, free and open-source Lightning wallet and accounts system."
|
||||
authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }]
|
||||
|
||||
@@ -72,6 +72,7 @@ async def app(settings: Settings):
|
||||
username="superadmin",
|
||||
password="secret1234",
|
||||
password_repeat="secret1234",
|
||||
first_install_token=settings.first_install_token,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from lnbits.core.crud import create_account, delete_account, get_account
|
||||
from lnbits.core.crud.settings import get_settings_field, set_settings_field
|
||||
from lnbits.core.db import db
|
||||
from lnbits.core.models import Account, UpdateSuperuserPassword, UserExtra
|
||||
from lnbits.core.services.users import check_admin_settings
|
||||
from lnbits.core.views.auth_api import first_install
|
||||
from lnbits.settings import settings
|
||||
|
||||
|
||||
async def _restore_setting_field(field_name: str, original_row) -> None:
|
||||
if original_row is None:
|
||||
await db.execute(
|
||||
"DELETE FROM system_settings WHERE id = :id AND tag = :tag",
|
||||
{"id": field_name, "tag": "core"},
|
||||
)
|
||||
return
|
||||
|
||||
await set_settings_field(field_name, original_row.value, original_row.tag)
|
||||
|
||||
|
||||
def test_has_first_install_token_changed_requires_a_confirmed_mismatch():
|
||||
original_token = settings.first_install_token
|
||||
original_confirmed = settings.first_install_token_confirmed
|
||||
|
||||
try:
|
||||
settings.first_install_token = "new-token"
|
||||
settings.first_install_token_confirmed = "old-token"
|
||||
assert settings.has_first_install_token_changed() is True
|
||||
|
||||
settings.first_install_token_confirmed = "new-token"
|
||||
assert settings.has_first_install_token_changed() is False
|
||||
|
||||
settings.first_install_token_confirmed = None
|
||||
assert settings.has_first_install_token_changed() is False
|
||||
|
||||
settings.first_install_token = None
|
||||
assert settings.has_first_install_token_changed() is False
|
||||
finally:
|
||||
settings.first_install_token = original_token
|
||||
settings.first_install_token_confirmed = original_confirmed
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_first_install_confirms_first_install_token(app):
|
||||
temp_super_user = uuid4().hex
|
||||
username = f"super_{temp_super_user[:8]}"
|
||||
original_super_user = settings.super_user
|
||||
original_first_install = settings.first_install
|
||||
original_first_install_token = settings.first_install_token
|
||||
original_first_install_token_confirmed = settings.first_install_token_confirmed
|
||||
original_confirmed_row = await get_settings_field("first_install_token_confirmed")
|
||||
|
||||
await create_account(Account(id=temp_super_user, extra=UserExtra(provider="env")))
|
||||
|
||||
try:
|
||||
settings.super_user = temp_super_user
|
||||
settings.first_install = True
|
||||
settings.first_install_token = "expected-token"
|
||||
settings.first_install_token_confirmed = None
|
||||
|
||||
response = await first_install(
|
||||
UpdateSuperuserPassword(
|
||||
username=username,
|
||||
password="secret1234",
|
||||
password_repeat="secret1234",
|
||||
first_install_token="expected-token",
|
||||
)
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert settings.first_install is False
|
||||
assert settings.first_install_token_confirmed == "expected-token"
|
||||
|
||||
confirmed_row = await get_settings_field("first_install_token_confirmed")
|
||||
assert confirmed_row is not None
|
||||
assert confirmed_row.value == "expected-token"
|
||||
|
||||
account = await get_account(temp_super_user)
|
||||
assert account is not None
|
||||
assert account.username == username
|
||||
assert account.extra.provider == "lnbits"
|
||||
assert account.verify_password("secret1234")
|
||||
finally:
|
||||
await _restore_setting_field(
|
||||
"first_install_token_confirmed", original_confirmed_row
|
||||
)
|
||||
settings.super_user = original_super_user
|
||||
settings.first_install = original_first_install
|
||||
settings.first_install_token = original_first_install_token
|
||||
settings.first_install_token_confirmed = original_first_install_token_confirmed
|
||||
await delete_account(temp_super_user)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_check_admin_settings_clears_persisted_super_user_when_token_changes(app):
|
||||
temp_super_user = uuid4().hex
|
||||
original_super_user = settings.super_user
|
||||
original_first_install = settings.first_install
|
||||
original_first_install_token = settings.first_install_token
|
||||
original_first_install_token_confirmed = settings.first_install_token_confirmed
|
||||
original_super_user_row = await get_settings_field("super_user")
|
||||
original_confirmed_row = await get_settings_field("first_install_token_confirmed")
|
||||
super_user_file = Path(settings.lnbits_data_folder) / ".super_user"
|
||||
|
||||
await create_account(
|
||||
Account(id=temp_super_user, extra=UserExtra(provider="lnbits"))
|
||||
)
|
||||
|
||||
try:
|
||||
await set_settings_field("super_user", temp_super_user)
|
||||
await set_settings_field("first_install_token_confirmed", "old-token")
|
||||
|
||||
settings.lnbits_admin_ui = True
|
||||
settings.super_user = temp_super_user
|
||||
settings.first_install = False
|
||||
settings.first_install_token = "new-token"
|
||||
settings.first_install_token_confirmed = "old-token"
|
||||
|
||||
await check_admin_settings()
|
||||
|
||||
super_user_row = await get_settings_field("super_user")
|
||||
assert super_user_row is not None
|
||||
assert super_user_row.value
|
||||
assert settings.first_install is True
|
||||
finally:
|
||||
await _restore_setting_field("super_user", original_super_user_row)
|
||||
await _restore_setting_field(
|
||||
"first_install_token_confirmed", original_confirmed_row
|
||||
)
|
||||
settings.super_user = original_super_user
|
||||
settings.first_install = original_first_install
|
||||
settings.first_install_token = original_first_install_token
|
||||
settings.first_install_token_confirmed = original_first_install_token_confirmed
|
||||
super_user_file.write_text(original_super_user)
|
||||
await delete_account(temp_super_user)
|
||||
@@ -49,6 +49,14 @@
|
||||
"phoenixd_api_password": "f171ba022a764e679eef950b21fb1c04f171ba022a764e679eef950b21fb1c04",
|
||||
"user_agent": "LNbits/Tests"
|
||||
}
|
||||
},
|
||||
"sparkl2": {
|
||||
"wallet_class": "SparkL2Wallet",
|
||||
"settings": {
|
||||
"spark_l2_external_endpoint": "http://127.0.0.1:8555",
|
||||
"spark_l2_external_api_key": "mock-spark-l2-api-key",
|
||||
"user_agent": "LNbits/Tests"
|
||||
}
|
||||
}
|
||||
},
|
||||
"functions": {
|
||||
@@ -115,6 +123,16 @@
|
||||
},
|
||||
"method": "GET"
|
||||
}
|
||||
},
|
||||
"sparkl2": {
|
||||
"status_endpoint": {
|
||||
"uri": "/v1/balance",
|
||||
"headers": {
|
||||
"X-Api-Key": "mock-spark-l2-api-key",
|
||||
"User-Agent": "LNbits/Tests"
|
||||
},
|
||||
"method": "POST"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
@@ -190,6 +208,16 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"status_endpoint": [
|
||||
{
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"balance_sats": 55
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -255,6 +283,16 @@
|
||||
"response": "test-error"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"status_endpoint": [
|
||||
{
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"error": "\"test-error\""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -313,6 +351,14 @@
|
||||
"response": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"status_endpoint": [
|
||||
{
|
||||
"response_type": "json",
|
||||
"response": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -371,6 +417,14 @@
|
||||
"response": "data-not-json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"status_endpoint": [
|
||||
{
|
||||
"response_type": "data",
|
||||
"response": "data-not-json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -447,6 +501,17 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"status_endpoint": [
|
||||
{
|
||||
"response_type": "response",
|
||||
"response": {
|
||||
"response": "Not Found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -523,6 +588,16 @@
|
||||
},
|
||||
"method": "POST"
|
||||
}
|
||||
},
|
||||
"sparkl2": {
|
||||
"create_invoice_endpoint": {
|
||||
"uri": "/v1/invoices",
|
||||
"headers": {
|
||||
"X-Api-Key": "mock-spark-l2-api-key",
|
||||
"User-Agent": "LNbits/Tests"
|
||||
},
|
||||
"method": "POST"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
@@ -638,6 +713,24 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"create_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"amount_sats": 555,
|
||||
"memo": "Test Invoice",
|
||||
"description_hash": null,
|
||||
"expiry_seconds": null
|
||||
},
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"checking_id": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96",
|
||||
"payment_request": "lnbc5550n1pnq9jg3sp52rvwstvjcypjsaenzdh0h30jazvzsf8aaye0julprtth9kysxtuspp5e5s3z7felv4t9zrcc6wpn7ehvjl5yzewanzl5crljdl3jgeffyhqdq2f38xy6t5wvxqzjccqpjrzjq0yzeq76ney45hmjlnlpvu0nakzy2g35hqh0dujq8ujdpr2e42pf2rrs6vqpgcsqqqqqqqqqqqqqqeqqyg9qxpqysgqwftcx89k5pp28435pgxfl2vx3ksemzxccppw2j9yjn0ngr6ed7wj8ztc0d5kmt2mvzdlcgrludhz7jncd5l5l9w820hc4clpwhtqj3gq62g66n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -735,6 +828,23 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"create_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"amount_sats": 555,
|
||||
"memo": "Test Invoice",
|
||||
"description_hash": null,
|
||||
"expiry_seconds": null
|
||||
},
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"error": "Test Error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -845,6 +955,21 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"create_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"amount_sats": 555,
|
||||
"memo": "Test Invoice",
|
||||
"description_hash": null,
|
||||
"expiry_seconds": null
|
||||
},
|
||||
"response_type": "json",
|
||||
"response": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -942,6 +1067,21 @@
|
||||
"response": "data-not-json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"create_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"amount_sats": 555,
|
||||
"memo": "Test Invoice",
|
||||
"description_hash": null,
|
||||
"expiry_seconds": null
|
||||
},
|
||||
"response_type": "data",
|
||||
"response": "data-not-json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1057,6 +1197,24 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"create_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"amount_sats": 555,
|
||||
"memo": "Test Invoice",
|
||||
"description_hash": null,
|
||||
"expiry_seconds": null
|
||||
},
|
||||
"response_type": "response",
|
||||
"response": {
|
||||
"response": "Not Found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1155,6 +1313,16 @@
|
||||
},
|
||||
"method": "POST"
|
||||
}
|
||||
},
|
||||
"sparkl2": {
|
||||
"pay_invoice_endpoint": {
|
||||
"uri": "/v1/payments",
|
||||
"headers": {
|
||||
"X-Api-Key": "mock-spark-l2-api-key",
|
||||
"User-Agent": "LNbits/Tests"
|
||||
},
|
||||
"method": "POST"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
@@ -1309,6 +1477,24 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"pay_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"bolt11": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu",
|
||||
"max_fee_sats": 25,
|
||||
"payment_hash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96"
|
||||
},
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"status": "LIGHTNING_PAYMENT_SUCCEEDED",
|
||||
"fee_msat": 30000,
|
||||
"preimage": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1361,7 +1547,23 @@
|
||||
"alby": {},
|
||||
"eclair": [],
|
||||
"lnbits": [],
|
||||
"phoenixd": []
|
||||
"phoenixd": [],
|
||||
"sparkl2": {
|
||||
"pay_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"bolt11": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu",
|
||||
"max_fee_sats": 25,
|
||||
"payment_hash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96"
|
||||
},
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"status": "LIGHTNING_PAYMENT_FAILED"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1505,7 +1707,24 @@
|
||||
}
|
||||
],
|
||||
"lnbits": [],
|
||||
"phoenixd": []
|
||||
"phoenixd": [],
|
||||
"sparkl2": {
|
||||
"pay_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"bolt11": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu",
|
||||
"max_fee_sats": 25,
|
||||
"payment_hash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96"
|
||||
},
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"status": "PAYMENT_PENDING",
|
||||
"preimage": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1598,6 +1817,22 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"pay_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"bolt11": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu",
|
||||
"max_fee_sats": 25,
|
||||
"payment_hash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96"
|
||||
},
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"error": "Test Error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1695,6 +1930,20 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"pay_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"bolt11": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu",
|
||||
"max_fee_sats": 25,
|
||||
"payment_hash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96"
|
||||
},
|
||||
"response_type": "json",
|
||||
"response": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1813,6 +2062,20 @@
|
||||
"response": "data-not-json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"pay_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"bolt11": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu",
|
||||
"max_fee_sats": 25,
|
||||
"payment_hash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96"
|
||||
},
|
||||
"response_type": "data",
|
||||
"response": "data-not-json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1943,6 +2206,23 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"pay_invoice_endpoint": [
|
||||
{
|
||||
"request_type": "json",
|
||||
"request_body": {
|
||||
"bolt11": "lnbc210n1pjlgal5sp5xr3uwlfm7ltumdjyukhys0z2rw6grgm8me9k4w9vn05zt9svzzjspp5ud2jdfpaqn5c2k2vphatsjypfafyk8rcvkvwexnrhmwm94ex4jtqdqu24hxjapq23jhxapqf9h8vmmfvdjscqpjrzjqta942048v7qxh5x7pxwplhmtwfl0f25cq23jh87rhx7lgrwwvv86r90guqqnwgqqqqqqqqqqqqqqpsqyg9qxpqysgqylngsyg960lltngzy90e8n22v4j2hvjs4l4ttuy79qqefjv8q87q9ft7uhwdjakvnsgk44qyhalv6ust54x98whl3q635hkwgsyw8xgqjl7jwu",
|
||||
"max_fee_sats": 25,
|
||||
"payment_hash": "e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96"
|
||||
},
|
||||
"response_type": "response",
|
||||
"response": {
|
||||
"response": "Not Found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2030,6 +2310,16 @@
|
||||
},
|
||||
"method": "GET"
|
||||
}
|
||||
},
|
||||
"sparkl2": {
|
||||
"get_invoice_status_endpoint": {
|
||||
"uri": "/v1/invoices/e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96",
|
||||
"headers": {
|
||||
"X-Api-Key": "mock-spark-l2-api-key",
|
||||
"User-Agent": "LNbits/Tests"
|
||||
},
|
||||
"method": "GET"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
@@ -2125,6 +2415,24 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"get_invoice_status_endpoint": [
|
||||
{
|
||||
"description": "LIGHTNING_PAYMENT_RECEIVED",
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"status": "LIGHTNING_PAYMENT_RECEIVED"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "TRANSFER_COMPLETED",
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"status": "TRANSFER_COMPLETED"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2193,7 +2501,8 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sparkl2": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2400,6 +2709,35 @@
|
||||
"response": "data-not-json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"get_invoice_status_endpoint": [
|
||||
{
|
||||
"description": "no data",
|
||||
"response_type": "json",
|
||||
"response": {}
|
||||
},
|
||||
{
|
||||
"description": "pending status",
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"status": "PAYMENT_PENDING"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "bad json",
|
||||
"response_type": "data",
|
||||
"response": "data-not-json"
|
||||
},
|
||||
{
|
||||
"description": "http 404",
|
||||
"response_type": "response",
|
||||
"response": {
|
||||
"response": "Not Found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2482,6 +2820,16 @@
|
||||
},
|
||||
"method": "GET"
|
||||
}
|
||||
},
|
||||
"sparkl2": {
|
||||
"get_payment_status_endpoint": {
|
||||
"uri": "/v1/payments/e35526a43d04e985594c0dfab848814f524b1c786598ec9a63beddb2d726ac96",
|
||||
"headers": {
|
||||
"X-Api-Key": "mock-spark-l2-api-key",
|
||||
"User-Agent": "LNbits/Tests"
|
||||
},
|
||||
"method": "GET"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
@@ -2587,6 +2935,28 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"get_payment_status_endpoint": [
|
||||
{
|
||||
"description": "LIGHTNING_PAYMENT_SUCCEEDED",
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"status": "LIGHTNING_PAYMENT_SUCCEEDED",
|
||||
"fee_msat": 1000,
|
||||
"preimage": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "TRANSFER_COMPLETED",
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"status": "TRANSFER_COMPLETED",
|
||||
"fee_msat": 1000,
|
||||
"preimage": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2675,7 +3045,8 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sparkl2": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2933,6 +3304,35 @@
|
||||
"response": "data-not-json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sparkl2": {
|
||||
"get_payment_status_endpoint": [
|
||||
{
|
||||
"description": "pending status",
|
||||
"response_type": "json",
|
||||
"response": {
|
||||
"status": "PAYMENT_PENDING"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "no data",
|
||||
"response_type": "json",
|
||||
"response": {}
|
||||
},
|
||||
{
|
||||
"description": "bad json",
|
||||
"response_type": "data",
|
||||
"response": "data-not-json"
|
||||
},
|
||||
{
|
||||
"description": "http 404",
|
||||
"response_type": "response",
|
||||
"response": {
|
||||
"response": "Not Found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1263,7 +1263,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "lnbits"
|
||||
version = "1.5.2"
|
||||
version = "1.5.3"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiosqlite" },
|
||||
@@ -2312,7 +2312,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.5"
|
||||
version = "2.33.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
@@ -2320,9 +2320,9 @@ dependencies = [
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user