Compare commits
5
Commits
v1.3.0-rc3
...
v1.3.0-rc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15984fa49b | ||
|
|
67dc096909 | ||
|
|
2daddf8b74 | ||
|
|
ae6cf47244 | ||
|
|
1488f580ff |
@@ -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 LnAddress, Lnurl, LnurlPayResponse
|
||||
from pydantic import BaseModel
|
||||
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
|
||||
@@ -18,3 +20,13 @@ class CreateLnurlWithdraw(BaseModel):
|
||||
|
||||
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,4 +1,8 @@
|
||||
from time import time
|
||||
|
||||
from lnurl import (
|
||||
LnAddress,
|
||||
Lnurl,
|
||||
LnurlErrorResponse,
|
||||
LnurlPayActionResponse,
|
||||
LnurlPayResponse,
|
||||
@@ -6,8 +10,11 @@ from lnurl import (
|
||||
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
|
||||
|
||||
@@ -34,12 +41,26 @@ async def get_pr_from_lnurl(
|
||||
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
|
||||
@@ -49,10 +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=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} "
|
||||
|
||||
@@ -37,6 +37,18 @@ 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)],
|
||||
@@ -47,13 +59,7 @@ lnurl_router = APIRouter(tags=["LNURL"])
|
||||
| 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
|
||||
@@ -68,13 +74,7 @@ async def api_lnurlscan(code: str) -> LnurlResponseModel:
|
||||
| LnurlErrorResponse,
|
||||
)
|
||||
async def api_lnurlscan_post(scan: LnurlScan) -> LnurlResponseModel:
|
||||
try:
|
||||
res = await lnurl_handle(scan.lnurl, user_agent=settings.user_agent, timeout=5)
|
||||
except LnurlResponseException as exc:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST, detail=str(exc)
|
||||
) from exc
|
||||
return res
|
||||
return await _handle(scan.lnurl)
|
||||
|
||||
|
||||
@lnurl_router.post("/api/v1/lnurlauth")
|
||||
@@ -101,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":
|
||||
@@ -119,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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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:]
|
||||
|
||||
+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}
|
||||
}
|
||||
})
|
||||
@@ -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,
|
||||
@@ -177,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(
|
||||
@@ -536,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,
|
||||
@@ -1098,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",
|
||||
|
||||
@@ -635,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
|
||||
|
||||
@@ -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.7.2"
|
||||
version = "0.7.3"
|
||||
description = "LNURL implementation for Python."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "lnurl-0.7.2-py3-none-any.whl", hash = "sha256:4323ac398d49e5b883000c166b4c2448df2acad2eddca99f656c67f7b97cd80f"},
|
||||
{file = "lnurl-0.7.2.tar.gz", hash = "sha256:9f0881cb5909512cadb35d238c27347d6a8afcec10b2b7aadd560083fd210c0b"},
|
||||
{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 = "7100b67d94f82cb53ceb8f4e47ae8a4aa426408feabeb0746c323d09f229c242"
|
||||
content-hash = "97407d81374521c715f792e64cd17fcbe9b487f7de515e62891657aa4fa4e383"
|
||||
|
||||
+10
-3
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "lnbits"
|
||||
version = "1.3.0-rc3"
|
||||
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.7.2"
|
||||
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 = [
|
||||
|
||||
@@ -792,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