2022-02-10 10:56:02 +01:00
|
|
|
import datetime
|
2022-07-16 14:23:03 +02:00
|
|
|
import json
|
|
|
|
from typing import Any, Dict, List, Optional
|
2022-02-10 10:56:02 +01:00
|
|
|
from urllib.parse import urlparse
|
2022-07-16 14:23:03 +02:00
|
|
|
from uuid import uuid4
|
2020-03-04 23:11:15 +01:00
|
|
|
|
2020-09-02 05:58:21 +02:00
|
|
|
from lnbits import bolt11
|
2022-07-16 14:23:03 +02:00
|
|
|
from lnbits.db import COCKROACH, POSTGRES, Connection
|
2022-12-19 09:10:41 +01:00
|
|
|
from lnbits.settings import AdminSettings, EditableSettings, SuperSettings, settings
|
2020-03-04 23:11:15 +01:00
|
|
|
|
2022-12-09 08:46:30 +01:00
|
|
|
from . import db
|
2023-01-12 23:52:46 +01:00
|
|
|
from .models import BalanceCheck, Payment, TinyURL, User, Wallet
|
2022-12-09 08:46:30 +01:00
|
|
|
|
2020-03-04 23:11:15 +01:00
|
|
|
# accounts
|
|
|
|
# --------
|
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def create_account(conn: Optional[Connection] = None) -> User:
|
2020-09-07 05:47:13 +02:00
|
|
|
user_id = uuid4().hex
|
2021-03-26 23:10:30 +01:00
|
|
|
await (conn or db).execute("INSERT INTO accounts (id) VALUES (?)", (user_id,))
|
2020-03-04 23:11:15 +01:00
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
new_account = await get_account(user_id=user_id, conn=conn)
|
2020-04-26 13:28:19 +02:00
|
|
|
assert new_account, "Newly created account couldn't be retrieved"
|
|
|
|
|
|
|
|
return new_account
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def get_account(
|
|
|
|
user_id: str, conn: Optional[Connection] = None
|
|
|
|
) -> Optional[User]:
|
|
|
|
row = await (conn or db).fetchone(
|
2022-01-30 20:43:30 +01:00
|
|
|
"SELECT id, email, pass as password FROM accounts WHERE id = ?", (user_id,)
|
2021-03-24 04:40:32 +01:00
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
return User(**row) if row else None
|
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def get_user(user_id: str, conn: Optional[Connection] = None) -> Optional[User]:
|
|
|
|
user = await (conn or db).fetchone(
|
|
|
|
"SELECT id, email FROM accounts WHERE id = ?", (user_id,)
|
|
|
|
)
|
2020-09-07 05:47:13 +02:00
|
|
|
|
|
|
|
if user:
|
2021-03-26 23:10:30 +01:00
|
|
|
extensions = await (conn or db).fetchall(
|
2021-06-22 04:22:52 +02:00
|
|
|
"""SELECT extension FROM extensions WHERE "user" = ? AND active""",
|
|
|
|
(user_id,),
|
2021-03-24 04:40:32 +01:00
|
|
|
)
|
2021-03-26 23:10:30 +01:00
|
|
|
wallets = await (conn or db).fetchall(
|
2020-09-07 05:47:13 +02:00
|
|
|
"""
|
|
|
|
SELECT *, COALESCE((SELECT balance FROM balances WHERE wallet = wallets.id), 0) AS balance_msat
|
|
|
|
FROM wallets
|
2021-06-22 04:22:52 +02:00
|
|
|
WHERE "user" = ?
|
2020-09-07 05:47:13 +02:00
|
|
|
""",
|
|
|
|
(user_id,),
|
|
|
|
)
|
2021-08-24 21:23:18 +02:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return User(
|
2021-10-17 19:33:29 +02:00
|
|
|
id=user["id"],
|
|
|
|
email=user["email"],
|
|
|
|
extensions=[e[0] for e in extensions],
|
|
|
|
wallets=[Wallet(**w) for w in wallets],
|
2022-12-05 20:41:23 +01:00
|
|
|
admin=user["id"] == settings.super_user
|
|
|
|
or user["id"] in settings.lnbits_admin_users,
|
2021-10-17 19:33:29 +02:00
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def update_user_extension(
|
2021-06-22 04:22:52 +02:00
|
|
|
*, user_id: str, extension: str, active: bool, conn: Optional[Connection] = None
|
2021-03-26 23:10:30 +01:00
|
|
|
) -> None:
|
|
|
|
await (conn or db).execute(
|
2020-09-07 05:47:13 +02:00
|
|
|
"""
|
2021-06-22 04:22:52 +02:00
|
|
|
INSERT INTO extensions ("user", extension, active) VALUES (?, ?, ?)
|
|
|
|
ON CONFLICT ("user", extension) DO UPDATE SET active = ?
|
2020-09-07 05:47:13 +02:00
|
|
|
""",
|
2021-06-22 04:22:52 +02:00
|
|
|
(user_id, extension, active, active),
|
2020-09-07 05:47:13 +02:00
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
# wallets
|
|
|
|
# -------
|
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def create_wallet(
|
|
|
|
*,
|
|
|
|
user_id: str,
|
|
|
|
wallet_name: Optional[str] = None,
|
|
|
|
conn: Optional[Connection] = None,
|
|
|
|
) -> Wallet:
|
2020-09-07 05:47:13 +02:00
|
|
|
wallet_id = uuid4().hex
|
2021-03-26 23:10:30 +01:00
|
|
|
await (conn or db).execute(
|
2020-09-07 05:47:13 +02:00
|
|
|
"""
|
2021-06-22 04:22:52 +02:00
|
|
|
INSERT INTO wallets (id, name, "user", adminkey, inkey)
|
2020-09-07 05:47:13 +02:00
|
|
|
VALUES (?, ?, ?, ?, ?)
|
|
|
|
""",
|
2021-03-24 04:40:32 +01:00
|
|
|
(
|
|
|
|
wallet_id,
|
2022-10-03 16:46:46 +02:00
|
|
|
wallet_name or settings.lnbits_default_wallet_name,
|
2021-03-24 04:40:32 +01:00
|
|
|
user_id,
|
|
|
|
uuid4().hex,
|
|
|
|
uuid4().hex,
|
|
|
|
),
|
2020-09-07 05:47:13 +02:00
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
new_wallet = await get_wallet(wallet_id=wallet_id, conn=conn)
|
2020-04-26 13:28:19 +02:00
|
|
|
assert new_wallet, "Newly created wallet couldn't be retrieved"
|
|
|
|
|
|
|
|
return new_wallet
|
2020-03-04 23:11:15 +01:00
|
|
|
|
2021-10-17 19:33:29 +02:00
|
|
|
|
2021-08-06 12:15:07 +02:00
|
|
|
async def update_wallet(
|
|
|
|
wallet_id: str, new_name: str, conn: Optional[Connection] = None
|
|
|
|
) -> Optional[Wallet]:
|
2022-07-19 18:51:35 +02:00
|
|
|
return await (conn or db).execute(
|
2021-08-06 12:15:07 +02:00
|
|
|
"""
|
|
|
|
UPDATE wallets SET
|
|
|
|
name = ?
|
|
|
|
WHERE id = ?
|
|
|
|
""",
|
2021-10-17 19:33:29 +02:00
|
|
|
(new_name, wallet_id),
|
2021-08-06 12:15:07 +02:00
|
|
|
)
|
|
|
|
|
2020-03-04 23:11:15 +01:00
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def delete_wallet(
|
|
|
|
*, user_id: str, wallet_id: str, conn: Optional[Connection] = None
|
|
|
|
) -> None:
|
|
|
|
await (conn or db).execute(
|
2020-09-07 05:47:13 +02:00
|
|
|
"""
|
|
|
|
UPDATE wallets AS w
|
|
|
|
SET
|
2021-06-22 04:22:52 +02:00
|
|
|
"user" = 'del:' || w."user",
|
2020-09-07 05:47:13 +02:00
|
|
|
adminkey = 'del:' || w.adminkey,
|
|
|
|
inkey = 'del:' || w.inkey
|
2021-06-22 04:22:52 +02:00
|
|
|
WHERE id = ? AND "user" = ?
|
2020-09-07 05:47:13 +02:00
|
|
|
""",
|
|
|
|
(wallet_id, user_id),
|
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def get_wallet(
|
|
|
|
wallet_id: str, conn: Optional[Connection] = None
|
|
|
|
) -> Optional[Wallet]:
|
|
|
|
row = await (conn or db).fetchone(
|
2020-09-07 05:47:13 +02:00
|
|
|
"""
|
|
|
|
SELECT *, COALESCE((SELECT balance FROM balances WHERE wallet = wallets.id), 0) AS balance_msat
|
|
|
|
FROM wallets
|
|
|
|
WHERE id = ?
|
|
|
|
""",
|
|
|
|
(wallet_id,),
|
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
return Wallet(**row) if row else None
|
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def get_wallet_for_key(
|
|
|
|
key: str, key_type: str = "invoice", conn: Optional[Connection] = None
|
|
|
|
) -> Optional[Wallet]:
|
|
|
|
row = await (conn or db).fetchone(
|
2020-09-07 05:47:13 +02:00
|
|
|
"""
|
|
|
|
SELECT *, COALESCE((SELECT balance FROM balances WHERE wallet = wallets.id), 0) AS balance_msat
|
|
|
|
FROM wallets
|
|
|
|
WHERE adminkey = ? OR inkey = ?
|
|
|
|
""",
|
|
|
|
(key, key),
|
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
2020-09-07 05:47:13 +02:00
|
|
|
if not row:
|
|
|
|
return None
|
2020-05-05 09:59:15 +02:00
|
|
|
|
2020-09-07 05:47:13 +02:00
|
|
|
if key_type == "admin" and row["adminkey"] != key:
|
|
|
|
return None
|
2020-05-05 09:59:15 +02:00
|
|
|
|
2020-09-07 05:47:13 +02:00
|
|
|
return Wallet(**row)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2022-09-23 15:33:51 +02:00
|
|
|
async def get_total_balance(conn: Optional[Connection] = None):
|
|
|
|
row = await (conn or db).fetchone("SELECT SUM(balance) FROM balances")
|
|
|
|
return 0 if row[0] is None else row[0]
|
|
|
|
|
|
|
|
|
2020-03-07 22:27:00 +01:00
|
|
|
# wallet payments
|
|
|
|
# ---------------
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def get_standalone_payment(
|
2022-06-03 14:33:31 +02:00
|
|
|
checking_id_or_hash: str,
|
|
|
|
conn: Optional[Connection] = None,
|
|
|
|
incoming: Optional[bool] = False,
|
2022-07-07 18:29:26 +02:00
|
|
|
wallet_id: Optional[str] = None,
|
2021-03-26 23:10:30 +01:00
|
|
|
) -> Optional[Payment]:
|
2022-06-03 14:33:31 +02:00
|
|
|
clause: str = "checking_id = ? OR hash = ?"
|
2022-07-07 18:29:26 +02:00
|
|
|
values = [checking_id_or_hash, checking_id_or_hash]
|
2022-06-03 14:33:31 +02:00
|
|
|
if incoming:
|
|
|
|
clause = f"({clause}) AND amount > 0"
|
2022-07-07 18:29:26 +02:00
|
|
|
|
|
|
|
if wallet_id:
|
|
|
|
clause = f"({clause}) AND wallet = ?"
|
|
|
|
values.append(wallet_id)
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
row = await (conn or db).fetchone(
|
2022-06-03 14:33:31 +02:00
|
|
|
f"""
|
2020-09-28 04:12:55 +02:00
|
|
|
SELECT *
|
|
|
|
FROM apipayments
|
2022-06-03 14:33:31 +02:00
|
|
|
WHERE {clause}
|
2021-03-07 04:08:36 +01:00
|
|
|
LIMIT 1
|
2020-09-28 04:12:55 +02:00
|
|
|
""",
|
2022-07-07 18:29:26 +02:00
|
|
|
tuple(values),
|
2020-09-28 04:12:55 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
return Payment.from_row(row) if row else None
|
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def get_wallet_payment(
|
|
|
|
wallet_id: str, payment_hash: str, conn: Optional[Connection] = None
|
|
|
|
) -> Optional[Payment]:
|
|
|
|
row = await (conn or db).fetchone(
|
2020-09-07 05:47:13 +02:00
|
|
|
"""
|
|
|
|
SELECT *
|
|
|
|
FROM apipayments
|
|
|
|
WHERE wallet = ? AND hash = ?
|
|
|
|
""",
|
|
|
|
(wallet_id, payment_hash),
|
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
2020-09-01 03:12:46 +02:00
|
|
|
return Payment.from_row(row) if row else None
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2022-11-17 13:59:40 +01:00
|
|
|
async def get_latest_payments_by_extension(ext_name: str, ext_id: str, limit: int = 5):
|
|
|
|
rows = await db.fetchall(
|
|
|
|
f"""
|
2022-11-30 23:47:52 +01:00
|
|
|
SELECT * FROM apipayments
|
|
|
|
WHERE pending = 'false'
|
2022-11-17 13:59:40 +01:00
|
|
|
AND extra LIKE ?
|
|
|
|
AND extra LIKE ?
|
|
|
|
ORDER BY time DESC LIMIT {limit}
|
|
|
|
""",
|
|
|
|
(
|
|
|
|
f"%{ext_name}%",
|
|
|
|
f"%{ext_id}%",
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
return rows
|
|
|
|
|
|
|
|
|
2021-03-21 21:57:33 +01:00
|
|
|
async def get_payments(
|
2020-09-12 03:14:25 +02:00
|
|
|
*,
|
2021-03-21 21:57:33 +01:00
|
|
|
wallet_id: Optional[str] = None,
|
2020-09-12 03:14:25 +02:00
|
|
|
complete: bool = False,
|
|
|
|
pending: bool = False,
|
|
|
|
outgoing: bool = False,
|
|
|
|
incoming: bool = False,
|
2021-03-24 05:41:19 +01:00
|
|
|
since: Optional[int] = None,
|
2020-09-12 03:14:25 +02:00
|
|
|
exclude_uncheckable: bool = False,
|
2022-02-17 13:10:34 +01:00
|
|
|
limit: Optional[int] = None,
|
|
|
|
offset: Optional[int] = None,
|
2021-03-26 23:10:30 +01:00
|
|
|
conn: Optional[Connection] = None,
|
2020-08-31 05:24:57 +02:00
|
|
|
) -> List[Payment]:
|
|
|
|
"""
|
|
|
|
Filters payments to be returned by complete | pending | outgoing | incoming.
|
|
|
|
"""
|
|
|
|
|
2021-03-24 05:41:19 +01:00
|
|
|
args: List[Any] = []
|
|
|
|
clause: List[str] = []
|
2021-03-21 21:57:33 +01:00
|
|
|
|
2021-03-24 05:41:19 +01:00
|
|
|
if since != None:
|
2021-06-22 04:22:52 +02:00
|
|
|
if db.type == POSTGRES:
|
|
|
|
clause.append("time > to_timestamp(?)")
|
2021-07-02 23:32:58 +02:00
|
|
|
elif db.type == COCKROACH:
|
|
|
|
clause.append("time > cast(? AS timestamp)")
|
2021-06-22 04:22:52 +02:00
|
|
|
else:
|
|
|
|
clause.append("time > ?")
|
2021-03-24 05:41:19 +01:00
|
|
|
args.append(since)
|
2021-03-21 21:57:33 +01:00
|
|
|
|
|
|
|
if wallet_id:
|
|
|
|
clause.append("wallet = ?")
|
2021-03-24 05:41:19 +01:00
|
|
|
args.append(wallet_id)
|
2021-03-21 21:57:33 +01:00
|
|
|
|
2020-08-31 05:24:57 +02:00
|
|
|
if complete and pending:
|
2021-03-21 21:57:33 +01:00
|
|
|
pass
|
2020-08-31 05:24:57 +02:00
|
|
|
elif complete:
|
2021-06-22 04:22:52 +02:00
|
|
|
clause.append("((amount > 0 AND pending = false) OR amount < 0)")
|
2020-08-31 05:24:57 +02:00
|
|
|
elif pending:
|
2021-06-22 04:22:52 +02:00
|
|
|
clause.append("pending = true")
|
2020-08-31 05:24:57 +02:00
|
|
|
else:
|
2021-03-21 21:57:33 +01:00
|
|
|
pass
|
2020-09-12 03:14:25 +02:00
|
|
|
|
2020-08-31 05:24:57 +02:00
|
|
|
if outgoing and incoming:
|
2021-03-21 21:57:33 +01:00
|
|
|
pass
|
2020-08-31 05:24:57 +02:00
|
|
|
elif outgoing:
|
2021-03-21 21:57:33 +01:00
|
|
|
clause.append("amount < 0")
|
2020-08-31 05:24:57 +02:00
|
|
|
elif incoming:
|
2021-03-21 21:57:33 +01:00
|
|
|
clause.append("amount > 0")
|
2020-08-31 05:24:57 +02:00
|
|
|
else:
|
2021-03-21 21:57:33 +01:00
|
|
|
pass
|
2020-09-12 03:14:25 +02:00
|
|
|
|
|
|
|
if exclude_uncheckable: # checkable means it has a checking_id that isn't internal
|
2021-03-21 21:57:33 +01:00
|
|
|
clause.append("checking_id NOT LIKE 'temp_%'")
|
|
|
|
clause.append("checking_id NOT LIKE 'internal_%'")
|
|
|
|
|
2022-02-17 13:10:34 +01:00
|
|
|
limit_clause = f"LIMIT {limit}" if type(limit) == int and limit > 0 else ""
|
|
|
|
offset_clause = f"OFFSET {offset}" if type(offset) == int and offset > 0 else ""
|
|
|
|
# combine limit and offset clauses
|
|
|
|
limit_offset_clause = (
|
|
|
|
f"{limit_clause} {offset_clause}"
|
|
|
|
if limit_clause and offset_clause
|
|
|
|
else limit_clause or offset_clause
|
|
|
|
)
|
|
|
|
|
2021-03-21 21:57:33 +01:00
|
|
|
where = ""
|
|
|
|
if clause:
|
|
|
|
where = f"WHERE {' AND '.join(clause)}"
|
2020-09-12 03:14:25 +02:00
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
rows = await (conn or db).fetchall(
|
2020-09-07 05:47:13 +02:00
|
|
|
f"""
|
|
|
|
SELECT *
|
|
|
|
FROM apipayments
|
2021-03-21 21:57:33 +01:00
|
|
|
{where}
|
2020-09-07 05:47:13 +02:00
|
|
|
ORDER BY time DESC
|
2022-02-17 13:10:34 +01:00
|
|
|
{limit_offset_clause}
|
2020-09-07 05:47:13 +02:00
|
|
|
""",
|
2021-03-24 05:41:19 +01:00
|
|
|
tuple(args),
|
2020-09-07 05:47:13 +02:00
|
|
|
)
|
2020-09-01 03:12:46 +02:00
|
|
|
return [Payment.from_row(row) for row in rows]
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2022-01-31 17:29:42 +01:00
|
|
|
async def delete_expired_invoices(
|
|
|
|
conn: Optional[Connection] = None,
|
|
|
|
) -> None:
|
2021-05-14 18:12:05 +02:00
|
|
|
# first we delete all invoices older than one month
|
|
|
|
await (conn or db).execute(
|
2021-06-22 04:22:52 +02:00
|
|
|
f"""
|
2021-05-14 18:12:05 +02:00
|
|
|
DELETE FROM apipayments
|
2021-06-22 04:22:52 +02:00
|
|
|
WHERE pending = true AND amount > 0
|
|
|
|
AND time < {db.timestamp_now} - {db.interval_seconds(2592000)}
|
2021-05-14 18:12:05 +02:00
|
|
|
"""
|
|
|
|
)
|
2022-10-05 14:17:23 +02:00
|
|
|
# then we delete all invoices whose expiry date is in the past
|
|
|
|
await (conn or db).execute(
|
2021-06-22 04:22:52 +02:00
|
|
|
f"""
|
2022-10-05 14:17:23 +02:00
|
|
|
DELETE FROM apipayments
|
|
|
|
WHERE pending = true AND amount > 0
|
|
|
|
AND expiry < {db.timestamp_now}
|
2021-05-14 18:12:05 +02:00
|
|
|
"""
|
2020-09-07 05:47:13 +02:00
|
|
|
)
|
2020-04-17 21:13:57 +02:00
|
|
|
|
|
|
|
|
2020-03-07 22:27:00 +01:00
|
|
|
# payments
|
|
|
|
# --------
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2020-11-21 22:04:39 +01:00
|
|
|
async def create_payment(
|
2020-09-01 03:12:46 +02:00
|
|
|
*,
|
|
|
|
wallet_id: str,
|
|
|
|
checking_id: str,
|
|
|
|
payment_request: str,
|
|
|
|
payment_hash: str,
|
|
|
|
amount: int,
|
|
|
|
memo: str,
|
|
|
|
fee: int = 0,
|
|
|
|
preimage: Optional[str] = None,
|
|
|
|
pending: bool = True,
|
|
|
|
extra: Optional[Dict] = None,
|
2020-12-24 13:38:35 +01:00
|
|
|
webhook: Optional[str] = None,
|
2021-03-26 23:10:30 +01:00
|
|
|
conn: Optional[Connection] = None,
|
2020-03-31 19:05:25 +02:00
|
|
|
) -> Payment:
|
2022-08-30 13:28:58 +02:00
|
|
|
|
|
|
|
# todo: add this when tests are fixed
|
|
|
|
# previous_payment = await get_wallet_payment(wallet_id, payment_hash, conn=conn)
|
|
|
|
# assert previous_payment is None, "Payment already exists"
|
|
|
|
|
2022-10-05 14:17:23 +02:00
|
|
|
try:
|
|
|
|
invoice = bolt11.decode(payment_request)
|
|
|
|
expiration_date = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry)
|
|
|
|
except:
|
2022-12-06 10:48:16 +01:00
|
|
|
# assume maximum bolt11 expiry of 31 days to be on the safe side
|
2022-10-05 14:17:23 +02:00
|
|
|
expiration_date = datetime.datetime.now() + datetime.timedelta(days=31)
|
2022-08-30 13:28:58 +02:00
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
await (conn or db).execute(
|
2020-09-07 05:47:13 +02:00
|
|
|
"""
|
|
|
|
INSERT INTO apipayments
|
|
|
|
(wallet, checking_id, bolt11, hash, preimage,
|
2022-10-05 14:17:23 +02:00
|
|
|
amount, pending, memo, fee, extra, webhook, expiry)
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
2020-09-07 05:47:13 +02:00
|
|
|
""",
|
|
|
|
(
|
|
|
|
wallet_id,
|
|
|
|
checking_id,
|
|
|
|
payment_request,
|
|
|
|
payment_hash,
|
|
|
|
preimage,
|
|
|
|
amount,
|
2021-06-22 04:22:52 +02:00
|
|
|
pending,
|
2020-09-07 05:47:13 +02:00
|
|
|
memo,
|
|
|
|
fee,
|
2021-03-24 04:40:32 +01:00
|
|
|
json.dumps(extra)
|
|
|
|
if extra and extra != {} and type(extra) is dict
|
|
|
|
else None,
|
2020-12-24 13:38:35 +01:00
|
|
|
webhook,
|
2022-12-02 17:38:36 +01:00
|
|
|
db.datetime_to_timestamp(expiration_date),
|
2020-09-07 05:47:13 +02:00
|
|
|
),
|
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
new_payment = await get_wallet_payment(wallet_id, payment_hash, conn=conn)
|
2020-04-26 13:28:19 +02:00
|
|
|
assert new_payment, "Newly created payment couldn't be retrieved"
|
|
|
|
|
|
|
|
return new_payment
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def update_payment_status(
|
2021-10-17 19:33:29 +02:00
|
|
|
checking_id: str, pending: bool, conn: Optional[Connection] = None
|
2021-03-26 23:10:30 +01:00
|
|
|
) -> None:
|
|
|
|
await (conn or db).execute(
|
2021-03-07 23:18:02 +01:00
|
|
|
"UPDATE apipayments SET pending = ? WHERE checking_id = ?",
|
2021-10-17 19:33:29 +02:00
|
|
|
(pending, checking_id),
|
2020-09-07 05:47:13 +02:00
|
|
|
)
|
2020-03-04 23:11:15 +01:00
|
|
|
|
|
|
|
|
2022-08-30 13:28:58 +02:00
|
|
|
async def update_payment_details(
|
|
|
|
checking_id: str,
|
|
|
|
pending: Optional[bool] = None,
|
|
|
|
fee: Optional[int] = None,
|
|
|
|
preimage: Optional[str] = None,
|
|
|
|
new_checking_id: Optional[str] = None,
|
|
|
|
conn: Optional[Connection] = None,
|
|
|
|
) -> None:
|
|
|
|
|
|
|
|
set_clause: List[str] = []
|
|
|
|
set_variables: List[Any] = []
|
|
|
|
|
|
|
|
if new_checking_id is not None:
|
|
|
|
set_clause.append("checking_id = ?")
|
|
|
|
set_variables.append(new_checking_id)
|
|
|
|
if pending is not None:
|
|
|
|
set_clause.append("pending = ?")
|
|
|
|
set_variables.append(pending)
|
|
|
|
if fee is not None:
|
|
|
|
set_clause.append("fee = ?")
|
|
|
|
set_variables.append(fee)
|
|
|
|
if preimage is not None:
|
|
|
|
set_clause.append("preimage = ?")
|
|
|
|
set_variables.append(preimage)
|
|
|
|
|
|
|
|
set_variables.append(checking_id)
|
|
|
|
|
|
|
|
await (conn or db).execute(
|
|
|
|
f"UPDATE apipayments SET {', '.join(set_clause)} WHERE checking_id = ?",
|
|
|
|
tuple(set_variables),
|
|
|
|
)
|
|
|
|
return
|
|
|
|
|
|
|
|
|
2022-12-21 13:57:00 +01:00
|
|
|
async def update_payment_extra(
|
2022-12-22 10:28:12 +01:00
|
|
|
payment_hash: str,
|
2022-12-21 13:57:00 +01:00
|
|
|
extra: dict,
|
2022-12-22 10:12:19 +01:00
|
|
|
outgoing: bool = False,
|
2022-12-21 13:57:00 +01:00
|
|
|
conn: Optional[Connection] = None,
|
|
|
|
) -> None:
|
|
|
|
"""
|
|
|
|
Only update the `extra` field for the payment.
|
|
|
|
Old values in the `extra` JSON object will be kept unless the new `extra` overwrites them.
|
|
|
|
"""
|
2022-12-22 10:12:19 +01:00
|
|
|
|
2022-12-22 11:23:46 +01:00
|
|
|
amount_clause = "AND amount < 0" if outgoing else "AND amount > 0"
|
2022-12-21 13:57:00 +01:00
|
|
|
|
|
|
|
row = await (conn or db).fetchone(
|
2022-12-22 10:12:19 +01:00
|
|
|
f"SELECT hash, extra from apipayments WHERE hash = ? {amount_clause}",
|
2022-12-22 10:28:12 +01:00
|
|
|
(payment_hash,),
|
2022-12-21 13:57:00 +01:00
|
|
|
)
|
|
|
|
if not row:
|
|
|
|
return
|
2022-12-21 14:47:22 +01:00
|
|
|
db_extra = json.loads(row["extra"] if row["extra"] else "{}")
|
|
|
|
db_extra.update(extra)
|
|
|
|
|
2022-12-21 13:57:00 +01:00
|
|
|
await (conn or db).execute(
|
2022-12-22 10:12:19 +01:00
|
|
|
f"UPDATE apipayments SET extra = ? WHERE hash = ? {amount_clause} ",
|
2022-12-22 10:28:12 +01:00
|
|
|
(json.dumps(db_extra), payment_hash),
|
2022-12-21 13:57:00 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-10-17 19:33:29 +02:00
|
|
|
async def delete_payment(checking_id: str, conn: Optional[Connection] = None) -> None:
|
2021-03-26 23:10:30 +01:00
|
|
|
await (conn or db).execute(
|
|
|
|
"DELETE FROM apipayments WHERE checking_id = ?", (checking_id,)
|
|
|
|
)
|
2022-09-18 15:27:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
async def delete_wallet_payment(
|
|
|
|
checking_id: str, wallet_id: str, conn: Optional[Connection] = None
|
|
|
|
) -> None:
|
|
|
|
await (conn or db).execute(
|
|
|
|
"DELETE FROM apipayments WHERE checking_id = ? AND wallet = ?",
|
|
|
|
(checking_id, wallet_id),
|
|
|
|
)
|
2020-08-19 18:53:27 +02:00
|
|
|
|
|
|
|
|
2021-03-26 23:10:30 +01:00
|
|
|
async def check_internal(
|
2021-10-17 19:33:29 +02:00
|
|
|
payment_hash: str, conn: Optional[Connection] = None
|
2021-03-26 23:10:30 +01:00
|
|
|
) -> Optional[str]:
|
|
|
|
row = await (conn or db).fetchone(
|
2020-09-07 05:47:13 +02:00
|
|
|
"""
|
2020-11-19 18:23:35 +01:00
|
|
|
SELECT checking_id FROM apipayments
|
2021-08-06 12:15:07 +02:00
|
|
|
WHERE hash = ? AND pending AND amount > 0
|
2020-11-19 18:23:35 +01:00
|
|
|
""",
|
2020-09-07 05:47:13 +02:00
|
|
|
(payment_hash,),
|
|
|
|
)
|
|
|
|
if not row:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return row["checking_id"]
|
2021-04-17 23:27:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
# balance_check
|
|
|
|
# -------------
|
|
|
|
|
|
|
|
|
|
|
|
async def save_balance_check(
|
2021-10-17 19:33:29 +02:00
|
|
|
wallet_id: str, url: str, conn: Optional[Connection] = None
|
2021-04-17 23:27:15 +02:00
|
|
|
):
|
|
|
|
domain = urlparse(url).netloc
|
|
|
|
|
|
|
|
await (conn or db).execute(
|
|
|
|
"""
|
2021-06-22 04:22:52 +02:00
|
|
|
INSERT INTO balance_check (wallet, service, url) VALUES (?, ?, ?)
|
|
|
|
ON CONFLICT (wallet, service) DO UPDATE SET url = ?
|
2021-04-17 23:27:15 +02:00
|
|
|
""",
|
2021-06-22 04:22:52 +02:00
|
|
|
(wallet_id, domain, url, url),
|
2021-04-17 23:27:15 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def get_balance_check(
|
2021-10-17 19:33:29 +02:00
|
|
|
wallet_id: str, domain: str, conn: Optional[Connection] = None
|
2021-04-17 23:27:15 +02:00
|
|
|
) -> Optional[BalanceCheck]:
|
|
|
|
row = await (conn or db).fetchone(
|
|
|
|
"""
|
|
|
|
SELECT wallet, service, url
|
|
|
|
FROM balance_check
|
|
|
|
WHERE wallet = ? AND service = ?
|
|
|
|
""",
|
|
|
|
(wallet_id, domain),
|
|
|
|
)
|
|
|
|
return BalanceCheck.from_row(row) if row else None
|
|
|
|
|
|
|
|
|
|
|
|
async def get_balance_checks(conn: Optional[Connection] = None) -> List[BalanceCheck]:
|
|
|
|
rows = await (conn or db).fetchall("SELECT wallet, service, url FROM balance_check")
|
|
|
|
return [BalanceCheck.from_row(row) for row in rows]
|
|
|
|
|
|
|
|
|
|
|
|
# balance_notify
|
|
|
|
# --------------
|
|
|
|
|
|
|
|
|
|
|
|
async def save_balance_notify(
|
2021-10-17 19:33:29 +02:00
|
|
|
wallet_id: str, url: str, conn: Optional[Connection] = None
|
2021-04-17 23:27:15 +02:00
|
|
|
):
|
|
|
|
await (conn or db).execute(
|
|
|
|
"""
|
2021-06-22 04:22:52 +02:00
|
|
|
INSERT INTO balance_notify (wallet, url) VALUES (?, ?)
|
|
|
|
ON CONFLICT (wallet) DO UPDATE SET url = ?
|
2021-04-17 23:27:15 +02:00
|
|
|
""",
|
2021-06-22 04:22:52 +02:00
|
|
|
(wallet_id, url, url),
|
2021-04-17 23:27:15 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def get_balance_notify(
|
2021-10-17 19:33:29 +02:00
|
|
|
wallet_id: str, conn: Optional[Connection] = None
|
2021-04-17 23:27:15 +02:00
|
|
|
) -> Optional[str]:
|
|
|
|
row = await (conn or db).fetchone(
|
|
|
|
"""
|
|
|
|
SELECT url
|
|
|
|
FROM balance_notify
|
|
|
|
WHERE wallet = ?
|
|
|
|
""",
|
|
|
|
(wallet_id,),
|
|
|
|
)
|
|
|
|
return row[0] if row else None
|
2022-12-08 14:41:52 +01:00
|
|
|
|
|
|
|
|
|
|
|
# admin
|
|
|
|
# --------
|
|
|
|
|
|
|
|
|
|
|
|
async def get_super_settings() -> Optional[SuperSettings]:
|
|
|
|
row = await db.fetchone("SELECT * FROM settings")
|
|
|
|
if not row:
|
|
|
|
return None
|
2022-12-09 12:14:22 +01:00
|
|
|
editable_settings = json.loads(row["editable_settings"])
|
|
|
|
return SuperSettings(**{"super_user": row["super_user"], **editable_settings})
|
2022-12-08 14:41:52 +01:00
|
|
|
|
|
|
|
|
|
|
|
async def get_admin_settings(is_super_user: bool = False) -> Optional[AdminSettings]:
|
|
|
|
sets = await get_super_settings()
|
|
|
|
if not sets:
|
|
|
|
return None
|
|
|
|
row_dict = dict(sets)
|
|
|
|
row_dict.pop("super_user")
|
|
|
|
admin_settings = AdminSettings(
|
|
|
|
super_user=is_super_user,
|
|
|
|
lnbits_allowed_funding_sources=settings.lnbits_allowed_funding_sources,
|
|
|
|
**row_dict,
|
|
|
|
)
|
|
|
|
return admin_settings
|
|
|
|
|
|
|
|
|
|
|
|
async def delete_admin_settings():
|
|
|
|
await db.execute("DELETE FROM settings")
|
|
|
|
|
|
|
|
|
2022-12-19 09:10:41 +01:00
|
|
|
async def update_admin_settings(data: EditableSettings):
|
2022-12-09 13:53:09 +01:00
|
|
|
await db.execute(f"UPDATE settings SET editable_settings = ?", (json.dumps(data),))
|
2022-12-08 14:41:52 +01:00
|
|
|
|
|
|
|
|
2022-12-16 10:22:32 +01:00
|
|
|
async def update_super_user(super_user: str):
|
2022-12-16 11:14:00 +01:00
|
|
|
await db.execute("UPDATE settings SET super_user = ?", (super_user,))
|
2022-12-16 10:22:32 +01:00
|
|
|
return await get_super_settings()
|
|
|
|
|
|
|
|
|
2022-12-16 11:09:13 +01:00
|
|
|
async def create_admin_settings(super_user: str, new_settings: dict):
|
2022-12-09 12:14:22 +01:00
|
|
|
sql = f"INSERT INTO settings (super_user, editable_settings) VALUES (?, ?)"
|
2022-12-16 11:09:13 +01:00
|
|
|
await db.execute(sql, (super_user, json.dumps(new_settings)))
|
2022-12-16 10:22:32 +01:00
|
|
|
return await get_super_settings()
|
2023-01-12 16:16:37 +01:00
|
|
|
|
|
|
|
|
|
|
|
# tinyurl
|
|
|
|
# -------
|
|
|
|
|
|
|
|
|
2023-01-24 14:27:30 +01:00
|
|
|
async def create_tinyurl(domain: str, endless: bool, conn: Optional[Connection] = None):
|
2023-01-12 16:16:37 +01:00
|
|
|
tinyurl_id = uuid4().hex[:8]
|
2023-01-12 23:52:46 +01:00
|
|
|
await (conn or db).execute(
|
2023-01-24 14:27:30 +01:00
|
|
|
f"INSERT INTO tiny_url (id, url, endless) VALUES (?, ?, ?)",
|
2023-01-24 14:32:21 +01:00
|
|
|
(
|
|
|
|
tinyurl_id,
|
|
|
|
domain,
|
|
|
|
endless,
|
|
|
|
),
|
2023-01-12 16:16:37 +01:00
|
|
|
)
|
|
|
|
return await get_tinyurl(tinyurl_id)
|
|
|
|
|
|
|
|
|
2023-01-12 23:52:46 +01:00
|
|
|
async def get_tinyurl(
|
|
|
|
tinyurl_id: str, conn: Optional[Connection] = None
|
|
|
|
) -> Optional[TinyURL]:
|
|
|
|
row = await (conn or db).fetchone(
|
|
|
|
f"SELECT * FROM tiny_url WHERE id = ?",
|
2023-01-24 12:03:46 +01:00
|
|
|
(tinyurl_id,),
|
2023-01-12 16:16:37 +01:00
|
|
|
)
|
|
|
|
return TinyURL.from_row(row) if row else None
|
2023-01-12 23:39:20 +01:00
|
|
|
|
|
|
|
|
2023-01-12 23:52:46 +01:00
|
|
|
async def get_tinyurl_by_url(
|
|
|
|
url: str, conn: Optional[Connection] = None
|
|
|
|
) -> Optional[TinyURL]:
|
|
|
|
row = await (conn or db).fetchone(
|
|
|
|
f"SELECT * FROM tiny_url WHERE url = ?",
|
2023-01-24 12:03:46 +01:00
|
|
|
(url,),
|
2023-01-12 23:39:20 +01:00
|
|
|
)
|
2023-01-12 23:52:46 +01:00
|
|
|
return TinyURL.from_row(row) if row else None
|