Compare commits
16
Commits
v1.3.0-rc2
...
v1.3.0-rc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15984fa49b | ||
|
|
67dc096909 | ||
|
|
2daddf8b74 | ||
|
|
ae6cf47244 | ||
|
|
1488f580ff | ||
|
|
609c949394 | ||
|
|
c24d2f24cf | ||
|
|
0ba81ec719 | ||
|
|
5e03707be5 | ||
|
|
50f1111356 | ||
|
|
112a0f9698 | ||
|
|
75016d1a27 | ||
|
|
5b9f244a9f | ||
|
|
0d2ff2e663 | ||
|
|
bdfefc2506 | ||
|
|
2a9f471783 |
@@ -33,7 +33,7 @@ async def create_wallet(
|
||||
async def update_wallet(
|
||||
wallet: Wallet,
|
||||
conn: Optional[Connection] = None,
|
||||
) -> Optional[Wallet]:
|
||||
) -> Wallet:
|
||||
wallet.updated_at = datetime.now(timezone.utc)
|
||||
await (conn or db).update("wallets", wallet)
|
||||
return wallet
|
||||
|
||||
@@ -735,3 +735,11 @@ async def m032_add_external_id_to_accounts(db: Connection):
|
||||
|
||||
async def m033_update_payment_table(db: Connection):
|
||||
await db.execute("ALTER TABLE apipayments ADD COLUMN fiat_provider TEXT")
|
||||
|
||||
|
||||
async def m034_add_stored_paylinks_to_wallet(db: Connection):
|
||||
await db.execute(
|
||||
"""
|
||||
ALTER TABLE wallets ADD COLUMN stored_paylinks TEXT
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from time import time
|
||||
from typing import Optional
|
||||
|
||||
from lnurl import Lnurl, LnurlPayResponse
|
||||
from pydantic import BaseModel
|
||||
from lnurl import LnAddress, Lnurl, LnurlPayResponse
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class CreateLnurlPayment(BaseModel):
|
||||
res: LnurlPayResponse
|
||||
res: LnurlPayResponse | None = None
|
||||
lnurl: Lnurl | LnAddress | None = None
|
||||
amount: int
|
||||
comment: Optional[str] = None
|
||||
unit: Optional[str] = None
|
||||
@@ -14,3 +16,17 @@ class CreateLnurlPayment(BaseModel):
|
||||
|
||||
class CreateLnurlWithdraw(BaseModel):
|
||||
lnurl_w: Lnurl
|
||||
|
||||
|
||||
class LnurlScan(BaseModel):
|
||||
lnurl: Lnurl | LnAddress
|
||||
|
||||
|
||||
class StoredPayLink(BaseModel):
|
||||
lnurl: str
|
||||
label: str
|
||||
last_used: int = Field(default_factory=lambda: int(time()))
|
||||
|
||||
|
||||
class StoredPayLinks(BaseModel):
|
||||
links: list[StoredPayLink] = []
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
|
||||
from ecdsa import SECP256k1, SigningKey
|
||||
from lnurl import encode as lnurl_encode
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from lnbits.core.models.lnurl import StoredPayLinks
|
||||
from lnbits.db import FilterModel
|
||||
from lnbits.helpers import url_for
|
||||
from lnbits.settings import settings
|
||||
@@ -41,6 +39,7 @@ class Wallet(BaseModel):
|
||||
currency: str | None = None
|
||||
balance_msat: int = Field(default=0, no_database=True)
|
||||
extra: WalletExtra = WalletExtra()
|
||||
stored_paylinks: StoredPayLinks = StoredPayLinks()
|
||||
|
||||
@property
|
||||
def balance(self) -> int:
|
||||
@@ -58,14 +57,6 @@ class Wallet(BaseModel):
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def lnurlauth_key(self, domain: str) -> SigningKey:
|
||||
hashing_key = hashlib.sha256(self.id.encode()).digest()
|
||||
linking_key = hmac.digest(hashing_key, domain.encode(), "sha256")
|
||||
|
||||
return SigningKey.from_string(
|
||||
linking_key, curve=SECP256k1, hashfunc=hashlib.sha256
|
||||
)
|
||||
|
||||
|
||||
class CreateWallet(BaseModel):
|
||||
name: str | None = None
|
||||
|
||||
@@ -1,41 +1,66 @@
|
||||
from time import time
|
||||
|
||||
from lnurl import (
|
||||
LnAddress,
|
||||
Lnurl,
|
||||
LnurlErrorResponse,
|
||||
LnurlPayActionResponse,
|
||||
LnurlPayResponse,
|
||||
LnurlResponseException,
|
||||
execute_pay_request,
|
||||
handle,
|
||||
)
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.core.models import CreateLnurlPayment
|
||||
from lnbits.core.crud import update_wallet
|
||||
from lnbits.core.models import CreateLnurlPayment, Wallet
|
||||
from lnbits.core.models.lnurl import StoredPayLink
|
||||
from lnbits.settings import settings
|
||||
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
|
||||
|
||||
|
||||
async def get_pr_from_lnurl(lnurl: str, amount_msat: int) -> str:
|
||||
async def get_pr_from_lnurl(
|
||||
lnurl: str, amount_msat: int, comment: str | None = None
|
||||
) -> str:
|
||||
res = await handle(lnurl, user_agent=settings.user_agent, timeout=10)
|
||||
if isinstance(res, LnurlErrorResponse):
|
||||
raise LnurlResponseException(res.reason)
|
||||
if not isinstance(res, LnurlPayResponse):
|
||||
raise LnurlResponseException(
|
||||
"Invalid LNURL response. Expected LnurlPayResponse."
|
||||
)
|
||||
res2 = await execute_pay_request(
|
||||
res,
|
||||
msat=str(amount_msat),
|
||||
msat=amount_msat,
|
||||
comment=comment,
|
||||
user_agent=settings.user_agent,
|
||||
timeout=10,
|
||||
)
|
||||
if not isinstance(res, LnurlPayActionResponse):
|
||||
raise LnurlResponseException(
|
||||
"Invalid LNURL pay response. Expected LnurlPayActionResponse."
|
||||
)
|
||||
if isinstance(res2, LnurlErrorResponse):
|
||||
raise LnurlResponseException(res2.reason)
|
||||
return res2.pr
|
||||
|
||||
|
||||
async def fetch_lnurl_pay_request(data: CreateLnurlPayment) -> LnurlPayActionResponse:
|
||||
async def fetch_lnurl_pay_request(
|
||||
data: CreateLnurlPayment, wallet: Wallet | None = None
|
||||
) -> tuple[LnurlPayResponse, LnurlPayActionResponse]:
|
||||
"""
|
||||
Pay an LNURL payment request.
|
||||
optional `wallet` is used to store the pay link in the wallet's stored links.
|
||||
|
||||
raises `LnurlResponseException` if pay request fails
|
||||
"""
|
||||
if not data.res and data.lnurl:
|
||||
res = await handle(data.lnurl, user_agent=settings.user_agent, timeout=5)
|
||||
if isinstance(res, LnurlErrorResponse):
|
||||
raise LnurlResponseException(res.reason)
|
||||
if not isinstance(res, LnurlPayResponse):
|
||||
raise LnurlResponseException(
|
||||
"Invalid LNURL response. Expected LnurlPayResponse."
|
||||
)
|
||||
data.res = res
|
||||
if not data.res:
|
||||
raise LnurlResponseException("No LNURL pay request provided.")
|
||||
|
||||
if data.unit and data.unit != "sat":
|
||||
# shift to float with 2 decimal places
|
||||
@@ -45,9 +70,69 @@ async def fetch_lnurl_pay_request(data: CreateLnurlPayment) -> LnurlPayActionRes
|
||||
else:
|
||||
amount_msat = data.amount
|
||||
|
||||
return await execute_pay_request(
|
||||
res2 = await execute_pay_request(
|
||||
data.res,
|
||||
msat=str(amount_msat),
|
||||
msat=amount_msat,
|
||||
comment=data.comment,
|
||||
user_agent=settings.user_agent,
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
if wallet:
|
||||
await store_paylink(data.res, res2, wallet, data.lnurl)
|
||||
|
||||
return data.res, res2
|
||||
|
||||
|
||||
async def store_paylink(
|
||||
res: LnurlPayResponse,
|
||||
res2: LnurlPayActionResponse,
|
||||
wallet: Wallet,
|
||||
lnurl: LnAddress | Lnurl | None = None,
|
||||
) -> None:
|
||||
|
||||
if res2.disposable is not False:
|
||||
return # do not store disposable LNURL pay links
|
||||
|
||||
logger.debug(f"storing lnurl pay link for wallet {wallet.id}. ")
|
||||
|
||||
stored_paylink = None
|
||||
# If we have only a LnurlPayResponse, we can use its lnaddress
|
||||
# because the lnurl is not available.
|
||||
if not lnurl:
|
||||
for _data in res.metadata.list():
|
||||
if _data[0] == "text/identifier":
|
||||
stored_paylink = StoredPayLink(
|
||||
lnurl=LnAddress(_data[1]), label=res.metadata.text
|
||||
)
|
||||
if not stored_paylink:
|
||||
logger.warning(
|
||||
"No lnaddress found in metadata for LNURL pay link. "
|
||||
"Skipping storage."
|
||||
)
|
||||
return # skip if lnaddress not found in metadata
|
||||
else:
|
||||
if isinstance(lnurl, Lnurl):
|
||||
_lnurl = str(lnurl.lud17 or lnurl.bech32)
|
||||
else:
|
||||
_lnurl = str(lnurl)
|
||||
stored_paylink = StoredPayLink(lnurl=_lnurl, label=res.metadata.text)
|
||||
|
||||
# update last_used if its already stored
|
||||
for pl in wallet.stored_paylinks.links:
|
||||
if pl.lnurl == stored_paylink.lnurl:
|
||||
pl.last_used = int(time())
|
||||
await update_wallet(wallet)
|
||||
logger.debug(
|
||||
"Updated last used time for LNURL "
|
||||
f"pay link {stored_paylink.lnurl} in wallet {wallet.id}."
|
||||
)
|
||||
return
|
||||
|
||||
# if not already stored, append it
|
||||
if not any(stored_paylink.lnurl == pl.lnurl for pl in wallet.stored_paylinks.links):
|
||||
wallet.stored_paylinks.links.append(stored_paylink)
|
||||
await update_wallet(wallet)
|
||||
logger.debug(
|
||||
f"Stored LNURL pay link {stored_paylink.lnurl} for wallet {wallet.id}."
|
||||
)
|
||||
|
||||
@@ -289,9 +289,87 @@
|
||||
</q-expansion-item>
|
||||
<q-separator></q-separator>
|
||||
{% endif %}
|
||||
|
||||
<q-expansion-item
|
||||
v-if="'{{ LNBITS_DENOMINATION }}' == 'sats'"
|
||||
group="extras"
|
||||
icon="qr_code"
|
||||
v-if="stored_paylinks.length > 0"
|
||||
:label="$t('stored_paylinks')"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row flex" v-for="paylink in stored_paylinks">
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
icon="send"
|
||||
size="xs"
|
||||
@click="sendToPaylink(paylink.lnurl)"
|
||||
>
|
||||
<q-tooltip>
|
||||
<span v-text="`send to: ${paylink.lnurl}`"></span>
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
color="secondary"
|
||||
icon="content_copy"
|
||||
size="xs"
|
||||
@click="copyText(paylink.lnurl)"
|
||||
>
|
||||
<q-tooltip>
|
||||
<span v-text="`copy: ${paylink.lnurl}`"></span>
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
<span
|
||||
v-text="paylink.label"
|
||||
class="q-mr-xs q-ml-xs"
|
||||
></span>
|
||||
<q-btn dense flat color="primary" icon="edit" size="xs">
|
||||
<q-popup-edit
|
||||
@update:model-value="editPaylink()"
|
||||
v-model="paylink.label"
|
||||
v-slot="scope"
|
||||
>
|
||||
<q-input
|
||||
dark
|
||||
color="white"
|
||||
v-model="scope.value"
|
||||
dense
|
||||
autofocus
|
||||
counter
|
||||
@keyup.enter="scope.set"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon name="edit" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-popup-edit>
|
||||
<q-tooltip>
|
||||
<span v-text="$t('edit')"></span>
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
<span style="flex-grow: 1"></span>
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
color="red"
|
||||
icon="delete"
|
||||
size="xs"
|
||||
@click="deletePaylink(paylink.lnurl)"
|
||||
>
|
||||
<q-tooltip>
|
||||
<span v-text="$t('delete')"></span>
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
<span v-text="dateFromNow(paylink.last_used)"></span>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
<q-separator></q-separator>
|
||||
<q-expansion-item
|
||||
group="extras"
|
||||
icon="phone_android"
|
||||
:label="$t('access_wallet_on_mobile')"
|
||||
|
||||
@@ -147,7 +147,7 @@ async def api_download_backup() -> FileResponse:
|
||||
pg_backup_filename = f"{settings.lnbits_data_folder}/lnbits-database.dmp"
|
||||
is_pg = db_url and db_url.startswith("postgres://")
|
||||
|
||||
if is_pg:
|
||||
if is_pg and db_url:
|
||||
p = urlparse(db_url)
|
||||
command = (
|
||||
f"pg_dump --host={p.hostname} "
|
||||
|
||||
@@ -231,7 +231,7 @@ async def api_disable_extension(
|
||||
return SimpleStatus(
|
||||
success=True, message=f"Extension '{ext_id}' already disabled."
|
||||
)
|
||||
logger.info(f"Disabeling extension: {ext_id}.")
|
||||
logger.info(f"Disabling extension: {ext_id}.")
|
||||
user_ext.active = False
|
||||
await update_user_extension(user_ext)
|
||||
return SimpleStatus(success=True, message=f"Extension '{ext_id}' disabled.")
|
||||
|
||||
@@ -7,7 +7,7 @@ from fastapi import Cookie, Depends, Query, Request
|
||||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
|
||||
from fastapi.routing import APIRouter
|
||||
from lnurl import decode as lnurl_decode
|
||||
from lnurl import url_decode
|
||||
from pydantic.types import UUID4
|
||||
|
||||
from lnbits.core.helpers import to_valid_user_id
|
||||
@@ -456,7 +456,7 @@ async def lnurlwallet(request: Request, lightning: str = ""):
|
||||
if not settings.lnbits_allow_new_accounts:
|
||||
return {"status": "ERROR", "reason": "New accounts are not allowed."}
|
||||
|
||||
lnurl = lnurl_decode(lightning)
|
||||
lnurl = url_decode(lightning)
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
check_callback_url(lnurl)
|
||||
|
||||
@@ -23,7 +23,7 @@ from lnurl.models import (
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.core.models import CreateLnurlWithdraw, Payment
|
||||
from lnbits.core.models.lnurl import CreateLnurlPayment
|
||||
from lnbits.core.models.lnurl import CreateLnurlPayment, LnurlScan
|
||||
from lnbits.decorators import (
|
||||
WalletTypeInfo,
|
||||
require_admin_key,
|
||||
@@ -37,27 +37,46 @@ from ..services import fetch_lnurl_pay_request, pay_invoice
|
||||
lnurl_router = APIRouter(tags=["LNURL"])
|
||||
|
||||
|
||||
async def _handle(lnurl: str) -> LnurlResponseModel:
|
||||
try:
|
||||
res = await lnurl_handle(lnurl, user_agent=settings.user_agent, timeout=5)
|
||||
if isinstance(res, LnurlErrorResponse):
|
||||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=res.reason)
|
||||
except LnurlResponseException as exc:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail=str(exc)
|
||||
) from exc
|
||||
return res
|
||||
|
||||
|
||||
@lnurl_router.get(
|
||||
"/api/v1/lnurlscan/{code}",
|
||||
dependencies=[Depends(require_invoice_key)],
|
||||
deprecated=True,
|
||||
response_model=LnurlPayResponse
|
||||
| LnurlWithdrawResponse
|
||||
| LnurlAuthResponse
|
||||
| LnurlErrorResponse,
|
||||
)
|
||||
async def api_lnurlscan(code: str) -> LnurlResponseModel:
|
||||
try:
|
||||
res = await lnurl_handle(code, user_agent=settings.user_agent, timeout=5)
|
||||
except LnurlResponseException as exc:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail=str(exc)
|
||||
) from exc
|
||||
|
||||
res = await _handle(code)
|
||||
if isinstance(res, (LnurlPayResponse, LnurlWithdrawResponse, LnurlAuthResponse)):
|
||||
check_callback_url(res.callback)
|
||||
return res
|
||||
|
||||
|
||||
@lnurl_router.post(
|
||||
"/api/v1/lnurlscan",
|
||||
dependencies=[Depends(require_invoice_key)],
|
||||
response_model=LnurlPayResponse
|
||||
| LnurlWithdrawResponse
|
||||
| LnurlAuthResponse
|
||||
| LnurlErrorResponse,
|
||||
)
|
||||
async def api_lnurlscan_post(scan: LnurlScan) -> LnurlResponseModel:
|
||||
return await _handle(scan.lnurl)
|
||||
|
||||
|
||||
@lnurl_router.post("/api/v1/lnurlauth")
|
||||
async def api_perform_lnurlauth(
|
||||
data: LnurlAuthResponse, key_type: WalletTypeInfo = Depends(require_admin_key)
|
||||
@@ -82,16 +101,29 @@ async def api_perform_lnurlauth(
|
||||
async def api_payments_pay_lnurl(
|
||||
data: CreateLnurlPayment, wallet: WalletTypeInfo = Depends(require_admin_key)
|
||||
) -> Payment:
|
||||
"""
|
||||
Pay an LNURL payment request.
|
||||
Either provice `res` (LnurlPayResponse) or `lnurl` (str) in the `data` object.
|
||||
"""
|
||||
if not data.res and not data.lnurl:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail="Missing LNURL or LnurlPayResponse data.",
|
||||
)
|
||||
|
||||
try:
|
||||
res = await fetch_lnurl_pay_request(data=data)
|
||||
res, res2 = await fetch_lnurl_pay_request(data=data, wallet=wallet.wallet)
|
||||
except LnurlResponseException as exc:
|
||||
logger.warning(exc)
|
||||
msg = f"Failed to connect to {data.res.callback}."
|
||||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=msg) from exc
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail=str(exc)
|
||||
) from exc
|
||||
|
||||
extra: dict[str, Any] = {}
|
||||
if res.success_action:
|
||||
extra["success_action"] = res.success_action.json()
|
||||
if res2.disposable is False:
|
||||
extra["stored"] = True
|
||||
if res2.success_action:
|
||||
extra["success_action"] = res2.success_action.json()
|
||||
if data.comment:
|
||||
extra["comment"] = data.comment
|
||||
if data.unit and data.unit != "sat":
|
||||
@@ -100,8 +132,8 @@ async def api_payments_pay_lnurl(
|
||||
|
||||
payment = await pay_invoice(
|
||||
wallet_id=wallet.wallet.id,
|
||||
payment_request=str(res.pr),
|
||||
description=data.res.metadata.text,
|
||||
payment_request=str(res2.pr),
|
||||
description=res.metadata.text,
|
||||
extra=extra,
|
||||
)
|
||||
|
||||
@@ -115,12 +147,12 @@ async def api_payment_pay_with_nfc(
|
||||
payment_request: str,
|
||||
lnurl_data: CreateLnurlWithdraw,
|
||||
) -> LnurlErrorResponse | LnurlSuccessResponse:
|
||||
if not lnurl_data.lnurl_w.lud17:
|
||||
return LnurlErrorResponse(reason="LNURL-withdraw lud17 not provided.")
|
||||
try:
|
||||
res = await lnurl_handle(
|
||||
lnurl_data.lnurl_w.callback_url, user_agent=settings.user_agent, timeout=10
|
||||
)
|
||||
url = lnurl_data.lnurl_w.lud17
|
||||
res = await lnurl_handle(url, user_agent=settings.user_agent, timeout=10)
|
||||
except (LnurlResponseException, Exception) as exc:
|
||||
logger.warning(exc)
|
||||
return LnurlErrorResponse(reason=str(exc))
|
||||
|
||||
if not isinstance(res, LnurlWithdrawResponse):
|
||||
|
||||
@@ -10,7 +10,7 @@ from fastapi import (
|
||||
Query,
|
||||
)
|
||||
from fastapi.responses import JSONResponse
|
||||
from lnurl import decode as lnurl_decode
|
||||
from lnurl import url_decode
|
||||
|
||||
from lnbits import bolt11
|
||||
from lnbits.core.crud.payments import (
|
||||
@@ -318,7 +318,7 @@ async def api_payments_decode(data: DecodePayment) -> JSONResponse:
|
||||
payment_str = data.data
|
||||
try:
|
||||
if payment_str[:5] == "LNURL":
|
||||
url = str(lnurl_decode(payment_str))
|
||||
url = str(url_decode(payment_str))
|
||||
return JSONResponse({"domain": url})
|
||||
else:
|
||||
invoice = bolt11.decode(payment_str)
|
||||
|
||||
@@ -10,11 +10,11 @@ from fastapi import (
|
||||
)
|
||||
|
||||
from lnbits.core.crud.wallets import get_wallets_paginated
|
||||
from lnbits.core.models import CreateWallet, KeyType, User, Wallet
|
||||
from lnbits.core.models import CreateWallet, KeyType, User, Wallet, WalletTypeInfo
|
||||
from lnbits.core.models.lnurl import StoredPayLink, StoredPayLinks
|
||||
from lnbits.core.models.wallets import WalletsFilters
|
||||
from lnbits.db import Filters, Page
|
||||
from lnbits.decorators import (
|
||||
WalletTypeInfo,
|
||||
check_user_exists,
|
||||
parse_filters,
|
||||
require_admin_key,
|
||||
@@ -93,6 +93,21 @@ async def api_reset_wallet_keys(
|
||||
return wallet
|
||||
|
||||
|
||||
@wallet_router.put("/stored_paylinks/{wallet_id}")
|
||||
async def api_put_stored_paylinks(
|
||||
wallet_id: str,
|
||||
data: StoredPayLinks,
|
||||
key_info: WalletTypeInfo = Depends(require_admin_key),
|
||||
) -> list[StoredPayLink]:
|
||||
if key_info.wallet.id != wallet_id:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.FORBIDDEN, detail="You cannot modify this wallet"
|
||||
)
|
||||
key_info.wallet.stored_paylinks.links = data.links
|
||||
wallet = await update_wallet(key_info.wallet)
|
||||
return wallet.stored_paylinks.links
|
||||
|
||||
|
||||
@wallet_router.patch("")
|
||||
async def api_update_wallet(
|
||||
name: Optional[str] = Body(None),
|
||||
|
||||
+9
-5
@@ -281,15 +281,19 @@ class Database(Compat):
|
||||
self.schema = self.name
|
||||
self.type = DB_TYPE
|
||||
|
||||
if DB_TYPE == SQLITE:
|
||||
if self.type == POSTGRES and settings.lnbits_database_url:
|
||||
database_uri = settings.lnbits_database_url.replace(
|
||||
"postgres://", "postgresql+asyncpg://"
|
||||
)
|
||||
elif self.type == COCKROACH and settings.lnbits_database_url:
|
||||
database_uri = settings.lnbits_database_url.replace(
|
||||
"cockroachdb://", "cockroachdb+asyncpg://"
|
||||
)
|
||||
else:
|
||||
self.path = os.path.join(
|
||||
settings.lnbits_data_folder, f"{self.name}.sqlite3"
|
||||
)
|
||||
database_uri = f"sqlite+aiosqlite:///{self.path}"
|
||||
else:
|
||||
database_uri = settings.lnbits_database_url.replace(
|
||||
"postgres://", "postgresql+asyncpg://"
|
||||
)
|
||||
|
||||
if self.name.startswith("ext_"):
|
||||
self.schema = self.name[4:]
|
||||
|
||||
+1
-5
@@ -99,11 +99,7 @@ def template_renderer(additional_folders: Optional[list] = None) -> Jinja2Templa
|
||||
"USE_DEFAULT_BGIMAGE": settings.lnbits_default_bgimage,
|
||||
"VOIDWALLET": settings.lnbits_backend_wallet_class == "VoidWallet",
|
||||
"WEBPUSH_PUBKEY": settings.lnbits_webpush_pubkey,
|
||||
"LNBITS_DENOMINATION": (
|
||||
settings.lnbits_denomination
|
||||
if settings.lnbits_denomination == "FakeWallet"
|
||||
else "sats"
|
||||
),
|
||||
"LNBITS_DENOMINATION": settings.lnbits_denomination,
|
||||
"has_holdinvoice": settings.has_holdinvoice,
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -142,14 +142,14 @@ class ExchangeRateProvider(BaseModel):
|
||||
|
||||
class InstalledExtensionsSettings(LNbitsSettings):
|
||||
# installed extensions that have been deactivated
|
||||
lnbits_deactivated_extensions: set[str] = Field(default=[])
|
||||
lnbits_deactivated_extensions: set[str] = Field(default=set())
|
||||
# upgraded extensions that require API redirects
|
||||
lnbits_upgraded_extensions: dict[str, str] = Field(default={})
|
||||
# list of redirects that extensions want to perform
|
||||
lnbits_extensions_redirects: list[RedirectPath] = Field(default=[])
|
||||
|
||||
# list of all extension ids
|
||||
lnbits_installed_extensions_ids: set[str] = Field(default=[])
|
||||
lnbits_installed_extensions_ids: set[str] = Field(default=set())
|
||||
|
||||
def find_extension_redirect(
|
||||
self, path: str, req_headers: list[tuple[bytes, bytes]]
|
||||
@@ -266,7 +266,7 @@ class ThemesSettings(LNbitsSettings):
|
||||
lnbits_default_theme: str = Field(default="salvador")
|
||||
lnbits_default_border: str = Field(default="hard-border")
|
||||
lnbits_default_gradient: bool = Field(default=True)
|
||||
lnbits_default_bgimage: str = Field(default=None)
|
||||
lnbits_default_bgimage: str | None = Field(default=None)
|
||||
|
||||
|
||||
class OpsSettings(LNbitsSettings):
|
||||
@@ -729,7 +729,7 @@ class AuthMethods(Enum):
|
||||
|
||||
class AuthSettings(LNbitsSettings):
|
||||
auth_token_expire_minutes: int = Field(default=525600, gt=0)
|
||||
auth_all_methods = [a.value for a in AuthMethods]
|
||||
auth_all_methods: list[str] = [a.value for a in AuthMethods]
|
||||
auth_allowed_methods: list[str] = Field(
|
||||
default=[
|
||||
AuthMethods.user_id_only.value,
|
||||
@@ -947,7 +947,7 @@ class EnvSettings(LNbitsSettings):
|
||||
|
||||
class PersistenceSettings(LNbitsSettings):
|
||||
lnbits_data_folder: str = Field(default="./data")
|
||||
lnbits_database_url: str = Field(default=None)
|
||||
lnbits_database_url: str | None = Field(default=None)
|
||||
|
||||
|
||||
class SuperUserSettings(LNbitsSettings):
|
||||
@@ -993,11 +993,11 @@ class TransientSettings(InstalledExtensionsSettings, ExchangeHistorySettings):
|
||||
lnbits_running: bool = Field(default=True)
|
||||
|
||||
# Remember the latest balance delta in order to compare with the current one
|
||||
latest_balance_delta_sats: int = Field(default=None)
|
||||
latest_balance_delta_sats: int = Field(default=0)
|
||||
|
||||
lnbits_all_extensions_ids: set[str] = Field(default=[])
|
||||
lnbits_all_extensions_ids: set[str] = Field(default=set())
|
||||
|
||||
server_startup_time: int = Field(default=time())
|
||||
server_startup_time: int = Field(default=int(time()))
|
||||
|
||||
has_holdinvoice: bool = Field(default=False)
|
||||
has_nodemanager: bool = Field(default=False)
|
||||
|
||||
+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
@@ -49,6 +49,7 @@ window.localisation.en = {
|
||||
export_to_phone_desc:
|
||||
'This QR code contains your wallet URL with full access. You can scan it from your phone to open your wallet from there.',
|
||||
access_wallet_on_mobile: 'Mobile Access',
|
||||
stored_paylinks: 'Stored LNURL pay links',
|
||||
wallet: 'Wallet: ',
|
||||
wallet_name: 'Wallet name',
|
||||
wallets: 'Wallets',
|
||||
|
||||
@@ -187,34 +187,6 @@ window.app.component('lnbits-lnurlpay-success-action', {
|
||||
}
|
||||
})
|
||||
|
||||
window.app.component('lnbits-qrcode', {
|
||||
mixins: [window.windowMixin],
|
||||
template: '#lnbits-qrcode',
|
||||
components: {
|
||||
QrcodeVue
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
options: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
custom: {
|
||||
margin: 3,
|
||||
width: 350,
|
||||
size: 350,
|
||||
logo: LNBITS_QR_LOGO
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.custom = {...this.custom, ...this.options}
|
||||
}
|
||||
})
|
||||
|
||||
window.app.component('lnbits-notifications-btn', {
|
||||
template: '#lnbits-notifications-btn',
|
||||
mixins: [window.windowMixin],
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
window.app.component('lnbits-qrcode-lnurl', {
|
||||
template: '#lnbits-qrcode-lnurl',
|
||||
mixins: [window.windowMixin],
|
||||
props: {
|
||||
url: {
|
||||
required: true,
|
||||
type: String
|
||||
},
|
||||
prefix: {
|
||||
type: String,
|
||||
default: 'lnurlp'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tab: 'bech32',
|
||||
lnurl: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setLnurl() {
|
||||
if (this.tab == 'bech32') {
|
||||
const bytes = new TextEncoder().encode(this.url)
|
||||
const bech32 = NostrTools.nip19.encodeBytes('lnurl', bytes)
|
||||
this.lnurl = `lightning:${bech32.toUpperCase()}`
|
||||
} else if (this.tab == 'lud17') {
|
||||
this.lnurl = this.url.replace('https://', this.prefix + '://')
|
||||
}
|
||||
this.$emit('update:lnurl', this.lnurl)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url() {
|
||||
this.setLnurl()
|
||||
},
|
||||
tab() {
|
||||
this.setLnurl()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.setLnurl()
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
window.app.component('lnbits-qrcode', {
|
||||
mixins: [window.windowMixin],
|
||||
template: '#lnbits-qrcode',
|
||||
components: {
|
||||
QrcodeVue
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
options: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
custom: {
|
||||
margin: 3,
|
||||
width: 350,
|
||||
size: 350,
|
||||
logo: LNBITS_QR_LOGO
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.custom = {...this.custom, ...this.options}
|
||||
}
|
||||
})
|
||||
+79
-13
@@ -5,6 +5,7 @@ window.WalletPageLogic = {
|
||||
origin: window.location.origin,
|
||||
baseUrl: `${window.location.protocol}//${window.location.host}/`,
|
||||
websocketUrl: `${'http:' ? 'ws://' : 'wss://'}${window.location.host}/api/v1/ws`,
|
||||
stored_paylinks: [],
|
||||
parse: {
|
||||
show: false,
|
||||
invoice: null,
|
||||
@@ -141,6 +142,13 @@ window.WalletPageLogic = {
|
||||
},
|
||||
canPay() {
|
||||
if (!this.parse.invoice) return false
|
||||
if (this.parse.invoice.expired) {
|
||||
Quasar.Notify.create({
|
||||
message: 'Invoice has expired',
|
||||
color: 'negative'
|
||||
})
|
||||
return false
|
||||
}
|
||||
return this.parse.invoice.sat <= this.g.wallet.sat
|
||||
},
|
||||
formattedAmount() {
|
||||
@@ -170,6 +178,10 @@ window.WalletPageLogic = {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
dateFromNow(unix) {
|
||||
const date = new Date(unix * 1000)
|
||||
return moment.utc(date).fromNow()
|
||||
},
|
||||
formatFiatAmount(amount, currency) {
|
||||
this.update.currency = currency
|
||||
this.formattedFiatAmount = LNbits.utils.formatCurrency(
|
||||
@@ -275,7 +287,10 @@ window.WalletPageLogic = {
|
||||
this.readNfcTag()
|
||||
}
|
||||
// WITHDRAW
|
||||
if (response.data.extra.lnurl_response !== null) {
|
||||
if (
|
||||
this.receive.lnurl &&
|
||||
response.data.extra?.lnurl_response !== null
|
||||
) {
|
||||
if (response.data.extra.lnurl_response === false) {
|
||||
response.data.extra.lnurl_response = `Unable to connect`
|
||||
}
|
||||
@@ -342,11 +357,9 @@ window.WalletPageLogic = {
|
||||
},
|
||||
lnurlScan() {
|
||||
LNbits.api
|
||||
.request(
|
||||
'GET',
|
||||
'/api/v1/lnurlscan/' + this.parse.data.request,
|
||||
this.g.wallet.adminkey
|
||||
)
|
||||
.request('POST', '/api/v1/lnurlscan', this.g.wallet.adminkey, {
|
||||
lnurl: this.parse.data.request
|
||||
})
|
||||
.then(response => {
|
||||
const data = response.data
|
||||
if (data.status === 'ERROR') {
|
||||
@@ -394,19 +407,29 @@ window.WalletPageLogic = {
|
||||
this.decodeRequest()
|
||||
this.parse.camera.show = false
|
||||
},
|
||||
isLnurl(req) {
|
||||
return (
|
||||
req.toLowerCase().startsWith('lnurl1') ||
|
||||
req.startsWith('lnurlp://') ||
|
||||
req.startsWith('lnurlw://') ||
|
||||
req.startsWith('lnurlauth://') ||
|
||||
req.match(/[\w.+-~_]+@[\w.+-~_]/)
|
||||
)
|
||||
},
|
||||
decodeRequest() {
|
||||
this.parse.show = true
|
||||
this.parse.data.request = this.parse.data.request.trim().toLowerCase()
|
||||
let req = this.parse.data.request
|
||||
this.parse.data.request = this.parse.data.request.trim()
|
||||
const req = this.parse.data.request.toLowerCase()
|
||||
if (req.startsWith('lightning:')) {
|
||||
this.parse.data.request = req.slice(10)
|
||||
this.parse.data.request = this.parse.data.request.slice(10)
|
||||
} else if (req.startsWith('lnurl:')) {
|
||||
this.parse.data.request = req.slice(6)
|
||||
this.parse.data.request = this.parse.data.request.slice(6)
|
||||
} else if (req.includes('lightning=lnurl1')) {
|
||||
this.parse.data.request = req.split('lightning=')[1].split('&')[0]
|
||||
this.parse.data.request = this.parse.data.request
|
||||
.split('lightning=')[1]
|
||||
.split('&')[0]
|
||||
}
|
||||
req = this.parse.data.request
|
||||
if (req.startsWith('lnurl1') || req.match(/[\w.+-~_]+@[\w.+-~_]/)) {
|
||||
if (this.isLnurl(this.parse.data.request)) {
|
||||
this.lnurlScan()
|
||||
return
|
||||
}
|
||||
@@ -518,6 +541,7 @@ window.WalletPageLogic = {
|
||||
LNbits.api
|
||||
.request('post', '/api/v1/payments/lnurl', this.g.wallet.adminkey, {
|
||||
res: this.parse.lnurlpay,
|
||||
lnurl: this.parse.data.request,
|
||||
unit: this.parse.data.unit,
|
||||
amount: this.parse.data.amount * 1000,
|
||||
comment: this.parse.data.comment,
|
||||
@@ -1080,9 +1104,51 @@ window.WalletPageLogic = {
|
||||
saveChartsPreferences() {
|
||||
this.$q.localStorage.set('lnbits.wallets.chartConfig', this.chartConfig)
|
||||
this.refreshCharts()
|
||||
},
|
||||
updatePaylinks() {
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
`/api/v1/wallet/stored_paylinks/${this.g.wallet.id}`,
|
||||
this.g.wallet.adminkey,
|
||||
{
|
||||
links: this.stored_paylinks
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
Quasar.Notify.create({
|
||||
message: 'Paylinks updated.',
|
||||
type: 'positive',
|
||||
timeout: 3500
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
LNbits.utils.notifyApiError(err)
|
||||
})
|
||||
},
|
||||
sendToPaylink(lnurl) {
|
||||
this.parse.data.request = lnurl
|
||||
this.parse.show = true
|
||||
this.lnurlScan()
|
||||
},
|
||||
editPaylink() {
|
||||
this.$nextTick(() => {
|
||||
this.updatePaylinks()
|
||||
})
|
||||
},
|
||||
deletePaylink(lnurl) {
|
||||
const links = []
|
||||
this.stored_paylinks.forEach(link => {
|
||||
if (link.lnurl !== lnurl) {
|
||||
links.push(link)
|
||||
}
|
||||
})
|
||||
this.stored_paylinks = links
|
||||
this.updatePaylinks()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.stored_paylinks = wallet.stored_paylinks.links
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
if (urlParams.has('lightning') || urlParams.has('lnurl')) {
|
||||
this.parse.data.request =
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
"js/bolt11-decoder.js"
|
||||
],
|
||||
"components": [
|
||||
"js/components/lnbits-qrcode.js",
|
||||
"js/components/lnbits-qrcode-lnurl.js",
|
||||
"js/components/lnbits-funding-sources.js",
|
||||
"js/components/extension-settings.js",
|
||||
"js/components/payment-list.js",
|
||||
|
||||
@@ -370,7 +370,12 @@
|
||||
v-if="extras.length"
|
||||
>
|
||||
<template v-for="entry in extras">
|
||||
<q-item v-if="!!entry.value" key="entry.key" class="text-grey-4">
|
||||
<q-item
|
||||
v-if="!!entry.value"
|
||||
key="entry.key"
|
||||
class="text-grey-4"
|
||||
style="white-space: normal; word-break: break-all"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label v-text="entry.key"></q-item-label>
|
||||
<q-item-label caption v-text="entry.value"></q-item-label>
|
||||
@@ -630,6 +635,25 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="lnbits-qrcode-lnurl">
|
||||
<div class="qrcode_lnurl__wrapper">
|
||||
<q-tabs
|
||||
v-model="tab"
|
||||
dense
|
||||
class="text-grey q-mb-md"
|
||||
active-color="primary"
|
||||
indicator-color="primary"
|
||||
align="justify"
|
||||
narrow-indicator
|
||||
inline-label
|
||||
>
|
||||
<q-tab name="bech32" icon="qr_code" label="bech32"></q-tab>
|
||||
<q-tab name="lud17" icon="link" label="url (lud17)"></q-tab>
|
||||
</q-tabs>
|
||||
<lnbits-qrcode :value="lnurl" class="rounded-borders"></lnbits-qrcode>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="lnbits-lnurlpay-success-action">
|
||||
<div>
|
||||
<p
|
||||
|
||||
Generated
+184
-3
@@ -448,6 +448,19 @@
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/camel-case": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
|
||||
@@ -589,6 +602,20 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
@@ -600,6 +627,51 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
@@ -637,12 +709,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
@@ -663,6 +738,52 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
@@ -675,6 +796,57 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/html-minifier-terser": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
|
||||
@@ -776,6 +948,15 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
|
||||
@@ -90,6 +90,8 @@
|
||||
"js/bolt11-decoder.js"
|
||||
],
|
||||
"components": [
|
||||
"js/components/lnbits-qrcode.js",
|
||||
"js/components/lnbits-qrcode-lnurl.js",
|
||||
"js/components/lnbits-funding-sources.js",
|
||||
"js/components/extension-settings.js",
|
||||
"js/components/payment-list.js",
|
||||
|
||||
Generated
+43
-37
@@ -2133,14 +2133,14 @@ valkey = ["valkey (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "lnurl"
|
||||
version = "0.6.8"
|
||||
version = "0.7.3"
|
||||
description = "LNURL implementation for Python."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "lnurl-0.6.8-py3-none-any.whl", hash = "sha256:4fff53efcdd401cf4169676bd1ab85e9e241a762a9a5407ee11e6a6e120e8279"},
|
||||
{file = "lnurl-0.6.8.tar.gz", hash = "sha256:de64a47179980a4b52cd6b89ad377cda14502f1998f53724490683f6f5c4ed90"},
|
||||
{file = "lnurl-0.7.3-py3-none-any.whl", hash = "sha256:1a6796cb5124047e2d289ca8ba2015bbf62e3f78d717802205dc6e303e31898a"},
|
||||
{file = "lnurl-0.7.3.tar.gz", hash = "sha256:b8e8036bc1d215d1d9b0cbb18b8d9b8d2e7e98850be1c98d33887e03c9f4b144"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -2430,44 +2430,50 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.16.1"
|
||||
version = "1.17.1"
|
||||
description = "Optional static typing for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b"},
|
||||
{file = "mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37"},
|
||||
{file = "mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab"},
|
||||
{file = "mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972"},
|
||||
{file = "mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7"},
|
||||
{file = "mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df"},
|
||||
{file = "mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390"},
|
||||
{file = "mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94"},
|
||||
{file = "mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b"},
|
||||
{file = "mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58"},
|
||||
{file = "mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5"},
|
||||
{file = "mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd"},
|
||||
{file = "mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b"},
|
||||
{file = "mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5"},
|
||||
{file = "mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b"},
|
||||
{file = "mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb"},
|
||||
{file = "mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403"},
|
||||
{file = "mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056"},
|
||||
{file = "mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341"},
|
||||
{file = "mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb"},
|
||||
{file = "mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19"},
|
||||
{file = "mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7"},
|
||||
{file = "mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81"},
|
||||
{file = "mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6"},
|
||||
{file = "mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849"},
|
||||
{file = "mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14"},
|
||||
{file = "mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a"},
|
||||
{file = "mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733"},
|
||||
{file = "mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd"},
|
||||
{file = "mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0"},
|
||||
{file = "mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a"},
|
||||
{file = "mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91"},
|
||||
{file = "mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed"},
|
||||
{file = "mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9"},
|
||||
{file = "mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99"},
|
||||
{file = "mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8"},
|
||||
{file = "mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8"},
|
||||
{file = "mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259"},
|
||||
{file = "mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d"},
|
||||
{file = "mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9"},
|
||||
{file = "mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -4573,4 +4579,4 @@ migration = ["psycopg2-binary"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "~3.12 | ~3.11 | ~3.10"
|
||||
content-hash = "c1714a4df0e8f7d8702fffe9c57b21fc39effb194631741b4718466c18b73d8f"
|
||||
content-hash = "97407d81374521c715f792e64cd17fcbe9b487f7de515e62891657aa4fa4e383"
|
||||
|
||||
+10
-3
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "lnbits"
|
||||
version = "1.3.0-rc2"
|
||||
version = "1.3.0-rc4"
|
||||
description = "LNbits, free and open-source Lightning wallet and accounts system."
|
||||
authors = ["Alan Bits <alan@lnbits.com>"]
|
||||
readme = "README.md"
|
||||
@@ -20,7 +20,7 @@ fastapi = "0.116.1"
|
||||
starlette = "0.47.1"
|
||||
httpx = "0.27.0"
|
||||
jinja2 = "3.1.6"
|
||||
lnurl = "0.6.8"
|
||||
lnurl = "0.7.3"
|
||||
pydantic = "1.10.22"
|
||||
pyqrcode = "1.2.1"
|
||||
shortuuid = "1.0.13"
|
||||
@@ -75,7 +75,7 @@ migration = ["psycopg2-binary"]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^25.1.0"
|
||||
mypy = "^1.11.2"
|
||||
mypy = "^1.17.1"
|
||||
types-protobuf = "^6.30.2.20250516"
|
||||
pre-commit = "^4.2.0"
|
||||
openapi-spec-validator = "^0.7.1"
|
||||
@@ -127,6 +127,13 @@ exclude = [
|
||||
"^lnbits/extensions",
|
||||
"^lnbits/upgrades",
|
||||
]
|
||||
plugins = "pydantic.mypy"
|
||||
|
||||
[tool.pydantic-mypy]
|
||||
init_forbid_extra = true
|
||||
init_typed = true
|
||||
warn_required_dynamic_aliases = true
|
||||
warn_untyped_fields = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = [
|
||||
|
||||
@@ -623,7 +623,6 @@ async def test_fiat_tracking(client, adminkey_headers_from, settings: Settings):
|
||||
{
|
||||
"tag": "payRequest",
|
||||
"callback": "https://example.com/callback",
|
||||
"k1": "randomk1value",
|
||||
"minSendable": 1000,
|
||||
"maxSendable": 1_500_000,
|
||||
"metadata": '[["text/plain", "Payment to yo"]]',
|
||||
@@ -793,9 +792,6 @@ async def test_api_payments_pay_lnurl(client, adminkey_headers_from):
|
||||
"/api/v1/payments/lnurl", json=lnurl_data, headers=adminkey_headers_from
|
||||
)
|
||||
assert response.status_code == 400
|
||||
assert (
|
||||
response.json()["detail"] == "Failed to connect to https://xxxxxxx.lnbits.com."
|
||||
)
|
||||
|
||||
# Test with invalid callback URL
|
||||
lnurl_data["res"]["callback"] = "invalid-url.lnbits.com"
|
||||
|
||||
Reference in New Issue
Block a user