chore: various tweaks
This commit is contained in:
parent
490e166f75
commit
dd23b20090
33
.env.example
33
.env.example
|
@ -1,20 +1,31 @@
|
||||||
FLASK_APP=lnbits
|
FLASK_APP=lnbits
|
||||||
FLASK_ENV=development
|
FLASK_ENV=development
|
||||||
|
|
||||||
|
LNBITS_SITE_TITLE=LNbits
|
||||||
LNBITS_WITH_ONION=0
|
LNBITS_WITH_ONION=0
|
||||||
LNBITS_DEFAULT_WALLET_NAME="LNbits wallet"
|
LNBITS_DEFAULT_WALLET_NAME="LNbits wallet"
|
||||||
LNBITS_FEE_RESERVE=0
|
LNBITS_FEE_RESERVE=0
|
||||||
|
|
||||||
LNBITS_BACKEND_WALLET_CLASS="LntxbotWallet"
|
LNBITS_BACKEND_WALLET_CLASS=LntxbotWallet
|
||||||
|
|
||||||
|
CLIGHTNING_RPC="/home/arc/.lightning/bitcoin/lightning-rpc"
|
||||||
|
|
||||||
|
LNBITS_ENDPOINT=127.0.0.1:5000
|
||||||
|
LNBITS_INVOICE_MACAROON=LNBITS_INVOICE_MACAROON
|
||||||
|
LNBITS_ADMIN_MACAROON=LNBITS_ADMIN_MACAROON
|
||||||
|
|
||||||
LND_GRPC_ENDPOINT=127.0.0.1
|
LND_GRPC_ENDPOINT=127.0.0.1
|
||||||
LND_GRPC_PORT=11009
|
LND_GRPC_PORT=11009
|
||||||
LND_CERT='/home/arc/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/tls.cert'
|
LND_CERT="/home/arc/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/tls.cert"
|
||||||
LND_ADMIN_MACAROON='/home/arc/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/admin.macaroon'
|
LND_ADMIN_MACAROON="/home/arc/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/admin.macaroon"
|
||||||
LND_INVOICE_MACAROON='/home/arc/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/invoice.macaroon'
|
LND_INVOICE_MACAROON="/home/arc/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/invoice.macaroon"
|
||||||
LND_READ_MACAROON='/home/arc/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/read.macaroon'
|
LND_READ_MACAROON="/home/arc/.config/Zap/lnd/bitcoin/mainnet/wallet-1/data/chain/bitcoin/mainnet/read.macaroon"
|
||||||
|
|
||||||
CLIGHTNING_RPC='/home/arc/.lightning/bitcoin/lightning-rpc'
|
LNPAY_API_ENDPOINT=https://lnpay.co/v1/
|
||||||
|
LNPAY_API_KEY=LNPAY_API_KEY
|
||||||
|
LNPAY_ADMIN_KEY=LNPAY_ADMIN_KEY
|
||||||
|
LNPAY_INVOICE_KEY=LNPAY_INVOICE_KEY
|
||||||
|
LNPAY_READ_KEY=LNPAY_READ_KEY
|
||||||
|
|
||||||
LNTXBOT_API_ENDPOINT=https://lntxbot.bigsun.xyz/
|
LNTXBOT_API_ENDPOINT=https://lntxbot.bigsun.xyz/
|
||||||
LNTXBOT_ADMIN_KEY=LNTXBOT_ADMIN_KEY
|
LNTXBOT_ADMIN_KEY=LNTXBOT_ADMIN_KEY
|
||||||
|
@ -23,13 +34,3 @@ LNTXBOT_INVOICE_KEY=LNTXBOT_INVOICE_KEY
|
||||||
OPENNODE_API_ENDPOINT=https://api.opennode.com/
|
OPENNODE_API_ENDPOINT=https://api.opennode.com/
|
||||||
OPENNODE_ADMIN_KEY=OPENNODE_ADMIN_KEY
|
OPENNODE_ADMIN_KEY=OPENNODE_ADMIN_KEY
|
||||||
OPENNODE_INVOICE_KEY=OPENNODE_INVOICE_KEY
|
OPENNODE_INVOICE_KEY=OPENNODE_INVOICE_KEY
|
||||||
|
|
||||||
LNPAY_API_ENDPOINT=https://lnpay.co/v1/
|
|
||||||
LNPAY_API_KEY=LNPAY_API_KEY
|
|
||||||
LNPAY_ADMIN_KEY=LNPAY_ADMIN_KEY
|
|
||||||
LNPAY_INVOICE_KEY=LNPAY_INVOICE_KEY
|
|
||||||
LNPAY_READ_KEY=LNPAY_READ_KEY
|
|
||||||
|
|
||||||
LNBITS_ENDPOINT=127.0.0.1:5000
|
|
||||||
LNBITS_INVOICE_MACAROON=LNBITS_INVOICE_MACAROON
|
|
||||||
LNBITS_ADMIN_MACAROON=LNBITS_ADMIN_MACAROON
|
|
||||||
|
|
1
Pipfile
1
Pipfile
|
@ -23,6 +23,7 @@ gunicorn = "*"
|
||||||
pylightning = "*"
|
pylightning = "*"
|
||||||
pyscss = "*"
|
pyscss = "*"
|
||||||
requests = "*"
|
requests = "*"
|
||||||
|
shortuuid = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
black = "==19.10b0"
|
black = "==19.10b0"
|
||||||
|
|
|
@ -58,6 +58,7 @@ for ext in valid_extensions:
|
||||||
|
|
||||||
app.jinja_env.globals["DEBUG"] = app.config["DEBUG"]
|
app.jinja_env.globals["DEBUG"] = app.config["DEBUG"]
|
||||||
app.jinja_env.globals["EXTENSIONS"] = valid_extensions
|
app.jinja_env.globals["EXTENSIONS"] = valid_extensions
|
||||||
|
app.jinja_env.globals["SITE_TITLE"] = getenv("LNBITS_SITE_TITLE", "LNbits")
|
||||||
app.jinja_env.filters["megajson"] = megajson
|
app.jinja_env.filters["megajson"] = megajson
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from uuid import uuid4
|
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
from lnbits.db import open_db
|
from lnbits.db import open_db
|
||||||
from lnbits.settings import DEFAULT_WALLET_NAME, FEE_RESERVE
|
from lnbits.settings import DEFAULT_WALLET_NAME, FEE_RESERVE
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from typing import Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
from lnbits.bolt11 import decode as bolt11_decode
|
from lnbits.bolt11 import decode as bolt11_decode
|
||||||
from lnbits.settings import WALLET, FEE_RESERVE
|
from lnbits.settings import WALLET, FEE_RESERVE
|
||||||
|
|
||||||
from .crud import create_payment
|
from .crud import create_payment
|
||||||
|
from .models import Wallet
|
||||||
|
|
||||||
|
|
||||||
def create_invoice(*, wallet_id: str, amount: int, memo: str) -> Tuple[str, str]:
|
def create_invoice(*, wallet_id: str, amount: int, memo: str) -> Tuple[str, str]:
|
||||||
|
@ -21,14 +22,23 @@ def create_invoice(*, wallet_id: str, amount: int, memo: str) -> Tuple[str, str]
|
||||||
return checking_id, payment_request
|
return checking_id, payment_request
|
||||||
|
|
||||||
|
|
||||||
def pay_invoice(*, wallet_id: str, bolt11: str) -> str:
|
def pay_invoice(*, wallet: Wallet, bolt11: str, max_sat: Optional[int] = None) -> str:
|
||||||
try:
|
try:
|
||||||
invoice = bolt11_decode(bolt11)
|
invoice = bolt11_decode(bolt11)
|
||||||
ok, checking_id, fee_msat, error_message = WALLET.pay_invoice(bolt11)
|
ok, checking_id, fee_msat, error_message = WALLET.pay_invoice(bolt11)
|
||||||
|
|
||||||
|
if invoice.amount_msat == 0:
|
||||||
|
raise ValueError("Amountless invoices not supported.")
|
||||||
|
|
||||||
|
if max_sat and invoice.amount_msat > max_sat * 1000:
|
||||||
|
raise ValueError("Amount in invoice is too high.")
|
||||||
|
|
||||||
|
if invoice.amount_msat > wallet.balance_msat:
|
||||||
|
raise PermissionError("Insufficient balance.")
|
||||||
|
|
||||||
if ok:
|
if ok:
|
||||||
create_payment(
|
create_payment(
|
||||||
wallet_id=wallet_id,
|
wallet_id=wallet.id,
|
||||||
checking_id=checking_id,
|
checking_id=checking_id,
|
||||||
amount=-invoice.amount_msat,
|
amount=-invoice.amount_msat,
|
||||||
memo=invoice.description,
|
memo=invoice.description,
|
||||||
|
@ -42,3 +52,7 @@ def pay_invoice(*, wallet_id: str, bolt11: str) -> str:
|
||||||
raise Exception(error_message or "Unexpected backend error.")
|
raise Exception(error_message or "Unexpected backend error.")
|
||||||
|
|
||||||
return checking_id
|
return checking_id
|
||||||
|
|
||||||
|
|
||||||
|
def check_payment(*, checking_id: str) -> str:
|
||||||
|
pass
|
|
@ -35,7 +35,7 @@
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
<q-expansion-item group="api" dense expand-separator label="Check an invoice (incoming or outgoing)"
|
<q-expansion-item group="api" dense expand-separator label="Check an invoice (incoming or outgoing)"
|
||||||
class="q-mb-md">
|
class="q-pb-md">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<code><span class="text-light-blue">GET</span> /api/v1/payments/<checking_id></code>
|
<code><span class="text-light-blue">GET</span> /api/v1/payments/<checking_id></code>
|
||||||
|
|
|
@ -8,12 +8,9 @@
|
||||||
{% endassets %}
|
{% endassets %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block drawer %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block page %}
|
{% block page %}
|
||||||
<div class="row q-col-gutter-md justify-between">
|
<div class="row q-col-gutter-md justify-between">
|
||||||
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
<div class="col-12 col-md-7 q-gutter-y-md">
|
||||||
|
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{ window_vars(user, wallet) }}
|
{{ window_vars(user, wallet) }}
|
||||||
|
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
|
||||||
{% assets filters='rjsmin', output='__bundle__/core/chart.js',
|
{% assets filters='rjsmin', output='__bundle__/core/chart.js',
|
||||||
'vendor/moment@2.24.0/moment.min.js',
|
'vendor/moment@2.24.0/moment.min.js',
|
||||||
'vendor/chart.js@2.9.3/chart.min.js' %}
|
'vendor/chart.js@2.9.3/chart.min.js' %}
|
||||||
|
@ -17,7 +18,6 @@
|
||||||
{% assets filters='rjsmin', output='__bundle__/core/wallet.js',
|
{% assets filters='rjsmin', output='__bundle__/core/wallet.js',
|
||||||
'vendor/bolt11/utils.js',
|
'vendor/bolt11/utils.js',
|
||||||
'vendor/bolt11/decoder.js',
|
'vendor/bolt11/decoder.js',
|
||||||
'vendor/vue-qrcode@1.0.2/vue-qrcode.min.js',
|
|
||||||
'vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.js',
|
'vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.js',
|
||||||
'core/js/wallet.js' %}
|
'core/js/wallet.js' %}
|
||||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
{% block page %}
|
{% block page %}
|
||||||
<div class="row q-col-gutter-md">
|
<div class="row q-col-gutter-md">
|
||||||
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
<div class="col-12 col-md-7 q-gutter-y-md">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<h3 class="q-my-none"><strong>{% raw %}{{ fbalance }}{% endraw %}</strong> sat</h3>
|
<h3 class="q-my-none"><strong>{% raw %}{{ fbalance }}{% endraw %}</strong> sat</h3>
|
||||||
|
@ -105,9 +105,10 @@
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
|
<div class="col-12 col-md-5 q-gutter-y-md">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
|
<q-btn flat color="grey" @click="exportCSV" class="float-right">Renew keys</q-btn>
|
||||||
<h6 class="text-subtitle1 q-mt-none q-mb-sm">LNbits wallet</h6>
|
<h6 class="text-subtitle1 q-mt-none q-mb-sm">LNbits wallet</h6>
|
||||||
<strong>Wallet name: </strong><em>{{ wallet.name }}</em><br>
|
<strong>Wallet name: </strong><em>{{ wallet.name }}</em><br>
|
||||||
<strong>Wallet ID: </strong><em>{{ wallet.id }}</em><br>
|
<strong>Wallet ID: </strong><em>{{ wallet.id }}</em><br>
|
||||||
|
@ -139,12 +140,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
|
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
|
||||||
<q-card class="q-pa-lg q-pt-xl" style="width: 500px">
|
<q-card v-if="!receive.paymentReq" class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
<q-form v-if="!receive.paymentReq" class="q-gutter-md">
|
<q-form class="q-gutter-md">
|
||||||
<q-input filled dense
|
<q-input filled dense
|
||||||
v-model.number="receive.data.amount"
|
v-model.number="receive.data.amount"
|
||||||
type="number"
|
type="number"
|
||||||
label="Amount *"></q-input>
|
label="Amount (sat) *"></q-input>
|
||||||
<q-input filled dense
|
<q-input filled dense
|
||||||
v-model.trim="receive.data.memo"
|
v-model.trim="receive.data.memo"
|
||||||
label="Memo"
|
label="Memo"
|
||||||
|
@ -158,26 +159,24 @@
|
||||||
</div>
|
</div>
|
||||||
<q-spinner v-if="receive.status == 'loading'" color="deep-purple" size="2.55em"></q-spinner>
|
<q-spinner v-if="receive.status == 'loading'" color="deep-purple" size="2.55em"></q-spinner>
|
||||||
</q-form>
|
</q-form>
|
||||||
<div v-else>
|
</q-card>
|
||||||
<div class="text-center q-mb-md">
|
<q-card v-else class="q-pa-lg lnbits__dialog-card">
|
||||||
<a :href="'lightning:' + receive.paymentReq">
|
<div class="text-center q-mb-md">
|
||||||
|
<a :href="'lightning:' + receive.paymentReq">
|
||||||
|
<q-responsive :ratio="1" class="q-mx-xl">
|
||||||
<qrcode :value="receive.paymentReq" :options="{width: 340}" class="rounded-borders"></qrcode>
|
<qrcode :value="receive.paymentReq" :options="{width: 340}" class="rounded-borders"></qrcode>
|
||||||
</a>
|
</q-responsive>
|
||||||
</div>
|
</a>
|
||||||
<!--<q-separator class="q-my-md"></q-separator>
|
</div>
|
||||||
<p class="text-caption" style="word-break: break-all">
|
<div class="row justify-between">
|
||||||
{% raw %}{{ receive.paymentReq }}{% endraw %}
|
<q-btn flat color="grey" @click="copyText(receive.paymentReq)">Copy invoice</q-btn>
|
||||||
</p>-->
|
<q-btn v-close-popup flat color="grey">Close</q-btn>
|
||||||
<div class="row justify-between">
|
|
||||||
<q-btn flat color="grey" @click="copyText(receive.paymentReq)">Copy invoice</q-btn>
|
|
||||||
<q-btn v-close-popup flat color="grey">Close</q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
<q-dialog v-model="send.show" position="top" @hide="closeSendDialog">
|
<q-dialog v-model="send.show" position="top" @hide="closeSendDialog">
|
||||||
<q-card class="q-pa-lg q-pt-xl" style="width: 500px">
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
<div v-if="!send.invoice">
|
<div v-if="!send.invoice">
|
||||||
<q-form v-if="!sendCamera.show" class="q-gutter-md">
|
<q-form v-if="!sendCamera.show" class="q-gutter-md">
|
||||||
<q-input filled dense
|
<q-input filled dense
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
from flask import g, jsonify, request
|
from flask import g, jsonify, request
|
||||||
|
|
||||||
from lnbits.bolt11 import decode as bolt11_decode
|
|
||||||
from lnbits.core import core_app
|
from lnbits.core import core_app
|
||||||
from lnbits.decorators import api_check_wallet_macaroon, api_validate_post_request
|
from lnbits.decorators import api_check_wallet_macaroon, api_validate_post_request
|
||||||
from lnbits.helpers import Status
|
from lnbits.helpers import Status
|
||||||
from lnbits.settings import WALLET
|
from lnbits.settings import WALLET
|
||||||
|
|
||||||
from ..utils import create_invoice, pay_invoice
|
from ..services import create_invoice, pay_invoice
|
||||||
|
|
||||||
|
|
||||||
@core_app.route("/api/v1/payments", methods=["GET"])
|
@core_app.route("/api/v1/payments", methods=["GET"])
|
||||||
|
@ -44,16 +43,11 @@ def api_payments_create_invoice():
|
||||||
@api_validate_post_request(schema={"bolt11": {"type": "string", "empty": False, "required": True}})
|
@api_validate_post_request(schema={"bolt11": {"type": "string", "empty": False, "required": True}})
|
||||||
def api_payments_pay_invoice():
|
def api_payments_pay_invoice():
|
||||||
try:
|
try:
|
||||||
invoice = bolt11_decode(g.data["bolt11"])
|
checking_id = pay_invoice(wallet=g.wallet, bolt11=g.data["bolt11"])
|
||||||
|
except ValueError as e:
|
||||||
if invoice.amount_msat == 0:
|
return jsonify({"message": str(e)}), Status.BAD_REQUEST
|
||||||
return jsonify({"message": "Amountless invoices not supported."}), Status.BAD_REQUEST
|
except PermissionError as e:
|
||||||
|
return jsonify({"message": str(e)}), Status.FORBIDDEN
|
||||||
if invoice.amount_msat > g.wallet.balance_msat:
|
|
||||||
return jsonify({"message": "Insufficient balance."}), Status.FORBIDDEN
|
|
||||||
|
|
||||||
checking_id = pay_invoice(wallet_id=g.wallet.id, bolt11=g.data["bolt11"])
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"message": str(e)}), Status.INTERNAL_SERVER_ERROR
|
return jsonify({"message": str(e)}), Status.INTERNAL_SERVER_ERROR
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
"name": "AMilk",
|
"name": "AMilk",
|
||||||
"short_description": "Assistant Faucet Milker",
|
"short_description": "Assistant Faucet Milker",
|
||||||
"icon": "room_service",
|
"icon": "room_service",
|
||||||
"contributors": ["eillarra"]
|
"contributors": ["arcbtc"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<q-dialog v-model="amilkDialog.show" position="top">
|
<q-dialog v-model="amilkDialog.show" position="top">
|
||||||
<q-card class="q-pa-lg q-pt-xl" style="width: 500px">
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
<q-form class="q-gutter-md">
|
<q-form class="q-gutter-md">
|
||||||
<q-select filled dense emit-value v-model="amilkDialog.data.wallet" :options="g.user.walletOptions" label="Wallet *">
|
<q-select filled dense emit-value v-model="amilkDialog.data.wallet" :options="g.user.walletOptions" label="Wallet *">
|
||||||
</q-select>
|
</q-select>
|
||||||
|
|
6
lnbits/extensions/events/config.json.wip
Normal file
6
lnbits/extensions/events/config.json.wip
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "Events",
|
||||||
|
"short_description": "LN tickets for events.",
|
||||||
|
"icon": "local_activity",
|
||||||
|
"contributors": ["arcbtc"]
|
||||||
|
}
|
5
lnbits/extensions/events/migrations.py
Normal file
5
lnbits/extensions/events/migrations.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from lnbits.db import open_ext_db
|
||||||
|
|
||||||
|
|
||||||
|
def migrate():
|
||||||
|
print("pending")
|
|
@ -1,15 +1,14 @@
|
||||||
from base64 import urlsafe_b64encode
|
|
||||||
from uuid import uuid4
|
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
from lnbits.db import open_ext_db
|
from lnbits.db import open_ext_db
|
||||||
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
||||||
from .models import Paywall
|
from .models import Paywall
|
||||||
|
|
||||||
|
|
||||||
def create_paywall(*, wallet_id: str, url: str, memo: str, amount: int) -> Paywall:
|
def create_paywall(*, wallet_id: str, url: str, memo: str, amount: int) -> Paywall:
|
||||||
with open_ext_db("paywall") as db:
|
with open_ext_db("paywall") as db:
|
||||||
paywall_id = urlsafe_b64encode(uuid4().bytes_le).decode('utf-8')
|
paywall_id = urlsafe_short_hash()
|
||||||
db.execute(
|
db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO paywalls (id, wallet, url, memo, amount)
|
INSERT INTO paywalls (id, wallet, url, memo, amount)
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
from base64 import urlsafe_b64encode
|
|
||||||
from uuid import uuid4
|
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
from lnbits.db import open_ext_db
|
from lnbits.db import open_ext_db
|
||||||
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
||||||
from .models import TPoS
|
from .models import TPoS
|
||||||
|
|
||||||
|
|
||||||
def create_tpos(*, wallet_id: str, name: str, currency: str) -> TPoS:
|
def create_tpos(*, wallet_id: str, name: str, currency: str) -> TPoS:
|
||||||
with open_ext_db("tpos") as db:
|
with open_ext_db("tpos") as db:
|
||||||
tpos_id = urlsafe_b64encode(uuid4().bytes_le).decode('utf-8')
|
tpos_id = urlsafe_short_hash()
|
||||||
db.execute(
|
db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO tposs (id, wallet, name, currency)
|
INSERT INTO tposs (id, wallet, name, currency)
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from flask import g, abort, render_template
|
|
||||||
from lnbits.core.crud import get_wallet
|
|
||||||
|
|
||||||
from lnbits.decorators import check_user_exists, validate_uuids
|
|
||||||
from lnbits.extensions.tpos import tpos_ext
|
|
||||||
from lnbits.helpers import Status
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
|
||||||
|
|
||||||
|
from flask import g, abort, render_template
|
||||||
|
|
||||||
|
from lnbits.core.crud import get_wallet
|
||||||
|
from lnbits.decorators import check_user_exists, validate_uuids
|
||||||
|
from lnbits.helpers import Status
|
||||||
|
|
||||||
|
from lnbits.extensions.tpos import tpos_ext
|
||||||
from .crud import get_tpos
|
from .crud import get_tpos
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
from flask import g, jsonify, request
|
from flask import g, jsonify, request
|
||||||
|
|
||||||
from lnbits.core.crud import get_user
|
from lnbits.core.crud import get_user
|
||||||
from lnbits.core.utils import create_invoice
|
from lnbits.core.services import create_invoice
|
||||||
from lnbits.decorators import api_check_wallet_macaroon, api_validate_post_request
|
from lnbits.decorators import api_check_wallet_macaroon, api_validate_post_request
|
||||||
from lnbits.helpers import Status
|
from lnbits.helpers import Status
|
||||||
from lnbits.settings import WALLET, FEE_RESERVE
|
from lnbits.settings import WALLET
|
||||||
from lnbits.extensions.tpos import tpos_ext
|
|
||||||
|
|
||||||
|
from lnbits.extensions.tpos import tpos_ext
|
||||||
from .crud import create_tpos, get_tpos, get_tposs, delete_tpos
|
from .crud import create_tpos, get_tpos, get_tposs, delete_tpos
|
||||||
|
|
||||||
|
|
||||||
@tpos_ext.route("/api/v1/tposs", methods=["GET"])
|
@tpos_ext.route("/api/v1/tposs", methods=["GET"])
|
||||||
@api_check_wallet_macaroon(key_type="invoice")
|
@api_check_wallet_macaroon(key_type="invoice")
|
||||||
def api_tposs():
|
def api_tposs():
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import shortuuid
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from typing import List, NamedTuple, Optional
|
from typing import List, NamedTuple, Optional
|
||||||
|
@ -52,6 +53,10 @@ class Status:
|
||||||
INTERNAL_SERVER_ERROR = 500
|
INTERNAL_SERVER_ERROR = 500
|
||||||
|
|
||||||
|
|
||||||
|
def urlsafe_short_hash() -> str:
|
||||||
|
return shortuuid.uuid()
|
||||||
|
|
||||||
|
|
||||||
class MegaEncoder(json.JSONEncoder):
|
class MegaEncoder(json.JSONEncoder):
|
||||||
def default(self, obj):
|
def default(self, obj):
|
||||||
if isinstance(obj, sqlite3.Row):
|
if isinstance(obj, sqlite3.Row):
|
||||||
|
|
|
@ -29,12 +29,15 @@
|
||||||
color: inherit;
|
color: inherit;
|
||||||
font-weight: bold; }
|
font-weight: bold; }
|
||||||
|
|
||||||
.q-table--dense th:first-child, .q-table--dense td:first-child,
|
.lnbits__dialog-card {
|
||||||
.q-table--dense .q-table__bottom {
|
width: 500px; }
|
||||||
padding-left: 6px !important; }
|
|
||||||
.q-table--dense th:last-child, .q-table--dense td:last-child,
|
.q-table--dense th:first-child, .q-table--dense td:first-child,
|
||||||
.q-table--dense .q-table__bottom {
|
.q-table--dense .q-table__bottom {
|
||||||
padding-right: 6px !important; }
|
padding-left: 6px !important; }
|
||||||
|
.q-table--dense th:last-child, .q-table--dense td:last-child,
|
||||||
|
.q-table--dense .q-table__bottom {
|
||||||
|
padding-right: 6px !important; }
|
||||||
|
|
||||||
video {
|
video {
|
||||||
border-radius: 3px; }
|
border-radius: 3px; }
|
||||||
|
|
|
@ -91,13 +91,17 @@ Vue.component('lnbits-extension-list', {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<q-list v-if="user" dense class="lnbits-drawer__q-list">
|
<q-list v-if="user && extensions.length" dense class="lnbits-drawer__q-list">
|
||||||
<q-item-label header>Extensions</q-item-label>
|
<q-item-label header>Extensions</q-item-label>
|
||||||
<q-item v-for="extension in userExtensions" :key="extension.code"
|
<q-item v-for="extension in userExtensions" :key="extension.code"
|
||||||
clickable
|
clickable
|
||||||
|
:active="extension.isActive"
|
||||||
tag="a" :href="[extension.url, '?usr=', user.id].join('')">
|
tag="a" :href="[extension.url, '?usr=', user.id].join('')">
|
||||||
<q-item-section side>
|
<q-item-section side>
|
||||||
<q-avatar size="md" color="grey-5">
|
<q-avatar size="md"
|
||||||
|
:color="(extension.isActive)
|
||||||
|
? (($q.dark.isActive) ? 'deep-purple-5' : 'deep-purple')
|
||||||
|
: 'grey-5'">
|
||||||
<q-icon :name="extension.icon" :size="($q.dark.isActive) ? '21px' : '20px'"
|
<q-icon :name="extension.icon" :size="($q.dark.isActive) ? '21px' : '20px'"
|
||||||
:color="($q.dark.isActive) ? 'blue-grey-10' : 'grey-3'"></q-icon>
|
:color="($q.dark.isActive) ? 'blue-grey-10' : 'grey-3'"></q-icon>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
|
@ -105,6 +109,9 @@ Vue.component('lnbits-extension-list', {
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label lines="1">{{ extension.name }}</q-item-label>
|
<q-item-label lines="1">{{ extension.name }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
|
<q-item-section side v-show="extension.isActive">
|
||||||
|
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
|
||||||
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item clickable tag="a" :href="['/extensions?usr=', user.id].join('')">
|
<q-item clickable tag="a" :href="['/extensions?usr=', user.id].join('')">
|
||||||
<q-item-section side>
|
<q-item-section side>
|
||||||
|
@ -119,18 +126,26 @@ Vue.component('lnbits-extension-list', {
|
||||||
computed: {
|
computed: {
|
||||||
userExtensions: function () {
|
userExtensions: function () {
|
||||||
if (!this.user) return [];
|
if (!this.user) return [];
|
||||||
|
|
||||||
|
var path = window.location.pathname;
|
||||||
var userExtensions = this.user.extensions;
|
var userExtensions = this.user.extensions;
|
||||||
|
|
||||||
return this.extensions.filter(function (obj) {
|
return this.extensions.filter(function (obj) {
|
||||||
return userExtensions.indexOf(obj.code) !== -1;
|
return userExtensions.indexOf(obj.code) !== -1;
|
||||||
|
}).map(function (obj) {
|
||||||
|
obj.isActive = path.startsWith(obj.url);
|
||||||
|
return obj;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: function () {
|
created: function () {
|
||||||
this.extensions = window.extensions.map(function (data) {
|
if (window.extensions) {
|
||||||
return LNbits.map.extension(data);
|
this.extensions = window.extensions.map(function (data) {
|
||||||
}).sort(function (a, b) {
|
return LNbits.map.extension(data);
|
||||||
return a.name.localeCompare(b.name);
|
}).sort(function (a, b) {
|
||||||
});
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (window.user) {
|
if (window.user) {
|
||||||
this.user = LNbits.map.user(window.user);
|
this.user = LNbits.map.user(window.user);
|
||||||
|
|
|
@ -48,6 +48,10 @@ body.body--dark .q-field--error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lnbits__dialog-card {
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
.q-table--dense {
|
.q-table--dense {
|
||||||
th:first-child,
|
th:first-child,
|
||||||
td:first-child,
|
td:first-child,
|
||||||
|
|
|
@ -8,7 +8,11 @@
|
||||||
<link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}">
|
<link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}">
|
||||||
{% endassets %}
|
{% endassets %}
|
||||||
{% block styles %}{% endblock %}
|
{% block styles %}{% endblock %}
|
||||||
<title>{% block title %}LNbits{% endblock %}</title>
|
<title>
|
||||||
|
{% block title %}
|
||||||
|
{% if SITE_TITLE != 'LNbits' %}{{ SITE_TITLE }}{% else %}LNbits{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
{% block head_scripts %}{% endblock %}
|
{% block head_scripts %}{% endblock %}
|
||||||
|
@ -19,23 +23,31 @@
|
||||||
|
|
||||||
<q-header bordered class="bg-lnbits-dark">
|
<q-header bordered class="bg-lnbits-dark">
|
||||||
<q-toolbar>
|
<q-toolbar>
|
||||||
<q-btn dense flat round icon="menu" @click="g.visibleDrawer = !g.visibleDrawer"></q-btn>
|
{% block drawer_toggle %}
|
||||||
<q-toolbar-title><strong>LN</strong>bits</q-toolbar-title>
|
<q-btn dense flat round icon="menu" @click="g.visibleDrawer = !g.visibleDrawer"></q-btn>
|
||||||
<q-badge color="yellow" text-color="black">
|
{% endblock %}
|
||||||
<span><span v-show="$q.screen.gt.sm">USE WITH CAUTION - LNbits wallet is still in </span>BETA</span>
|
{% if SITE_TITLE != 'LNbits' %}
|
||||||
</q-badge>
|
<q-toolbar-title>{{ SITE_TITLE }}</q-toolbar-title>
|
||||||
|
{% else %}
|
||||||
|
<q-toolbar-title><strong>LN</strong>bits</q-toolbar-title>
|
||||||
|
{% endif %}
|
||||||
|
{% block beta %}
|
||||||
|
<q-badge color="yellow" text-color="black">
|
||||||
|
<span><span v-show="$q.screen.gt.sm">USE WITH CAUTION - LNbits wallet is still in </span>BETA</span>
|
||||||
|
</q-badge>
|
||||||
|
{% endblock %}
|
||||||
<q-btn dense flat round @click="toggleDarkMode" :icon="($q.dark.isActive) ? 'brightness_3' : 'wb_sunny'" class="q-ml-lg" size="sm">
|
<q-btn dense flat round @click="toggleDarkMode" :icon="($q.dark.isActive) ? 'brightness_3' : 'wb_sunny'" class="q-ml-lg" size="sm">
|
||||||
<q-tooltip>Toggle Dark Mode</q-tooltip>
|
<q-tooltip>Toggle Dark Mode</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
</q-header>
|
</q-header>
|
||||||
|
|
||||||
<q-drawer v-model="g.visibleDrawer" side="left" :width="($q.screen.lt.md) ? 260 : 230" show-if-above :elevated="$q.screen.lt.md">
|
{% block drawer %}
|
||||||
{% block drawer %}
|
<q-drawer v-model="g.visibleDrawer" side="left" :width="($q.screen.lt.md) ? 260 : 230" show-if-above :elevated="$q.screen.lt.md">
|
||||||
<lnbits-wallet-list></lnbits-wallet-list>
|
<lnbits-wallet-list></lnbits-wallet-list>
|
||||||
<lnbits-extension-list class="q-pb-xl"></lnbits-extension-list>
|
<lnbits-extension-list class="q-pb-xl"></lnbits-extension-list>
|
||||||
{% endblock %}
|
</q-drawer>
|
||||||
</q-drawer>
|
{% endblock %}
|
||||||
|
|
||||||
<q-page-container>
|
<q-page-container>
|
||||||
<q-page class="q-px-md q-py-lg" :class="{'q-px-lg': $q.screen.gt.xs}">
|
<q-page class="q-px-md q-py-lg" :class="{'q-px-lg': $q.screen.gt.xs}">
|
||||||
|
|
|
@ -56,5 +56,5 @@ class OpenNodeWallet(Wallet):
|
||||||
if not r.ok:
|
if not r.ok:
|
||||||
return PaymentStatus(None)
|
return PaymentStatus(None)
|
||||||
|
|
||||||
statuses = {"pending": None, "confirmed": True, "error": False, "failed": False}
|
statuses = {"initial": None, "pending": None, "confirmed": True, "error": False, "failed": False}
|
||||||
return PaymentStatus(statuses[r.json()["data"]["status"]])
|
return PaymentStatus(statuses[r.json()["data"]["status"]])
|
||||||
|
|
|
@ -18,7 +18,7 @@ grpcio==1.28.1
|
||||||
gunicorn==20.0.4
|
gunicorn==20.0.4
|
||||||
idna==2.9
|
idna==2.9
|
||||||
itsdangerous==1.1.0
|
itsdangerous==1.1.0
|
||||||
jinja2==2.11.1
|
jinja2==2.11.2
|
||||||
limits==1.5.1
|
limits==1.5.1
|
||||||
lnd-grpc==0.4.0
|
lnd-grpc==0.4.0
|
||||||
lnurl==0.3.3
|
lnurl==0.3.3
|
||||||
|
@ -28,6 +28,7 @@ pydantic==1.4
|
||||||
pylightning==0.0.7.3
|
pylightning==0.0.7.3
|
||||||
pyscss==1.3.7
|
pyscss==1.3.7
|
||||||
requests==2.23.0
|
requests==2.23.0
|
||||||
|
shortuuid==1.0.1
|
||||||
six==1.14.0
|
six==1.14.0
|
||||||
typing-extensions==3.7.4.2 ; python_version < '3.8'
|
typing-extensions==3.7.4.2 ; python_version < '3.8'
|
||||||
urllib3==1.25.8
|
urllib3==1.25.8
|
||||||
|
|
Loading…
Reference in New Issue
Block a user