migrate to sqlalchemy-aio.

a big refactor that:

- fixes some issues that might have happened (or not) with asynchronous
    reactions to payments;
- paves the way to https://github.com/lnbits/lnbits/issues/121;
- uses more async/await notation which just looks nice; and
- makes it simple(r?) for one extension to modify stuff from other extensions.
This commit is contained in:
fiatjaf
2020-11-21 23:02:14 -03:00
parent f877dde2b0
commit d3fc52cd49
68 changed files with 971 additions and 1075 deletions
+2
View File
@@ -1,5 +1,7 @@
from quart import Blueprint
from lnbits.db import Database
db = Database("database")
core_app: Blueprint = Blueprint(
"core", __name__, template_folder="templates", static_folder="static", static_url_path="/core/static"
+39 -39
View File
@@ -2,11 +2,11 @@ import json
import datetime
from uuid import uuid4
from typing import List, Optional, Dict
from quart import g
from lnbits import bolt11
from lnbits.settings import DEFAULT_WALLET_NAME
from . import db
from .models import User, Wallet, Payment
@@ -14,28 +14,28 @@ from .models import User, Wallet, Payment
# --------
def create_account() -> User:
async def create_account() -> User:
user_id = uuid4().hex
g.db.execute("INSERT INTO accounts (id) VALUES (?)", (user_id,))
await db.execute("INSERT INTO accounts (id) VALUES (?)", (user_id,))
new_account = get_account(user_id=user_id)
new_account = await get_account(user_id=user_id)
assert new_account, "Newly created account couldn't be retrieved"
return new_account
def get_account(user_id: str) -> Optional[User]:
row = g.db.fetchone("SELECT id, email, pass as password FROM accounts WHERE id = ?", (user_id,))
async def get_account(user_id: str) -> Optional[User]:
row = await db.fetchone("SELECT id, email, pass as password FROM accounts WHERE id = ?", (user_id,))
return User(**row) if row else None
def get_user(user_id: str) -> Optional[User]:
user = g.db.fetchone("SELECT id, email FROM accounts WHERE id = ?", (user_id,))
async def get_user(user_id: str) -> Optional[User]:
user = await db.fetchone("SELECT id, email FROM accounts WHERE id = ?", (user_id,))
if user:
extensions = g.db.fetchall("SELECT extension FROM extensions WHERE user = ? AND active = 1", (user_id,))
wallets = g.db.fetchall(
extensions = await db.fetchall("SELECT extension FROM extensions WHERE user = ? AND active = 1", (user_id,))
wallets = await db.fetchall(
"""
SELECT *, COALESCE((SELECT balance FROM balances WHERE wallet = wallets.id), 0) AS balance_msat
FROM wallets
@@ -51,8 +51,8 @@ def get_user(user_id: str) -> Optional[User]:
)
def update_user_extension(*, user_id: str, extension: str, active: int) -> None:
g.db.execute(
async def update_user_extension(*, user_id: str, extension: str, active: int) -> None:
await db.execute(
"""
INSERT OR REPLACE INTO extensions (user, extension, active)
VALUES (?, ?, ?)
@@ -65,9 +65,9 @@ def update_user_extension(*, user_id: str, extension: str, active: int) -> None:
# -------
def create_wallet(*, user_id: str, wallet_name: Optional[str] = None) -> Wallet:
async def create_wallet(*, user_id: str, wallet_name: Optional[str] = None) -> Wallet:
wallet_id = uuid4().hex
g.db.execute(
await db.execute(
"""
INSERT INTO wallets (id, name, user, adminkey, inkey)
VALUES (?, ?, ?, ?, ?)
@@ -75,14 +75,14 @@ def create_wallet(*, user_id: str, wallet_name: Optional[str] = None) -> Wallet:
(wallet_id, wallet_name or DEFAULT_WALLET_NAME, user_id, uuid4().hex, uuid4().hex),
)
new_wallet = get_wallet(wallet_id=wallet_id)
new_wallet = await get_wallet(wallet_id=wallet_id)
assert new_wallet, "Newly created wallet couldn't be retrieved"
return new_wallet
def delete_wallet(*, user_id: str, wallet_id: str) -> None:
g.db.execute(
async def delete_wallet(*, user_id: str, wallet_id: str) -> None:
await db.execute(
"""
UPDATE wallets AS w
SET
@@ -95,8 +95,8 @@ def delete_wallet(*, user_id: str, wallet_id: str) -> None:
)
def get_wallet(wallet_id: str) -> Optional[Wallet]:
row = g.db.fetchone(
async def get_wallet(wallet_id: str) -> Optional[Wallet]:
row = await db.fetchone(
"""
SELECT *, COALESCE((SELECT balance FROM balances WHERE wallet = wallets.id), 0) AS balance_msat
FROM wallets
@@ -108,8 +108,8 @@ def get_wallet(wallet_id: str) -> Optional[Wallet]:
return Wallet(**row) if row else None
def get_wallet_for_key(key: str, key_type: str = "invoice") -> Optional[Wallet]:
row = g.db.fetchone(
async def get_wallet_for_key(key: str, key_type: str = "invoice") -> Optional[Wallet]:
row = await db.fetchone(
"""
SELECT *, COALESCE((SELECT balance FROM balances WHERE wallet = wallets.id), 0) AS balance_msat
FROM wallets
@@ -131,8 +131,8 @@ def get_wallet_for_key(key: str, key_type: str = "invoice") -> Optional[Wallet]:
# ---------------
def get_standalone_payment(checking_id: str) -> Optional[Payment]:
row = g.db.fetchone(
async def get_standalone_payment(checking_id: str) -> Optional[Payment]:
row = await db.fetchone(
"""
SELECT *
FROM apipayments
@@ -144,8 +144,8 @@ def get_standalone_payment(checking_id: str) -> Optional[Payment]:
return Payment.from_row(row) if row else None
def get_wallet_payment(wallet_id: str, payment_hash: str) -> Optional[Payment]:
row = g.db.fetchone(
async def get_wallet_payment(wallet_id: str, payment_hash: str) -> Optional[Payment]:
row = await db.fetchone(
"""
SELECT *
FROM apipayments
@@ -157,7 +157,7 @@ def get_wallet_payment(wallet_id: str, payment_hash: str) -> Optional[Payment]:
return Payment.from_row(row) if row else None
def get_wallet_payments(
async def get_wallet_payments(
wallet_id: str,
*,
complete: bool = False,
@@ -197,7 +197,7 @@ def get_wallet_payments(
clause += "AND checking_id NOT LIKE 'temp_%' "
clause += "AND checking_id NOT LIKE 'internal_%' "
rows = g.db.fetchall(
rows = await db.fetchall(
f"""
SELECT *
FROM apipayments
@@ -210,8 +210,8 @@ def get_wallet_payments(
return [Payment.from_row(row) for row in rows]
def delete_expired_invoices() -> None:
rows = g.db.fetchall(
async def delete_expired_invoices() -> None:
rows = await db.fetchall(
"""
SELECT bolt11
FROM apipayments
@@ -228,7 +228,7 @@ def delete_expired_invoices() -> None:
if expiration_date > datetime.datetime.utcnow():
continue
g.db.execute(
await db.execute(
"""
DELETE FROM apipayments
WHERE pending = 1 AND hash = ?
@@ -241,7 +241,7 @@ def delete_expired_invoices() -> None:
# --------
def create_payment(
async def create_payment(
*,
wallet_id: str,
checking_id: str,
@@ -254,7 +254,7 @@ def create_payment(
pending: bool = True,
extra: Optional[Dict] = None,
) -> Payment:
g.db.execute(
await db.execute(
"""
INSERT INTO apipayments
(wallet, checking_id, bolt11, hash, preimage,
@@ -275,14 +275,14 @@ def create_payment(
),
)
new_payment = get_wallet_payment(wallet_id, payment_hash)
new_payment = await get_wallet_payment(wallet_id, payment_hash)
assert new_payment, "Newly created payment couldn't be retrieved"
return new_payment
def update_payment_status(checking_id: str, pending: bool) -> None:
g.db.execute(
async def update_payment_status(checking_id: str, pending: bool) -> None:
await db.execute(
"UPDATE apipayments SET pending = ? WHERE checking_id = ?",
(
int(pending),
@@ -291,12 +291,12 @@ def update_payment_status(checking_id: str, pending: bool) -> None:
)
def delete_payment(checking_id: str) -> None:
g.db.execute("DELETE FROM apipayments WHERE checking_id = ?", (checking_id,))
async def delete_payment(checking_id: str) -> None:
await db.execute("DELETE FROM apipayments WHERE checking_id = ?", (checking_id,))
def check_internal(payment_hash: str) -> Optional[str]:
row = g.db.fetchone(
async def check_internal(payment_hash: str) -> Optional[str]:
row = await db.fetchone(
"""
SELECT checking_id FROM apipayments
WHERE hash = ? AND pending AND amount > 0
+19 -19
View File
@@ -1,8 +1,8 @@
import sqlite3
from sqlalchemy.exc import OperationalError # type: ignore
def m000_create_migrations_table(db):
db.execute(
async def m000_create_migrations_table(db):
await db.execute(
"""
CREATE TABLE dbversions (
db TEXT PRIMARY KEY,
@@ -12,11 +12,11 @@ def m000_create_migrations_table(db):
)
def m001_initial(db):
async def m001_initial(db):
"""
Initial LNbits tables.
"""
db.execute(
await db.execute(
"""
CREATE TABLE IF NOT EXISTS accounts (
id TEXT PRIMARY KEY,
@@ -25,7 +25,7 @@ def m001_initial(db):
);
"""
)
db.execute(
await db.execute(
"""
CREATE TABLE IF NOT EXISTS extensions (
user TEXT NOT NULL,
@@ -36,7 +36,7 @@ def m001_initial(db):
);
"""
)
db.execute(
await db.execute(
"""
CREATE TABLE IF NOT EXISTS wallets (
id TEXT PRIMARY KEY,
@@ -47,7 +47,7 @@ def m001_initial(db):
);
"""
)
db.execute(
await db.execute(
"""
CREATE TABLE IF NOT EXISTS apipayments (
payhash TEXT NOT NULL,
@@ -63,7 +63,7 @@ def m001_initial(db):
"""
)
db.execute(
await db.execute(
"""
CREATE VIEW IF NOT EXISTS balances AS
SELECT wallet, COALESCE(SUM(s), 0) AS balance FROM (
@@ -82,22 +82,22 @@ def m001_initial(db):
)
def m002_add_fields_to_apipayments(db):
async def m002_add_fields_to_apipayments(db):
"""
Adding fields to apipayments for better accounting,
and renaming payhash to checking_id since that is what it really is.
"""
try:
db.execute("ALTER TABLE apipayments RENAME COLUMN payhash TO checking_id")
db.execute("ALTER TABLE apipayments ADD COLUMN hash TEXT")
db.execute("CREATE INDEX by_hash ON apipayments (hash)")
db.execute("ALTER TABLE apipayments ADD COLUMN preimage TEXT")
db.execute("ALTER TABLE apipayments ADD COLUMN bolt11 TEXT")
db.execute("ALTER TABLE apipayments ADD COLUMN extra TEXT")
await db.execute("ALTER TABLE apipayments RENAME COLUMN payhash TO checking_id")
await db.execute("ALTER TABLE apipayments ADD COLUMN hash TEXT")
await db.execute("CREATE INDEX by_hash ON apipayments (hash)")
await db.execute("ALTER TABLE apipayments ADD COLUMN preimage TEXT")
await db.execute("ALTER TABLE apipayments ADD COLUMN bolt11 TEXT")
await db.execute("ALTER TABLE apipayments ADD COLUMN extra TEXT")
import json
rows = db.fetchall("SELECT * FROM apipayments")
rows = await (await db.execute("SELECT * FROM apipayments")).fetchall()
for row in rows:
if not row["memo"] or not row["memo"].startswith("#"):
continue
@@ -106,7 +106,7 @@ def m002_add_fields_to_apipayments(db):
prefix = "#" + ext + " "
if row["memo"].startswith(prefix):
new = row["memo"][len(prefix) :]
db.execute(
await db.execute(
"""
UPDATE apipayments SET extra = ?, memo = ?
WHERE checking_id = ? AND memo = ?
@@ -114,7 +114,7 @@ def m002_add_fields_to_apipayments(db):
(json.dumps({"tag": ext}), new, row["checking_id"], row["memo"]),
)
break
except sqlite3.OperationalError:
except OperationalError:
# this is necessary now because it may be the case that this migration will
# run twice in some environments.
# catching errors like this won't be necessary in anymore now that we
+11 -15
View File
@@ -40,18 +40,14 @@ class Wallet(NamedTuple):
hashing_key = hashlib.sha256(self.id.encode("utf-8")).digest()
linking_key = hmac.digest(hashing_key, domain.encode("utf-8"), "sha256")
return SigningKey.from_string(
linking_key,
curve=SECP256k1,
hashfunc=hashlib.sha256,
)
return SigningKey.from_string(linking_key, curve=SECP256k1, hashfunc=hashlib.sha256,)
def get_payment(self, payment_hash: str) -> Optional["Payment"]:
async def get_payment(self, payment_hash: str) -> Optional["Payment"]:
from .crud import get_wallet_payment
return get_wallet_payment(self.id, payment_hash)
return await get_wallet_payment(self.id, payment_hash)
def get_payments(
async def get_payments(
self,
*,
complete: bool = True,
@@ -62,7 +58,7 @@ class Wallet(NamedTuple):
) -> List["Payment"]:
from .crud import get_wallet_payments
return get_wallet_payments(
return await get_wallet_payments(
self.id,
complete=complete,
pending=pending,
@@ -125,12 +121,12 @@ class Payment(NamedTuple):
def is_uncheckable(self) -> bool:
return self.checking_id.startswith("temp_") or self.checking_id.startswith("internal_")
def set_pending(self, pending: bool) -> None:
async def set_pending(self, pending: bool) -> None:
from .crud import update_payment_status
update_payment_status(self.checking_id, pending)
await update_payment_status(self.checking_id, pending)
def check_pending(self) -> None:
async def check_pending(self) -> None:
if self.is_uncheckable:
return
@@ -139,9 +135,9 @@ class Payment(NamedTuple):
else:
pending = WALLET.get_invoice_status(self.checking_id)
self.set_pending(pending.pending)
await self.set_pending(pending.pending)
def delete(self) -> None:
async def delete(self) -> None:
from .crud import delete_payment
delete_payment(self.checking_id)
await delete_payment(self.checking_id)
+26 -36
View File
@@ -1,4 +1,3 @@
import trio # type: ignore
import json
import httpx
from io import BytesIO
@@ -18,10 +17,11 @@ from lnbits.helpers import urlsafe_short_hash
from lnbits.settings import WALLET
from lnbits.wallets.base import PaymentStatus, PaymentResponse
from . import db
from .crud import get_wallet, create_payment, delete_payment, check_internal, update_payment_status, get_wallet_payment
def create_invoice(
async def create_invoice(
*,
wallet_id: str,
amount: int, # in satoshis
@@ -29,6 +29,7 @@ def create_invoice(
description_hash: Optional[bytes] = None,
extra: Optional[Dict] = None,
) -> Tuple[str, str]:
await db.begin()
invoice_memo = None if description_hash else memo
storeable_memo = memo
@@ -41,7 +42,7 @@ def create_invoice(
invoice = bolt11.decode(payment_request)
amount_msat = amount * 1000
create_payment(
await create_payment(
wallet_id=wallet_id,
checking_id=checking_id,
payment_request=payment_request,
@@ -51,11 +52,11 @@ def create_invoice(
extra=extra,
)
g.db.commit()
await db.commit()
return invoice.payment_hash, payment_request
def pay_invoice(
async def pay_invoice(
*,
wallet_id: str,
payment_request: str,
@@ -63,6 +64,7 @@ def pay_invoice(
extra: Optional[Dict] = None,
description: str = "",
) -> str:
await db.begin()
temp_id = f"temp_{urlsafe_short_hash()}"
internal_id = f"internal_{urlsafe_short_hash()}"
@@ -94,58 +96,53 @@ def pay_invoice(
)
# check_internal() returns the checking_id of the invoice we're waiting for
internal_checking_id = check_internal(invoice.payment_hash)
internal_checking_id = await check_internal(invoice.payment_hash)
if internal_checking_id:
# create a new payment from this wallet
create_payment(checking_id=internal_id, fee=0, pending=False, **payment_kwargs)
await create_payment(checking_id=internal_id, fee=0, pending=False, **payment_kwargs)
else:
# create a temporary payment here so we can check if
# the balance is enough in the next step
fee_reserve = max(1000, int(invoice.amount_msat * 0.01))
create_payment(checking_id=temp_id, fee=-fee_reserve, **payment_kwargs)
await create_payment(checking_id=temp_id, fee=-fee_reserve, **payment_kwargs)
# do the balance check
wallet = get_wallet(wallet_id)
wallet = await get_wallet(wallet_id)
assert wallet
if wallet.balance_msat < 0:
g.db.rollback()
await db.rollback()
raise PermissionError("Insufficient balance.")
else:
g.db.commit()
await db.commit()
await db.begin()
if internal_checking_id:
# mark the invoice from the other side as not pending anymore
# so the other side only has access to his new money when we are sure
# the payer has enough to deduct from
update_payment_status(checking_id=internal_checking_id, pending=False)
await update_payment_status(checking_id=internal_checking_id, pending=False)
# notify receiver asynchronously
from lnbits.tasks import internal_invoice_paid
try:
internal_invoice_paid.send_nowait(internal_checking_id)
except trio.WouldBlock:
pass
await internal_invoice_paid.send(internal_checking_id)
else:
# actually pay the external invoice
payment: PaymentResponse = WALLET.pay_invoice(payment_request)
if payment.ok and payment.checking_id:
create_payment(
checking_id=payment.checking_id,
fee=payment.fee_msat,
preimage=payment.preimage,
**payment_kwargs,
await create_payment(
checking_id=payment.checking_id, fee=payment.fee_msat, preimage=payment.preimage, **payment_kwargs,
)
delete_payment(temp_id)
await delete_payment(temp_id)
else:
raise Exception(payment.error_message or "Failed to pay_invoice on backend.")
g.db.commit()
await db.commit()
return invoice.payment_hash
async def redeem_lnurl_withdraw(wallet_id: str, res: LnurlWithdrawResponse, memo: Optional[str] = None) -> None:
_, payment_request = create_invoice(
_, payment_request = await create_invoice(
wallet_id=wallet_id,
amount=res.max_sats,
memo=memo or res.default_description or "",
@@ -154,8 +151,7 @@ async def redeem_lnurl_withdraw(wallet_id: str, res: LnurlWithdrawResponse, memo
async with httpx.AsyncClient() as client:
await client.get(
res.callback.base,
params={**res.callback.query_params, **{"k1": res.k1, "pr": payment_request}},
res.callback.base, params={**res.callback.query_params, **{"k1": res.k1, "pr": payment_request}},
)
@@ -212,11 +208,7 @@ async def perform_lnurlauth(callback: str) -> Optional[LnurlErrorResponse]:
async with httpx.AsyncClient() as client:
r = await client.get(
callback,
params={
"k1": k1.hex(),
"key": key.verifying_key.to_string("compressed").hex(),
"sig": sig.hex(),
},
params={"k1": k1.hex(), "key": key.verifying_key.to_string("compressed").hex(), "sig": sig.hex(),},
)
try:
resp = json.loads(r.text)
@@ -225,13 +217,11 @@ async def perform_lnurlauth(callback: str) -> Optional[LnurlErrorResponse]:
return LnurlErrorResponse(reason=resp["reason"])
except (KeyError, json.decoder.JSONDecodeError):
return LnurlErrorResponse(
reason=r.text[:200] + "..." if len(r.text) > 200 else r.text,
)
return LnurlErrorResponse(reason=r.text[:200] + "..." if len(r.text) > 200 else r.text,)
def check_invoice_status(wallet_id: str, payment_hash: str) -> PaymentStatus:
payment = get_wallet_payment(wallet_id, payment_hash)
async def check_invoice_status(wallet_id: str, payment_hash: str) -> PaymentStatus:
payment = await get_wallet_payment(wallet_id, payment_hash)
if not payment:
return PaymentStatus(None)
+24 -43
View File
@@ -2,7 +2,6 @@ import trio # type: ignore
import json
import lnurl # type: ignore
import httpx
import traceback
from urllib.parse import urlparse, urlunparse, urlencode, parse_qs, ParseResult
from quart import g, jsonify, request, make_response
from http import HTTPStatus
@@ -12,7 +11,7 @@ from typing import Dict, Union
from lnbits import bolt11
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
from .. import core_app
from .. import core_app, db
from ..services import create_invoice, pay_invoice, perform_lnurlauth
from ..crud import delete_expired_invoices
from ..tasks import sse_listeners
@@ -22,13 +21,7 @@ from ..tasks import sse_listeners
@api_check_wallet_key("invoice")
async def api_wallet():
return (
jsonify(
{
"id": g.wallet.id,
"name": g.wallet.name,
"balance": g.wallet.balance_msat,
}
),
jsonify({"id": g.wallet.id, "name": g.wallet.name, "balance": g.wallet.balance_msat,}),
HTTPStatus.OK,
)
@@ -37,12 +30,12 @@ async def api_wallet():
@api_check_wallet_key("invoice")
async def api_payments():
if "check_pending" in request.args:
delete_expired_invoices()
await delete_expired_invoices()
for payment in g.wallet.get_payments(complete=False, pending=True, exclude_uncheckable=True):
payment.check_pending()
for payment in await g.wallet.get_payments(complete=False, pending=True, exclude_uncheckable=True):
await payment.check_pending()
return jsonify(g.wallet.get_payments(pending=True)), HTTPStatus.OK
return jsonify(await g.wallet.get_payments(pending=True)), HTTPStatus.OK
@api_check_wallet_key("invoice")
@@ -63,12 +56,14 @@ async def api_payments_create_invoice():
memo = g.data["memo"]
try:
payment_hash, payment_request = create_invoice(
payment_hash, payment_request = await create_invoice(
wallet_id=g.wallet.id, amount=g.data["amount"], memo=memo, description_hash=description_hash
)
except Exception as e:
g.db.rollback()
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR
except Exception as exc:
await db.rollback()
raise exc
await db.commit()
invoice = bolt11.decode(payment_request)
@@ -76,11 +71,7 @@ async def api_payments_create_invoice():
if g.data.get("lnurl_callback"):
async with httpx.AsyncClient() as client:
try:
r = await client.get(
g.data["lnurl_callback"],
params={"pr": payment_request},
timeout=10,
)
r = await client.get(g.data["lnurl_callback"], params={"pr": payment_request}, timeout=10,)
if r.is_error:
lnurl_response = r.text
else:
@@ -110,15 +101,14 @@ async def api_payments_create_invoice():
@api_validate_post_request(schema={"bolt11": {"type": "string", "empty": False, "required": True}})
async def api_payments_pay_invoice():
try:
payment_hash = pay_invoice(wallet_id=g.wallet.id, payment_request=g.data["bolt11"])
payment_hash = await pay_invoice(wallet_id=g.wallet.id, payment_request=g.data["bolt11"])
except ValueError as e:
return jsonify({"message": str(e)}), HTTPStatus.BAD_REQUEST
except PermissionError as e:
return jsonify({"message": str(e)}), HTTPStatus.FORBIDDEN
except Exception as exc:
traceback.print_exc(7)
g.db.rollback()
return jsonify({"message": str(exc)}), HTTPStatus.INTERNAL_SERVER_ERROR
await db.rollback()
raise exc
return (
jsonify(
@@ -157,9 +147,7 @@ async def api_payments_pay_lnurl():
async with httpx.AsyncClient() as client:
try:
r = await client.get(
g.data["callback"],
params={"amount": g.data["amount"], "comment": g.data["comment"]},
timeout=40,
g.data["callback"], params={"amount": g.data["amount"], "comment": g.data["comment"]}, timeout=40,
)
if r.is_error:
return jsonify({"message": "failed to connect"}), HTTPStatus.BAD_REQUEST
@@ -198,16 +186,12 @@ async def api_payments_pay_lnurl():
if g.data["comment"]:
extra["comment"] = g.data["comment"]
payment_hash = pay_invoice(
wallet_id=g.wallet.id,
payment_request=params["pr"],
description=g.data.get("description", ""),
extra=extra,
payment_hash = await pay_invoice(
wallet_id=g.wallet.id, payment_request=params["pr"], description=g.data.get("description", ""), extra=extra,
)
except Exception as exc:
traceback.print_exc(7)
g.db.rollback()
return jsonify({"message": str(exc)}), HTTPStatus.INTERNAL_SERVER_ERROR
await db.rollback()
raise exc
return (
jsonify(
@@ -225,7 +209,7 @@ async def api_payments_pay_lnurl():
@core_app.route("/api/v1/payments/<payment_hash>", methods=["GET"])
@api_check_wallet_key("invoice")
async def api_payment(payment_hash):
payment = g.wallet.get_payment(payment_hash)
payment = await g.wallet.get_payment(payment_hash)
if not payment:
return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND
@@ -233,7 +217,7 @@ async def api_payment(payment_hash):
return jsonify({"paid": True, "preimage": payment.preimage}), HTTPStatus.OK
try:
payment.check_pending()
await payment.check_pending()
except Exception:
return jsonify({"paid": False}), HTTPStatus.OK
@@ -243,7 +227,6 @@ async def api_payment(payment_hash):
@core_app.route("/api/v1/payments/sse", methods=["GET"])
@api_check_wallet_key("invoice", accept_querystring=True)
async def api_payments_sse():
g.db.close()
this_wallet_id = g.wallet.id
send_payment, receive_payment = trio.open_memory_channel(0)
@@ -364,9 +347,7 @@ async def api_lnurlscan(code: str):
@core_app.route("/api/v1/lnurlauth", methods=["POST"])
@api_check_wallet_key("admin")
@api_validate_post_request(
schema={
"callback": {"type": "string", "required": True},
}
schema={"callback": {"type": "string", "required": True},}
)
async def api_perform_lnurlauth():
err = await perform_lnurlauth(g.data["callback"])
+16 -15
View File
@@ -8,8 +8,8 @@ from lnurl import LnurlResponse, LnurlWithdrawResponse, decode as decode_lnurl
from lnbits.core import core_app
from lnbits.decorators import check_user_exists, validate_uuids
from lnbits.settings import LNBITS_ALLOWED_USERS, SERVICE_FEE
from lnbits.tasks import run_on_pseudo_request
from .. import db
from ..crud import (
create_account,
get_user,
@@ -41,11 +41,11 @@ async def extensions():
abort(HTTPStatus.BAD_REQUEST, "You can either `enable` or `disable` an extension.")
if extension_to_enable:
update_user_extension(user_id=g.user.id, extension=extension_to_enable, active=1)
await update_user_extension(user_id=g.user.id, extension=extension_to_enable, active=1)
elif extension_to_disable:
update_user_extension(user_id=g.user.id, extension=extension_to_disable, active=0)
await update_user_extension(user_id=g.user.id, extension=extension_to_disable, active=0)
return await render_template("core/extensions.html", user=get_user(g.user.id))
return await render_template("core/extensions.html", user=await get_user(g.user.id))
@core_app.route("/wallet")
@@ -63,9 +63,12 @@ async def wallet():
# nothing: create everything
if not user_id:
user = get_user(create_account().id)
user = await get_user((await create_account()).id)
else:
user = get_user(user_id) or abort(HTTPStatus.NOT_FOUND, "User does not exist.")
user = await get_user(user_id)
if not user:
abort(HTTPStatus.NOT_FOUND, "User does not exist.")
return
if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
abort(HTTPStatus.UNAUTHORIZED, "User not authorized.")
@@ -74,7 +77,7 @@ async def wallet():
if user.wallets and not wallet_name:
wallet = user.wallets[0]
else:
wallet = create_wallet(user_id=user.id, wallet_name=wallet_name)
wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name)
return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))
@@ -95,7 +98,7 @@ async def deletewallet():
if wallet_id not in user_wallet_ids:
abort(HTTPStatus.FORBIDDEN, "Not your wallet.")
else:
delete_wallet(user_id=g.user.id, wallet_id=wallet_id)
await delete_wallet(user_id=g.user.id, wallet_id=wallet_id)
user_wallet_ids.remove(wallet_id)
if user_wallet_ids:
@@ -120,14 +123,12 @@ async def lnurlwallet():
except Exception as exc:
return f"Could not process lnurl-withdraw: {exc}", HTTPStatus.INTERNAL_SERVER_ERROR
account = create_account()
user = get_user(account.id)
wallet = create_wallet(user_id=user.id)
g.db.commit()
account = await create_account()
user = await get_user(account.id)
wallet = await create_wallet(user_id=user.id)
await db.commit()
await run_on_pseudo_request(
redeem_lnurl_withdraw, wallet.id, withdraw_res, "LNbits initial funding: voucher redeem."
)
g.nursery.start_soon(redeem_lnurl_withdraw, wallet.id, withdraw_res, "LNbits initial funding: voucher redeem.")
await trio.sleep(3)
return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))