Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ff997ec1 | ||
|
|
eb55170905 | ||
|
|
dc466ae738 | ||
|
|
dd5fe8b7a2 | ||
|
|
5196ee6a15 | ||
|
|
3498cfb5b8 | ||
|
|
30ffe437a0 | ||
|
|
574641d432 | ||
|
|
90d14629d9 | ||
|
|
3be06269c2 | ||
|
|
d7723696d8 | ||
|
|
91f7b7730d | ||
|
|
b2aaf97cf3 | ||
|
|
8e0d1cc100 | ||
|
|
cb3fd56647 | ||
|
|
9f383bfee6 | ||
|
|
3b2e28dd60 | ||
|
|
132192bc94 | ||
|
|
281c3df826 | ||
|
|
08591f34c2 | ||
|
|
f0e8ae0f5c | ||
|
|
168cb726b1 | ||
|
|
91ac245307 | ||
|
|
d098e2f710 | ||
|
|
a0f65f4cda | ||
|
|
606ee215b4 | ||
|
|
7716f819f6 |
@@ -23,3 +23,10 @@ mypy.ini
|
||||
package-lock.json
|
||||
package.json
|
||||
pytest.ini
|
||||
|
||||
.mypy_cache
|
||||
.github
|
||||
.pytest_cache
|
||||
.vscode
|
||||
bin
|
||||
dist
|
||||
|
||||
@@ -18,7 +18,6 @@ ENABLE_LOG_TO_FILE=true
|
||||
# https://loguru.readthedocs.io/en/stable/api/logger.html#file
|
||||
LOG_ROTATION="100 MB"
|
||||
LOG_RETENTION="3 months"
|
||||
|
||||
# for database cleanup commands
|
||||
# CLEANUP_WALLETS_DAYS=90
|
||||
|
||||
@@ -187,7 +186,6 @@ BOLTZ_CLIENT_ENDPOINT=127.0.0.1:9002
|
||||
BOLTZ_CLIENT_MACAROON="/home/bob/.boltz/macaroons/admin.macaroon"
|
||||
# HEXSTRING instead of path also possible
|
||||
BOLTZ_CLIENT_CERT="/home/bob/.boltz/tls.cert"
|
||||
BOLTZ_CLIENT_WALLET="lnbits"
|
||||
|
||||
# StrikeWallet
|
||||
STRIKE_API_ENDPOINT=https://api.strike.me/v1
|
||||
@@ -333,4 +331,3 @@ LNBITS_RESERVE_FEE_PERCENT=1.0
|
||||
######################################
|
||||
###### Logging and Development #######
|
||||
######################################
|
||||
|
||||
|
||||
@@ -12,6 +12,13 @@ jobs:
|
||||
lint:
|
||||
uses: ./.github/workflows/lint.yml
|
||||
|
||||
test-frontend:
|
||||
needs: [ lint ]
|
||||
uses: ./.github/workflows/make.yml
|
||||
with:
|
||||
make: test-frontend
|
||||
npm: true
|
||||
|
||||
test-api:
|
||||
needs: [ lint ]
|
||||
strategy:
|
||||
@@ -75,7 +82,14 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10"]
|
||||
backend-wallet-class: ["LndRestWallet", "LndWallet", "CoreLightningWallet", "CoreLightningRestWallet", "LNbitsWallet", "EclairWallet"]
|
||||
backend-wallet-class:
|
||||
- BoltzWallet
|
||||
- LndRestWallet
|
||||
- LndWallet
|
||||
- CoreLightningWallet
|
||||
- CoreLightningRestWallet
|
||||
- LNbitsWallet
|
||||
- EclairWallet
|
||||
with:
|
||||
custom-pytest: "uv run pytest tests/regtest"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
@@ -62,3 +62,4 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
build-args: LNBITS_TAG=${{ inputs.tag }}
|
||||
|
||||
@@ -40,8 +40,8 @@ jobs:
|
||||
run: |
|
||||
git clone https://github.com/lnbits/legend-regtest-enviroment.git docker
|
||||
cd docker
|
||||
chmod +x ./tests
|
||||
./tests
|
||||
chmod +x ./start-regtest
|
||||
./start-regtest
|
||||
sudo chmod -R a+rwx .
|
||||
|
||||
- name: Run pytest
|
||||
@@ -63,6 +63,8 @@ jobs:
|
||||
LNBITS_ENDPOINT: http://localhost:5001
|
||||
LNBITS_KEY: "d08a3313322a4514af75d488bcc27eee"
|
||||
ECLAIR_URL: http://127.0.0.1:8082
|
||||
BOLTZ_CLIENT_ENDPOINT: 127.0.0.1:9002
|
||||
BOLTZ_MNEMONIC: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
|
||||
LNBITS_MAX_OUTGOING_PAYMENT_AMOUNT_SATS: 1000000000
|
||||
LNBITS_MAX_INCOMING_PAYMENT_AMOUNT_SATS: 1000000000
|
||||
ECLAIR_PASS: lnbits
|
||||
|
||||
@@ -10,7 +10,29 @@ permissions:
|
||||
|
||||
jobs:
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
upload_url: ${{ steps.get_upload_url.outputs.upload_url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Create github pre-release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref_name }}
|
||||
run: |
|
||||
gh release create "$tag" --prerelease --generate-notes --draft
|
||||
- id: get_upload_url
|
||||
name: Get upload url of Github release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref_name }}
|
||||
run: |
|
||||
upload_url=$(gh release view "$tag" --json uploadUrl -q ".uploadUrl")
|
||||
echo "upload_url=$upload_url" >> "$GITHUB_OUTPUT"
|
||||
|
||||
docker:
|
||||
if: github.repository == 'lnbits/lnbits'
|
||||
uses: ./.github/workflows/docker.yml
|
||||
with:
|
||||
tag: ${{ github.ref_name }}
|
||||
@@ -19,6 +41,7 @@ jobs:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
pypi:
|
||||
if: github.repository == 'lnbits/lnbits'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Install dependencies for building secp256k1
|
||||
@@ -30,3 +53,10 @@ jobs:
|
||||
uses: JRubics/poetry-publish@v1.15
|
||||
with:
|
||||
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
||||
|
||||
appimage:
|
||||
needs: [ release ]
|
||||
uses: ./.github/workflows/appimage.yml
|
||||
with:
|
||||
tag_name: ${{ github.ref_name }}
|
||||
upload_url: ${{ needs.release.outputs.upload_url }}
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
release:
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
upload_url: ${{ steps.get_upload_url.outputs.upload_url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Create github release
|
||||
@@ -23,7 +23,13 @@ jobs:
|
||||
tag: ${{ github.ref_name }}
|
||||
run: |
|
||||
gh release create "$tag" --generate-notes --draft
|
||||
upload_url=$(gh release view "$tag" --repo "${{ github.repository }}" --json uploadUrl -q ".uploadUrl")
|
||||
- id: get_upload_url
|
||||
name: Get upload url of Github release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref_name }}
|
||||
run: |
|
||||
upload_url=$(gh release view "$tag" --json uploadUrl -q ".uploadUrl")
|
||||
echo "upload_url=$upload_url" >> "$GITHUB_OUTPUT"
|
||||
|
||||
docker:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: tests
|
||||
name: tests pytest
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
FROM boltz/boltz-client:latest AS boltz
|
||||
ARG LNBITS_TAG=latest
|
||||
|
||||
FROM lnbits/lnbits:latest
|
||||
FROM boltz/boltz-client:latest AS boltz
|
||||
FROM lnbits/lnbits:${LNBITS_TAG}
|
||||
|
||||
COPY --from=boltz /bin/boltzd /bin/boltzcli /usr/local/bin/
|
||||
RUN ls -l /usr/local/bin/boltzd
|
||||
|
||||
@@ -41,6 +41,10 @@ dev:
|
||||
docker:
|
||||
docker build -t lnbits/lnbits .
|
||||
|
||||
test-frontend:
|
||||
npm install
|
||||
npm test
|
||||
|
||||
test-wallets:
|
||||
LNBITS_DATA_FOLDER="./tests/data" \
|
||||
LNBITS_BACKEND_WALLET_CLASS="FakeWallet" \
|
||||
|
||||
@@ -16,7 +16,7 @@ async def create_user_asset(user_id: str, file: UploadFile, is_public: bool) ->
|
||||
if file.content_type.lower() not in settings.lnbits_assets_allowed_mime_types:
|
||||
raise ValueError(f"File type '{file.content_type}' not allowed.")
|
||||
|
||||
if user_id not in settings.lnbits_assets_no_limit_users:
|
||||
if not settings.is_unlimited_assets_user(user_id):
|
||||
user_assets_count = await get_user_assets_count(user_id)
|
||||
if user_assets_count >= settings.lnbits_max_assets_per_user:
|
||||
raise ValueError(
|
||||
|
||||
@@ -337,7 +337,7 @@ async def api_payment(payment_hash, x_api_key: str | None = Header(None)):
|
||||
return {"paid": False, "status": "failed"}
|
||||
|
||||
try:
|
||||
status = await payment.check_status()
|
||||
payment = await update_pending_payment(payment)
|
||||
except Exception:
|
||||
if wallet and wallet.id == payment.wallet_id:
|
||||
return {"paid": False, "details": payment}
|
||||
@@ -346,7 +346,7 @@ async def api_payment(payment_hash, x_api_key: str | None = Header(None)):
|
||||
if wallet and wallet.id == payment.wallet_id:
|
||||
return {
|
||||
"paid": payment.success,
|
||||
"status": f"{status!s}",
|
||||
"status": f"{payment.status!s}",
|
||||
"preimage": payment.preimage,
|
||||
"details": payment,
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ async def check_account_id_exists(
|
||||
account_id = cache.get(cache_key)
|
||||
if account_id:
|
||||
r.scope["user_id"] = account_id.id
|
||||
await _check_user_access(r, account_id, conn=conn)
|
||||
await _check_user_access(r, account_id.id, conn=conn)
|
||||
return account_id
|
||||
|
||||
account = await _check_account_exists(r, access_token, usr, conn=conn)
|
||||
|
||||
@@ -35,6 +35,7 @@ class StripeTerminalOptions(BaseModel):
|
||||
|
||||
capture_method: Literal["automatic", "manual"] = "automatic"
|
||||
metadata: dict[str, str] = Field(default_factory=dict)
|
||||
reader_id: str | None = None
|
||||
|
||||
|
||||
class StripeCheckoutOptions(BaseModel):
|
||||
@@ -295,6 +296,15 @@ class StripeWallet(FiatProvider):
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
async def _process_terminal_payment_intent(
|
||||
self, reader_id: str, payment_intent_id: str
|
||||
) -> None:
|
||||
data = {"payment_intent": payment_intent_id}
|
||||
r = await self.client.post(
|
||||
f"/v1/terminal/readers/{reader_id}/process_payment_intent", data=data
|
||||
)
|
||||
r.raise_for_status()
|
||||
|
||||
async def _create_checkout_invoice(
|
||||
self,
|
||||
amount_cents: int,
|
||||
@@ -378,6 +388,17 @@ class StripeWallet(FiatProvider):
|
||||
ok=False,
|
||||
error_message="Error: missing PaymentIntent or client_secret",
|
||||
)
|
||||
if term.reader_id:
|
||||
try:
|
||||
await self._process_terminal_payment_intent(term.reader_id, pi_id)
|
||||
except Exception as exc:
|
||||
logger.warning(exc)
|
||||
return FiatInvoiceResponse(
|
||||
ok=False,
|
||||
error_message=(
|
||||
"Error: unable to process PaymentIntent on reader"
|
||||
),
|
||||
)
|
||||
return FiatInvoiceResponse(
|
||||
ok=True, checking_id=pi_id, payment_request=client_secret
|
||||
)
|
||||
|
||||
+7
-20
@@ -6,7 +6,6 @@ import inspect
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import secrets
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
from os import path
|
||||
@@ -19,21 +18,6 @@ from loguru import logger
|
||||
from pydantic import BaseModel, BaseSettings, Extra, Field, validator
|
||||
|
||||
|
||||
def generate_default_boltz_mnemonic() -> str | None:
|
||||
"""
|
||||
Generate a BIP39 mnemonic using the bundled embit dependency.
|
||||
If embit is unavailable at runtime, return None to avoid blocking startup.
|
||||
"""
|
||||
try:
|
||||
from embit import bip39
|
||||
except Exception as exc: # pragma: no cover - dependency missing at runtime
|
||||
logger.warning(f"Unable to generate Boltz mnemonic (embit missing): {exc}")
|
||||
return None
|
||||
|
||||
entropy = secrets.token_bytes(16) # 128 bits -> 12-word mnemonic
|
||||
return bip39.mnemonic_from_bytes(entropy)
|
||||
|
||||
|
||||
def list_parse_fallback(v: str):
|
||||
v = v.replace(" ", "")
|
||||
if len(v) > 0:
|
||||
@@ -324,6 +308,12 @@ class AssetSettings(LNbitsSettings):
|
||||
lnbits_max_assets_per_user: int = Field(default=1, ge=0)
|
||||
lnbits_assets_no_limit_users: list[str] = Field(default=[])
|
||||
|
||||
def is_unlimited_assets_user(self, user_id: str) -> bool:
|
||||
return (
|
||||
settings.is_admin_user(user_id)
|
||||
or user_id in self.lnbits_assets_no_limit_users
|
||||
)
|
||||
|
||||
|
||||
class FeeSettings(LNbitsSettings):
|
||||
lnbits_reserve_fee_min: int = Field(default=2000, ge=0)
|
||||
@@ -617,12 +607,9 @@ class BreezLiquidSdkFundingSource(LNbitsSettings):
|
||||
class BoltzFundingSource(LNbitsSettings):
|
||||
boltz_client_endpoint: str | None = Field(default="127.0.0.1:9002")
|
||||
boltz_client_macaroon: str | None = Field(default=None)
|
||||
boltz_client_wallet: str | None = Field(default="lnbits")
|
||||
boltz_client_password: str = Field(default="")
|
||||
boltz_client_cert: str | None = Field(default=None)
|
||||
boltz_mnemonic: str | None = Field(
|
||||
default_factory=generate_default_boltz_mnemonic
|
||||
)
|
||||
boltz_mnemonic: str | None = Field(default=None)
|
||||
|
||||
|
||||
class StrikeFundingSource(LNbitsSettings):
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+29
-4
@@ -1,6 +1,30 @@
|
||||
window.app.component('lnbits-error', {
|
||||
template: '#lnbits-error',
|
||||
mixins: [window.windowMixin],
|
||||
<template>
|
||||
<div class="text-center q-pa-md flex flex-center">
|
||||
<div>
|
||||
<div class="error-code" v-text="code"></div>
|
||||
<div class="error-message" v-text="message"></div>
|
||||
<div class="q-mx-auto q-mt-lg justify-center" style="width: max-content">
|
||||
<q-btn
|
||||
v-if="isExtension"
|
||||
color="primary"
|
||||
@click="goToExtension()"
|
||||
label="Go To Extension"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
v-else-if="g.isUserAuthorized"
|
||||
color="primary"
|
||||
@click="goToWallet()"
|
||||
label="Go to Wallet"
|
||||
></q-btn>
|
||||
<q-btn v-else color="primary" @click="goBack()" label="Go Back"></q-btn>
|
||||
<span class="q-mx-md">OR</span>
|
||||
<q-btn color="secondary" @click="goHome()" label="Go Home"></q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['dynamic', 'code', 'message'],
|
||||
computed: {
|
||||
isExtension() {
|
||||
@@ -50,4 +74,5 @@ window.app.component('lnbits-error', {
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
import LnbitsError from './lnbits-error.vue'
|
||||
|
||||
window.LnbitsError = LnbitsError
|
||||
@@ -89,8 +89,14 @@ window.app.component('lnbits-admin-funding-sources', {
|
||||
lnd_rest_cert: 'Certificate',
|
||||
lnd_rest_macaroon: 'Macaroon',
|
||||
lnd_rest_macaroon_encrypted: 'Encrypted Macaroon',
|
||||
lnd_rest_route_hints: 'Enable Route Hints',
|
||||
lnd_rest_allow_self_payment: 'Allow Self Payment'
|
||||
lnd_rest_route_hints: {
|
||||
advanced: true,
|
||||
label: 'Enable Route Hints'
|
||||
},
|
||||
lnd_rest_allow_self_payment: {
|
||||
advanced: true,
|
||||
label: 'Allow Self Payment'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
@@ -160,22 +166,27 @@ window.app.component('lnbits-admin-funding-sources', {
|
||||
'BoltzWallet',
|
||||
'Boltz',
|
||||
{
|
||||
boltz_client_endpoint: 'Endpoint',
|
||||
boltz_client_endpoint: {
|
||||
label: 'Boltz client endpoint',
|
||||
value: '127.0.0.1:9002'
|
||||
},
|
||||
boltz_client_macaroon: {
|
||||
label: 'Admin Macaroon path or hex',
|
||||
default: '/home/ubuntu/.boltz/macaroons/admin.macaroon'
|
||||
value: '/home/ubuntu/.boltz/macaroons/admin.macaroon'
|
||||
},
|
||||
boltz_client_cert: {
|
||||
label: 'Certificate path or hex',
|
||||
default: '/home/ben/.boltz/tls.cert'
|
||||
value: '/home/ubuntu/.boltz/tls.cert'
|
||||
},
|
||||
boltz_client_wallet: 'Wallet Name',
|
||||
boltz_client_password: 'Wallet Password (can be empty)',
|
||||
boltz_mnemonic: {
|
||||
label: 'Liquid mnemonic (copy into greenwallet)',
|
||||
label: 'Liquid seed phrase',
|
||||
hint: 'Boltz will fetch once connected, but you can change later (can be opened in a liquid wallet) ',
|
||||
copy: true,
|
||||
qrcode: true,
|
||||
hint: 'Paste or type your liquid mnemonic'
|
||||
qrcode: true
|
||||
},
|
||||
boltz_client_password: {
|
||||
label: 'Wallet Password (optional)',
|
||||
advanced: true
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -168,4 +168,8 @@ window.app.use(Quasar, quasarConfig)
|
||||
|
||||
window.app.use(window.i18n)
|
||||
window.app.use(window.router)
|
||||
|
||||
window.app.component(QrcodeVue)
|
||||
window.app.component('lnbits-error', window.LnbitsError)
|
||||
|
||||
window.app.mount('#vue')
|
||||
|
||||
@@ -86,6 +86,7 @@ window.PageExtensions = {
|
||||
version: release.version
|
||||
})
|
||||
.then(response => {
|
||||
this.selectedExtension.inProgress = false
|
||||
const extension = this.extensions.find(
|
||||
ext => ext.id === this.selectedExtension.id
|
||||
)
|
||||
@@ -100,7 +101,7 @@ window.PageExtensions = {
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn(err)
|
||||
extension.inProgress = false
|
||||
this.selectedExtension.inProgress = false
|
||||
LNbits.utils.notifyApiError(err)
|
||||
})
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -76,7 +76,6 @@
|
||||
"js/components/lnbits-wallet-paylinks.js",
|
||||
"js/components/lnbits-wallet-extra.js",
|
||||
"js/components/lnbits-home-logos.js",
|
||||
"js/components/lnbits-error.js",
|
||||
"js/components/lnbits-qrcode.js",
|
||||
"js/components/lnbits-qrcode-lnurl.js",
|
||||
"js/components/lnbits-disclaimer.js",
|
||||
|
||||
@@ -94,6 +94,8 @@
|
||||
{% endif %}
|
||||
<!-- scripts from extensions -->
|
||||
{% block scripts %}{% endblock %}
|
||||
<!-- modules js -->
|
||||
<script src="/static/modules/components.umd.js"></script>
|
||||
<!-- components js -->
|
||||
{% for url in INCLUDED_COMPONENTS %}
|
||||
<script src="{{ static_url_for('static', url) }}"></script>
|
||||
|
||||
@@ -29,8 +29,7 @@ include('components/lnbits-wallet-api-docs.vue') %} {%
|
||||
include('components/lnbits-wallet-share.vue') %} {%
|
||||
include('components/lnbits-wallet-charts.vue') %} {%
|
||||
include('components/lnbits-wallet-paylinks.vue') %} {%
|
||||
include('components/lnbits-wallet-extra.vue') %} {%
|
||||
include('components/lnbits-error.vue') %}
|
||||
include('components/lnbits-wallet-extra.vue') %}
|
||||
|
||||
<template id="lnbits-manage">
|
||||
<q-list v-if="g.user" dense class="lnbits-drawer__q-list">
|
||||
|
||||
@@ -35,7 +35,9 @@
|
||||
>
|
||||
<div
|
||||
class="row"
|
||||
v-for="([key, prop], i) in Object.entries(fundingSources.get(fund))"
|
||||
v-for="([key, prop], i) in Object.entries(
|
||||
fundingSources.get(fund)
|
||||
).filter(([, p]) => !p.advanced)"
|
||||
:key="i"
|
||||
>
|
||||
<div class="col-12">
|
||||
@@ -46,7 +48,7 @@
|
||||
:type="hideInput ? 'password' : 'text'"
|
||||
:label="prop.label"
|
||||
:hint="prop.hint"
|
||||
:readonly="prop.readonly || false"
|
||||
:value="prop.value"
|
||||
>
|
||||
<q-btn
|
||||
v-if="prop.copy"
|
||||
@@ -69,6 +71,55 @@
|
||||
</q-input>
|
||||
</div>
|
||||
</div>
|
||||
<q-expansion-item
|
||||
v-if="
|
||||
Object.entries(fundingSources.get(fund)).some(([, p]) => p.advanced)
|
||||
"
|
||||
dense
|
||||
expand-separator
|
||||
icon="tune"
|
||||
label="Advanced"
|
||||
class="q-mt-sm"
|
||||
>
|
||||
<div
|
||||
class="row"
|
||||
v-for="([key, prop], i) in Object.entries(
|
||||
fundingSources.get(fund)
|
||||
).filter(([, p]) => p.advanced)"
|
||||
:key="`adv-${i}`"
|
||||
>
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
v-model="formData[key]"
|
||||
filled
|
||||
class="q-mt-sm"
|
||||
:type="hideInput ? 'password' : 'text'"
|
||||
:label="prop.label"
|
||||
:hint="prop.hint"
|
||||
:readonly="prop.readonly || false"
|
||||
>
|
||||
<q-btn
|
||||
v-if="prop.copy"
|
||||
@click="utils.copyText(formData[key])"
|
||||
icon="content_copy"
|
||||
class="cursor-pointer"
|
||||
color="grey"
|
||||
flat
|
||||
dense
|
||||
></q-btn>
|
||||
<q-btn
|
||||
v-if="prop.qrcode"
|
||||
@click="showQRValue(formData[key])"
|
||||
icon="qr_code"
|
||||
class="cursor-pointer"
|
||||
color="grey"
|
||||
flat
|
||||
dense
|
||||
></q-btn>
|
||||
</q-input>
|
||||
</div>
|
||||
</div>
|
||||
</q-expansion-item>
|
||||
</div>
|
||||
</q-list>
|
||||
<q-dialog v-model="showQRDialog">
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<template id="lnbits-error">
|
||||
<div class="text-center q-pa-md flex flex-center">
|
||||
<div>
|
||||
<div class="error-code" v-text="code"></div>
|
||||
<div class="error-message" v-text="message"></div>
|
||||
<div class="q-mx-auto q-mt-lg justify-center" style="width: max-content">
|
||||
<q-btn
|
||||
v-if="isExtension"
|
||||
color="primary"
|
||||
@click="goToExtension()"
|
||||
label="Go To Extension"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
v-else-if="g.isUserAuthorized"
|
||||
color="primary"
|
||||
@click="goToWallet()"
|
||||
label="Go to Wallet"
|
||||
></q-btn>
|
||||
<q-btn v-else color="primary" @click="goBack()" label="Go Back"></q-btn>
|
||||
<span class="q-mx-md">OR</span>
|
||||
<q-btn color="secondary" @click="goHome()" label="Go Home"></q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
+137
-59
@@ -5,7 +5,7 @@ from bolt11.decode import decode
|
||||
from grpc.aio import AioRpcError
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.helpers import normalize_endpoint
|
||||
from lnbits.helpers import normalize_endpoint, sha256s
|
||||
from lnbits.settings import settings
|
||||
from lnbits.wallets.boltz_grpc_files import boltzrpc_pb2, boltzrpc_pb2_grpc
|
||||
from lnbits.wallets.lnd_grpc_files.lightning_pb2_grpc import grpc
|
||||
@@ -38,21 +38,18 @@ class BoltzWallet(Wallet):
|
||||
raise ValueError(
|
||||
"cannot initialize BoltzWallet: missing boltz_client_endpoint"
|
||||
)
|
||||
if not settings.boltz_client_wallet:
|
||||
raise ValueError(
|
||||
"cannot initialize BoltzWallet: missing boltz_client_wallet"
|
||||
)
|
||||
|
||||
self.endpoint = normalize_endpoint(
|
||||
settings.boltz_client_endpoint, add_proto=True
|
||||
)
|
||||
|
||||
self.metadata = None
|
||||
if settings.boltz_client_macaroon:
|
||||
self.metadata = [
|
||||
("macaroon", load_macaroon(settings.boltz_client_macaroon))
|
||||
]
|
||||
else:
|
||||
self.metadata = None
|
||||
try:
|
||||
macaroon = load_macaroon(settings.boltz_client_macaroon)
|
||||
self.metadata = [("macaroon", macaroon)]
|
||||
except Exception as e:
|
||||
logger.error(f"BoltzWallet failed to load macaroon: {e}")
|
||||
|
||||
if settings.boltz_client_cert:
|
||||
cert = open(settings.boltz_client_cert, "rb").read()
|
||||
@@ -63,31 +60,21 @@ class BoltzWallet(Wallet):
|
||||
|
||||
self.rpc = boltzrpc_pb2_grpc.BoltzStub(channel)
|
||||
self.wallet_id = 0
|
||||
|
||||
# Auto-create wallet if running in Docker mode
|
||||
async def _init_boltz_wallet():
|
||||
try:
|
||||
wallet_name = settings.boltz_client_wallet or "lnbits"
|
||||
mnemonic = await self._fetch_wallet(wallet_name)
|
||||
if mnemonic:
|
||||
logger.info(
|
||||
"✅ Mnemonic found for Boltz wallet, saving to settings"
|
||||
)
|
||||
settings.boltz_mnemonic = mnemonic
|
||||
|
||||
from lnbits.core.crud.settings import set_settings_field
|
||||
|
||||
await set_settings_field("boltz_mnemonic", mnemonic)
|
||||
else:
|
||||
logger.warning("⚠️ No mnemonic returned from Boltz")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Failed to auto-create Boltz wallet: {e}")
|
||||
|
||||
self._init_wallet_task = asyncio.create_task(_init_boltz_wallet())
|
||||
self.wallet_name = "lnbits"
|
||||
self.wallet_ready = False
|
||||
|
||||
async def status(self) -> StatusResponse:
|
||||
if self.wallet_ready is False:
|
||||
self.wallet_ready = True
|
||||
if settings.boltz_mnemonic: # restore wallet from mnemonic
|
||||
await self._restore_boltz_wallet(
|
||||
settings.boltz_mnemonic,
|
||||
settings.boltz_client_password,
|
||||
)
|
||||
else: # create new wallet
|
||||
await self._create_boltz_wallet()
|
||||
try:
|
||||
request = boltzrpc_pb2.GetWalletRequest(name=settings.boltz_client_wallet)
|
||||
request = boltzrpc_pb2.GetWalletRequest(name=self.wallet_name)
|
||||
response: boltzrpc_pb2.Wallet = await self.rpc.GetWallet(
|
||||
request, metadata=self.metadata
|
||||
)
|
||||
@@ -126,12 +113,18 @@ class BoltzWallet(Wallet):
|
||||
try:
|
||||
response = await self.rpc.CreateReverseSwap(request, metadata=self.metadata)
|
||||
except AioRpcError as exc:
|
||||
logger.warning(exc)
|
||||
return InvoiceResponse(ok=False, error_message=exc.details())
|
||||
fee_msat = response.routing_fee_milli_sat
|
||||
return InvoiceResponse(
|
||||
ok=True, checking_id=response.id, payment_request=response.invoice
|
||||
ok=True,
|
||||
checking_id=response.id,
|
||||
payment_request=response.invoice,
|
||||
fee_msat=fee_msat,
|
||||
)
|
||||
|
||||
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
|
||||
|
||||
pair = boltzrpc_pb2.Pair(**{"from": boltzrpc_pb2.LBTC})
|
||||
try:
|
||||
pair_info: boltzrpc_pb2.PairInfo
|
||||
@@ -143,8 +136,9 @@ class BoltzWallet(Wallet):
|
||||
|
||||
if not invoice.amount_msat:
|
||||
raise ValueError("amountless invoice")
|
||||
|
||||
service_fee: float = invoice.amount_msat * pair_info.fees.percentage / 100
|
||||
estimate = service_fee + pair_info.fees.miner_fees * 1000
|
||||
estimate = int(service_fee + pair_info.fees.miner_fees * 1000)
|
||||
if estimate > fee_limit_msat:
|
||||
error = f"fee of {estimate} msat exceeds limit of {fee_limit_msat} msat"
|
||||
|
||||
@@ -166,11 +160,12 @@ class BoltzWallet(Wallet):
|
||||
if response.id == "":
|
||||
# note that there is no way to provide a checking id here,
|
||||
# but there is no need since it immediately is considered as successfull
|
||||
return PaymentResponse(
|
||||
ok=True,
|
||||
checking_id=response.id,
|
||||
logger.warning(
|
||||
"Boltz invoice paid directly on liquid network using magic routing"
|
||||
)
|
||||
return PaymentResponse(ok=True, checking_id=invoice.payment_hash)
|
||||
except AioRpcError as exc:
|
||||
logger.warning(exc)
|
||||
return PaymentResponse(ok=False, error_message=exc.details())
|
||||
|
||||
try:
|
||||
@@ -180,10 +175,15 @@ class BoltzWallet(Wallet):
|
||||
info_request, metadata=self.metadata
|
||||
):
|
||||
if info.swap.state == boltzrpc_pb2.SUCCESSFUL:
|
||||
fee_msat = (info.swap.onchain_fee + info.swap.service_fee) * 1000
|
||||
logger.debug(
|
||||
f"Boltz swap successful, status: {info.swap.status}"
|
||||
f"fee_msat: {fee_msat}"
|
||||
)
|
||||
return PaymentResponse(
|
||||
ok=True,
|
||||
checking_id=response.id,
|
||||
fee_msat=(info.swap.onchain_fee + info.swap.service_fee) * 1000,
|
||||
checking_id=invoice.payment_hash,
|
||||
fee_msat=fee_msat,
|
||||
preimage=info.swap.preimage,
|
||||
)
|
||||
elif info.swap.error != "":
|
||||
@@ -192,21 +192,29 @@ class BoltzWallet(Wallet):
|
||||
ok=False, error_message="stream stopped unexpectedly"
|
||||
)
|
||||
except AioRpcError as exc:
|
||||
logger.warning(exc)
|
||||
return PaymentResponse(ok=False, error_message=exc.details())
|
||||
|
||||
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
||||
try:
|
||||
request = boltzrpc_pb2.GetSwapInfoRequest(id=checking_id)
|
||||
response: boltzrpc_pb2.GetSwapInfoResponse = await self.rpc.GetSwapInfo(
|
||||
boltzrpc_pb2.GetSwapInfoRequest(id=checking_id), metadata=self.metadata
|
||||
request,
|
||||
metadata=self.metadata,
|
||||
)
|
||||
swap = response.reverse_swap
|
||||
except AioRpcError:
|
||||
except AioRpcError as exc:
|
||||
logger.warning(exc)
|
||||
return PaymentPendingStatus()
|
||||
if swap.state == boltzrpc_pb2.SwapState.SUCCESSFUL:
|
||||
fee_msat = (
|
||||
swap.service_fee + swap.onchain_fee
|
||||
) * 1000 + swap.routing_fee_msat
|
||||
logger.debug(
|
||||
f"Boltz swap successful, status: {swap.status}, fee_msat: {fee_msat}"
|
||||
)
|
||||
return PaymentSuccessStatus(
|
||||
fee_msat=(
|
||||
(swap.service_fee + swap.onchain_fee) * 1000 + swap.routing_fee_msat
|
||||
),
|
||||
fee_msat=fee_msat,
|
||||
preimage=swap.preimage,
|
||||
)
|
||||
elif swap.state == boltzrpc_pb2.SwapState.PENDING:
|
||||
@@ -216,18 +224,23 @@ class BoltzWallet(Wallet):
|
||||
|
||||
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
||||
try:
|
||||
checking_id_bytes = bytes.fromhex(checking_id)
|
||||
request = boltzrpc_pb2.GetSwapInfoRequest(payment_hash=checking_id_bytes)
|
||||
response: boltzrpc_pb2.GetSwapInfoResponse = await self.rpc.GetSwapInfo(
|
||||
boltzrpc_pb2.GetSwapInfoRequest(
|
||||
payment_hash=bytes.fromhex(checking_id)
|
||||
),
|
||||
request,
|
||||
metadata=self.metadata,
|
||||
)
|
||||
swap = response.swap
|
||||
except AioRpcError:
|
||||
except AioRpcError as exc:
|
||||
logger.warning(exc)
|
||||
return PaymentPendingStatus()
|
||||
if swap.state == boltzrpc_pb2.SwapState.SUCCESSFUL:
|
||||
fee_msat = (swap.service_fee + swap.onchain_fee) * 1000
|
||||
logger.debug(
|
||||
f"Boltz swap successful, status: {swap.status}, fee_msat: {fee_msat}"
|
||||
)
|
||||
return PaymentSuccessStatus(
|
||||
fee_msat=(swap.service_fee + swap.onchain_fee) * 1000,
|
||||
fee_msat=fee_msat,
|
||||
preimage=swap.preimage,
|
||||
)
|
||||
elif swap.state == boltzrpc_pb2.SwapState.PENDING:
|
||||
@@ -244,7 +257,16 @@ class BoltzWallet(Wallet):
|
||||
request, metadata=self.metadata
|
||||
):
|
||||
reverse = info.reverse_swap
|
||||
if reverse and reverse.state == boltzrpc_pb2.SUCCESSFUL:
|
||||
if (
|
||||
reverse
|
||||
and reverse.state == boltzrpc_pb2.SUCCESSFUL
|
||||
and reverse.status == "invoice.settled"
|
||||
):
|
||||
fee_msat = ((reverse.service_fee + reverse.onchain_fee) * 1000,)
|
||||
logger.debug(
|
||||
f"Boltz reverse swap settled: {reverse.id}, "
|
||||
f"fee_msat: {fee_msat}"
|
||||
)
|
||||
yield reverse.id
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
@@ -253,24 +275,80 @@ class BoltzWallet(Wallet):
|
||||
)
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def _fetch_wallet(self, wallet_name: str) -> str | None:
|
||||
async def _check_wallet_exists(
|
||||
self, wallet_name: str
|
||||
) -> boltzrpc_pb2.Wallet | None:
|
||||
try:
|
||||
request = boltzrpc_pb2.GetWalletRequest(name=wallet_name)
|
||||
response = await self.rpc.GetWallet(request, metadata=self.metadata)
|
||||
logger.info(f"Wallet '{wallet_name}' already exists with ID {response.id}")
|
||||
return settings.boltz_mnemonic
|
||||
except AioRpcError as exc:
|
||||
details = exc.details() or "unknown error"
|
||||
if exc.code() != grpc.StatusCode.NOT_FOUND:
|
||||
logger.error(f"Error checking wallet existence: {details}")
|
||||
raise
|
||||
return response
|
||||
except AioRpcError:
|
||||
return None
|
||||
|
||||
async def _delete_wallet(self, wallet_id: int) -> None:
|
||||
logger.info(f"Deleting wallet '{wallet_id}'")
|
||||
delete_request = boltzrpc_pb2.RemoveWalletRequest(id=wallet_id)
|
||||
await self.rpc.RemoveWallet(delete_request, metadata=self.metadata)
|
||||
|
||||
async def _restore_wallet(self, wallet_name: str, mnemonic: str, password: str):
|
||||
logger.info(f"Restoring wallet '{wallet_name}' from mnemonic")
|
||||
credentials = boltzrpc_pb2.WalletCredentials(mnemonic=mnemonic)
|
||||
params = boltzrpc_pb2.WalletParams(
|
||||
name=wallet_name,
|
||||
currency=boltzrpc_pb2.LBTC,
|
||||
password=password,
|
||||
)
|
||||
restore_request = boltzrpc_pb2.ImportWalletRequest(
|
||||
credentials=credentials, params=params
|
||||
)
|
||||
response = await self.rpc.ImportWallet(restore_request, metadata=self.metadata)
|
||||
return response
|
||||
|
||||
async def _create_wallet(self, wallet_name: str, password: str) -> str:
|
||||
logger.info(f"Creating new wallet '{wallet_name}'")
|
||||
params = boltzrpc_pb2.WalletParams(
|
||||
name=wallet_name,
|
||||
currency=boltzrpc_pb2.LBTC,
|
||||
password=settings.boltz_client_password,
|
||||
password=password,
|
||||
)
|
||||
create_request = boltzrpc_pb2.CreateWalletRequest(params=params)
|
||||
response = await self.rpc.CreateWallet(create_request, metadata=self.metadata)
|
||||
return response.mnemonic
|
||||
|
||||
async def _restore_boltz_wallet(self, mnemonic: str, password: str):
|
||||
try:
|
||||
# delete the wallet with to name without hashing first if it exists
|
||||
wallet = await self._check_wallet_exists(self.wallet_name)
|
||||
if wallet:
|
||||
await self._delete_wallet(wallet.id)
|
||||
|
||||
# recreate the wallet with the hashed name
|
||||
self.wallet_name = sha256s(mnemonic + password)
|
||||
wallet = await self._check_wallet_exists(self.wallet_name)
|
||||
if wallet:
|
||||
logger.success("✅ Boltz wallet exists.")
|
||||
return
|
||||
await self._restore_wallet(self.wallet_name, mnemonic, password)
|
||||
logger.success("✅ Boltz wallet restored from existing mnemonic")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Failed to restore Boltz wallet: {e}")
|
||||
|
||||
async def _create_boltz_wallet(self):
|
||||
try:
|
||||
# delete the wallet with to name without hashing first if it exists
|
||||
wallet = await self._check_wallet_exists(self.wallet_name)
|
||||
if wallet:
|
||||
await self._delete_wallet(wallet.id)
|
||||
|
||||
logger.info("No Mnemonic found for Boltz wallet, creating wallet...")
|
||||
mnemonic = await self._create_wallet(
|
||||
self.wallet_name, settings.boltz_client_password
|
||||
)
|
||||
logger.success("✅ Boltz wallet created successfully")
|
||||
settings.boltz_mnemonic = mnemonic
|
||||
from lnbits.core.crud.settings import set_settings_field
|
||||
|
||||
await set_settings_field("boltz_mnemonic", mnemonic)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Failed to create Boltz wallet: {e}")
|
||||
|
||||
Generated
+2899
-1
File diff suppressed because it is too large
Load Diff
+13
-3
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "lnbits",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"sass": "./node_modules/.bin/sass ./lnbits/static/scss/base.scss > ./lnbits/static/css/base.css",
|
||||
"vendor_copy": "node -e \"require('./package.json').vendor.forEach((file) => require('fs').copyFileSync(file, './lnbits/static/vendor/'+file.split('/').pop()))\"",
|
||||
@@ -10,15 +11,25 @@
|
||||
"vendor_minify_css": "./node_modules/.bin/cleancss -o ./lnbits/static/bundle.min.css ./lnbits/static/bundle.css",
|
||||
"vendor_minify_js": "./node_modules/.bin/terser ./lnbits/static/bundle.js -o ./lnbits/static/bundle.min.js --compress --mangle",
|
||||
"vendor_minify_components": "./node_modules/.bin/terser ./lnbits/static/bundle-components.js -o ./lnbits/static/bundle-components.min.js --compress --mangle",
|
||||
"bundle": "npm run sass && npm run vendor_copy && npm run vendor_json && npm run vendor_bundle_css && npm run vendor_bundle_js && npm run vendor_bundle_components && npm run vendor_minify_css && npm run vendor_minify_js && npm run vendor_minify_components"
|
||||
"bundle": "npm run sass && npm run vendor_copy && npm run vendor_json && npm run vendor_bundle_css && npm run vendor_bundle_js && npm run vendor_bundle_components && npm run vendor_minify_css && npm run vendor_minify_js && npm run vendor_minify_components",
|
||||
"test": "npm run test:unit",
|
||||
"test:unit": "vitest --watch=false tests/frontend/unit",
|
||||
"test:unit:watch": "vitest tests/frontend/unit",
|
||||
"build": "vite build",
|
||||
"watch": "vite build --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^1.2.4",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"clean-css-cli": "^5.6.3",
|
||||
"concat": "^1.0.3",
|
||||
"jsdom": "^27.3.0",
|
||||
"prettier": "^3.7.4",
|
||||
"pyright": "1.1.289",
|
||||
"sass": "^1.94.2",
|
||||
"terser": "^5.44.1"
|
||||
"terser": "^5.44.1",
|
||||
"vitest": "^3"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.13.2",
|
||||
@@ -129,7 +140,6 @@
|
||||
"js/components/lnbits-wallet-paylinks.js",
|
||||
"js/components/lnbits-wallet-extra.js",
|
||||
"js/components/lnbits-home-logos.js",
|
||||
"js/components/lnbits-error.js",
|
||||
"js/components/lnbits-qrcode.js",
|
||||
"js/components/lnbits-qrcode-lnurl.js",
|
||||
"js/components/lnbits-disclaimer.js",
|
||||
|
||||
Generated
+20
-13
@@ -1314,14 +1314,14 @@ standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[stand
|
||||
|
||||
[[package]]
|
||||
name = "fastapi-sso"
|
||||
version = "0.18.0"
|
||||
version = "0.19.0"
|
||||
description = "FastAPI plugin to enable SSO to most common providers (such as Facebook login, Google login and login via Microsoft Office 365 Account)"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "fastapi_sso-0.18.0-py3-none-any.whl", hash = "sha256:727754ad770b70690f1471f7b0a9e17c6dfd8ebd6e477616d3bde1eaf62e53dc"},
|
||||
{file = "fastapi_sso-0.18.0.tar.gz", hash = "sha256:d8df5a686af7a6a7be248817544b405cf77f7e9ffcd5d0d7d2a196fd071964bc"},
|
||||
{file = "fastapi_sso-0.19.0-py3-none-any.whl", hash = "sha256:d958c46cd9996234c7b162e192168b4c0807a248224a55b0f877d3a82a16a930"},
|
||||
{file = "fastapi_sso-0.19.0.tar.gz", hash = "sha256:629f00581f72ea7e57f7b8775f8d2c425629c428c194359a2b4ebaa6bcb8e12b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1333,21 +1333,16 @@ pyjwt = ">=2.10.1,<3.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.18.0"
|
||||
version = "3.20.1"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
python-versions = ">=3.10"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"},
|
||||
{file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"},
|
||||
{file = "filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a"},
|
||||
{file = "filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
|
||||
typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""]
|
||||
|
||||
[[package]]
|
||||
name = "filetype"
|
||||
version = "1.2.0"
|
||||
@@ -1491,6 +1486,8 @@ files = [
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f47617f698838ba98f4ff4189aef02e7343952df3a615f847bb575c3feb177a7"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af41be48a4f60429d5cad9d22175217805098a9ef7c40bfef44f7669fb9d74d8"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246"},
|
||||
@@ -1500,6 +1497,8 @@ files = [
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9c6de1940a7d828635fbd254d69db79e54619f165ee7ce32fda763a9cb6a58c"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03c5136e7be905045160b1b9fdca93dd6727b180feeafda6818e6496434ed8c5"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb"},
|
||||
@@ -1509,6 +1508,8 @@ files = [
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945"},
|
||||
@@ -1518,6 +1519,8 @@ files = [
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f"},
|
||||
@@ -1525,6 +1528,8 @@ files = [
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d"},
|
||||
@@ -1534,6 +1539,8 @@ files = [
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:28a3c6b7cd72a96f61b0e4b2a36f681025b60ae4779cc73c1535eb5f29560b10"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:52206cd642670b0b320a1fd1cbfd95bca0e043179c1d8a045f2c6109dfe973be"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb"},
|
||||
{file = "greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d"},
|
||||
@@ -4564,4 +4571,4 @@ migration = ["psycopg2-binary"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.10,<3.13"
|
||||
content-hash = "13f4b6c9c33e051dba10dfa6833c11bb3aabdd4cce395fa8480d1fa4ee9ddb96"
|
||||
content-hash = "141fcae1bdf2b6356097afa0e2ccf970ad90e09e26a8361accd7e013e865b74c"
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "lnbits"
|
||||
version = "1.4.0-rc2"
|
||||
version = "1.4.0"
|
||||
requires-python = ">=3.10,<3.13"
|
||||
description = "LNbits, free and open-source Lightning wallet and accounts system."
|
||||
authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }]
|
||||
@@ -38,7 +38,7 @@ dependencies = [
|
||||
"bolt11==2.1.1",
|
||||
"pyjwt==2.10.1",
|
||||
"itsdangerous==2.2.0",
|
||||
"fastapi-sso==0.18.0",
|
||||
"fastapi-sso==0.19.0",
|
||||
# needed for boltz, lnurldevice, watchonly extensions
|
||||
"embit==0.8.0",
|
||||
# needed for scheduler extension
|
||||
|
||||
+2
-2
@@ -331,8 +331,8 @@ def _settings_cleanup(settings: Settings):
|
||||
settings.lnbits_allowed_users = []
|
||||
settings.auth_allowed_methods = AuthMethods.all()
|
||||
settings.auth_credetials_update_threshold = 120
|
||||
settings.lnbits_reserve_fee_percent = 1
|
||||
settings.lnbits_reserve_fee_min = 2000
|
||||
settings.lnbits_reserve_fee_percent = 2
|
||||
settings.lnbits_reserve_fee_min = 20000
|
||||
settings.lnbits_service_fee = 0
|
||||
settings.lnbits_reserve_fee_percent = 0
|
||||
settings.lnbits_wallet_limit_daily_max_withdraw = 0
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import {mount} from '@vue/test-utils'
|
||||
import {expect, test} from 'vitest'
|
||||
import LnbitsError from '../../../lnbits/static/components/lnbits-error.vue'
|
||||
|
||||
// installQuasarPlugin()
|
||||
export const quasarMock = {
|
||||
global: {
|
||||
mocks: {
|
||||
g: {
|
||||
isUserAuthorized: true
|
||||
},
|
||||
$q: {
|
||||
platform: {},
|
||||
screen: {},
|
||||
dark: false
|
||||
}
|
||||
},
|
||||
stubs: {
|
||||
QCard: true,
|
||||
QCardSection: true,
|
||||
QIcon: true,
|
||||
QBtn: true
|
||||
}
|
||||
}
|
||||
}
|
||||
test('displays message and code', () => {
|
||||
expect(LnbitsError).toBeTruthy()
|
||||
|
||||
const wrapper = mount(LnbitsError, {
|
||||
props: {
|
||||
message: 'Page not found!!!',
|
||||
code: 404
|
||||
},
|
||||
...quasarMock
|
||||
})
|
||||
expect(wrapper.text()).toContain('Page not found!!!')
|
||||
expect(wrapper.text()).toContain('404')
|
||||
})
|
||||
@@ -6,6 +6,11 @@ from subprocess import PIPE, Popen, TimeoutExpired
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.wallets import get_funding_source
|
||||
|
||||
funding_source = get_funding_source()
|
||||
is_boltz_wallet = funding_source.__class__.__name__ == "BoltzWallet"
|
||||
|
||||
docker_lightning_cli = [
|
||||
"docker",
|
||||
"exec",
|
||||
@@ -27,6 +32,16 @@ docker_bitcoin_cli = [
|
||||
]
|
||||
|
||||
|
||||
docker_elements_cli = [
|
||||
"docker",
|
||||
"exec",
|
||||
"lnbits-elementsd-1",
|
||||
"elements-cli",
|
||||
"-rpcport=18884",
|
||||
"-chain=liquidregtest",
|
||||
]
|
||||
|
||||
|
||||
docker_lightning_unconnected_cli = [
|
||||
"docker",
|
||||
"exec",
|
||||
@@ -50,7 +65,7 @@ docker_lightning_noroute_cli = [
|
||||
|
||||
|
||||
def run_cmd(cmd: list) -> str:
|
||||
timeout = 10
|
||||
timeout = 30
|
||||
process = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
|
||||
logger.debug(f"running command: {cmd}")
|
||||
@@ -128,6 +143,12 @@ def mine_blocks(blocks: int = 1) -> str:
|
||||
return run_cmd(cmd)
|
||||
|
||||
|
||||
def mine_blocks_liquid(blocks: int = 1) -> str:
|
||||
cmd = docker_elements_cli.copy()
|
||||
cmd.extend(["-generate", str(blocks)])
|
||||
return run_cmd(cmd)
|
||||
|
||||
|
||||
def get_unconnected_node_uri() -> str:
|
||||
cmd = docker_lightning_unconnected_cli.copy()
|
||||
cmd.append("getinfo")
|
||||
|
||||
@@ -18,6 +18,7 @@ from ..helpers import FakeError, is_fake, is_regtest
|
||||
from .helpers import (
|
||||
cancel_invoice,
|
||||
get_real_invoice,
|
||||
mine_blocks_liquid,
|
||||
pay_real_invoice,
|
||||
settle_invoice,
|
||||
)
|
||||
@@ -66,7 +67,7 @@ async def test_pay_real_invoice(
|
||||
|
||||
await asyncio.sleep(1)
|
||||
balance = await get_node_balance_sats()
|
||||
assert prev_balance - balance == 100
|
||||
assert prev_balance - balance == 100 + abs(payment.fee // 1000)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -153,7 +154,8 @@ async def test_create_real_invoice(client, adminkey_headers_from, inkey_headers_
|
||||
|
||||
async def on_paid(payment: Payment):
|
||||
|
||||
assert payment.checking_id == invoice["payment_hash"]
|
||||
assert payment.payment_hash == invoice["payment_hash"]
|
||||
assert payment.checking_id == invoice["checking_id"]
|
||||
|
||||
response = await client.get(
|
||||
f'/api/v1/payments/{invoice["payment_hash"]}', headers=inkey_headers_from
|
||||
@@ -164,7 +166,8 @@ async def test_create_real_invoice(client, adminkey_headers_from, inkey_headers_
|
||||
|
||||
await asyncio.sleep(1)
|
||||
balance = await get_node_balance_sats()
|
||||
assert balance - prev_balance == create_invoice.amount
|
||||
fee = abs(payment_status.get("details", {}).get("fee", 0) // 1000)
|
||||
assert balance - prev_balance == create_invoice.amount - fee
|
||||
|
||||
assert payment_status.get("preimage") is not None
|
||||
|
||||
@@ -200,6 +203,8 @@ async def test_pay_real_invoice_set_pending_and_check_state(
|
||||
assert len(invoice["payment_hash"]) == 64
|
||||
assert len(invoice["checking_id"]) > 0
|
||||
|
||||
mine_blocks_liquid(1)
|
||||
await asyncio.sleep(1)
|
||||
# check the payment status
|
||||
response = await client.get(
|
||||
f'/api/v1/payments/{invoice["payment_hash"]}', headers=inkey_headers_from
|
||||
@@ -341,6 +346,7 @@ async def test_pay_hold_invoice_check_pending_and_fail_cancel_payment_task_in_me
|
||||
assert preimage_hash == invoice_obj.payment_hash
|
||||
cancel_invoice(preimage_hash)
|
||||
|
||||
mine_blocks_liquid(1)
|
||||
# check if paid
|
||||
await asyncio.sleep(1)
|
||||
|
||||
@@ -382,7 +388,9 @@ async def test_receive_real_invoice_set_pending_and_check_state(
|
||||
assert not payment_status["paid"]
|
||||
|
||||
async def on_paid(payment: Payment):
|
||||
assert payment.checking_id == invoice["payment_hash"]
|
||||
|
||||
assert payment.payment_hash == invoice["payment_hash"]
|
||||
assert payment.checking_id == invoice["checking_id"]
|
||||
|
||||
response = await client.get(
|
||||
f'/api/v1/payments/{invoice["payment_hash"]}', headers=inkey_headers_from
|
||||
|
||||
@@ -7,6 +7,8 @@ from lnbits.core.services import (
|
||||
from lnbits.wallets import get_funding_source
|
||||
from lnbits.wallets.base import PaymentStatus
|
||||
|
||||
from .helpers import is_boltz_wallet
|
||||
|
||||
description = "test create invoice"
|
||||
|
||||
|
||||
@@ -17,7 +19,10 @@ async def test_create_invoice(from_wallet):
|
||||
amount=1000,
|
||||
memo=description,
|
||||
)
|
||||
assert payment.preimage
|
||||
|
||||
# we cannot know the preimage of the swap yet
|
||||
if not is_boltz_wallet:
|
||||
assert payment.preimage
|
||||
|
||||
invoice = decode(payment.bolt11)
|
||||
assert invoice.payment_hash == payment.payment_hash
|
||||
@@ -35,7 +40,10 @@ async def test_create_internal_invoice(from_wallet):
|
||||
payment = await create_invoice(
|
||||
wallet_id=from_wallet.id, amount=1000, memo=description, internal=True
|
||||
)
|
||||
assert payment.preimage
|
||||
|
||||
# we cannot know the preimage of the swap yet
|
||||
if not is_boltz_wallet:
|
||||
assert payment.preimage
|
||||
|
||||
invoice = decode(payment.bolt11)
|
||||
assert invoice.payment_hash == payment.payment_hash
|
||||
|
||||
@@ -6,6 +6,8 @@ from lnbits.core.services import (
|
||||
)
|
||||
from lnbits.exceptions import PaymentError
|
||||
|
||||
from .helpers import is_boltz_wallet
|
||||
|
||||
description = "test pay invoice"
|
||||
|
||||
|
||||
@@ -17,9 +19,12 @@ async def test_services_pay_invoice(to_wallet, real_invoice):
|
||||
description=description,
|
||||
)
|
||||
assert payment
|
||||
assert payment.status == PaymentState.SUCCESS
|
||||
assert payment.memo == description
|
||||
assert payment.preimage
|
||||
if not is_boltz_wallet:
|
||||
assert payment.status == PaymentState.SUCCESS
|
||||
assert payment.preimage
|
||||
else:
|
||||
assert payment.status == PaymentState.PENDING
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
||||
@@ -784,7 +784,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "fastapi-sso"
|
||||
version = "0.18.0"
|
||||
version = "0.19.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "fastapi" },
|
||||
@@ -793,9 +793,9 @@ dependencies = [
|
||||
{ name = "pydantic", extra = ["email"] },
|
||||
{ name = "pyjwt" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d2/57/cc971c018af5d09eb5f8d1cd12abdd99ab4c59ea5c0b0b1b96349ffe117d/fastapi_sso-0.18.0.tar.gz", hash = "sha256:d8df5a686af7a6a7be248817544b405cf77f7e9ffcd5d0d7d2a196fd071964bc", size = 16811, upload-time = "2025-03-20T17:09:09.958Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/74/fc/644bc8f82fc887fffcf9a3eab8eb3dea06ee9ea160ef20441455ed7a0001/fastapi_sso-0.19.0.tar.gz", hash = "sha256:629f00581f72ea7e57f7b8775f8d2c425629c428c194359a2b4ebaa6bcb8e12b", size = 17278, upload-time = "2025-12-17T15:18:06.721Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/03/70ca13994f5569d343a9f99dba2930c8ae3471171f161b8887d44b6c526f/fastapi_sso-0.18.0-py3-none-any.whl", hash = "sha256:727754ad770b70690f1471f7b0a9e17c6dfd8ebd6e477616d3bde1eaf62e53dc", size = 26103, upload-time = "2025-03-20T17:09:08.656Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/61/d2ac0800cb1f390ec2aad464ea4cc5944de285fabc8f987327da4633f17d/fastapi_sso-0.19.0-py3-none-any.whl", hash = "sha256:d958c46cd9996234c7b162e192168b4c0807a248224a55b0f877d3a82a16a930", size = 26419, upload-time = "2025-12-17T15:18:05.439Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1247,7 +1247,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "lnbits"
|
||||
version = "1.4.0rc2"
|
||||
version = "1.4.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiosqlite" },
|
||||
@@ -1342,7 +1342,7 @@ requires-dist = [
|
||||
{ name = "ecdsa", specifier = "==0.19.1" },
|
||||
{ name = "embit", specifier = "==0.8.0" },
|
||||
{ name = "fastapi", specifier = "==0.116.1" },
|
||||
{ name = "fastapi-sso", specifier = "==0.18.0" },
|
||||
{ name = "fastapi-sso", specifier = "==0.19.0" },
|
||||
{ name = "filetype", specifier = "==1.2.0" },
|
||||
{ name = "grpcio", specifier = "==1.69.0" },
|
||||
{ name = "httpx", specifier = "==0.27.0" },
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import {defineConfig} from 'vitest/config'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom'
|
||||
},
|
||||
define: {
|
||||
'process.env': {} // Replace process.env with an empty object for browser builds
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry: 'lnbits/static/components/main.js',
|
||||
name: 'LnbitsComponents',
|
||||
fileName: 'components', // Output file name (build.js)
|
||||
formats: ['umd']
|
||||
},
|
||||
minify: 'terser', // Optional: minify the output
|
||||
rollupOptions: {
|
||||
// If you have external dependencies, mark them here
|
||||
external: []
|
||||
},
|
||||
outDir: 'lnbits/static/modules' // Output folder
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user