remove exception to black line-length and reformat.
This commit is contained in:
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_amilk")
|
||||
|
||||
amilk_ext: Blueprint = Blueprint("amilk", __name__, static_folder="static", template_folder="templates")
|
||||
amilk_ext: Blueprint = Blueprint(
|
||||
"amilk", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -31,7 +31,9 @@ async def get_amilks(wallet_ids: Union[str, List[str]]) -> List[AMilk]:
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM amilks WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM amilks WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [AMilk(**row) for row in rows]
|
||||
|
||||
|
||||
@@ -21,7 +21,10 @@ async def api_amilks():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([amilk._asdict() for amilk in await get_amilks(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([amilk._asdict() for amilk in await get_amilks(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@amilk_ext.route("/api/v1/amilk/milk/<amilk_id>", methods=["GET"])
|
||||
@@ -35,12 +38,18 @@ async def api_amilkit(amilk_id):
|
||||
abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process withdraw LNURL.")
|
||||
|
||||
payment_hash, payment_request = await create_invoice(
|
||||
wallet_id=milk.wallet, amount=withdraw_res.max_sats, memo=memo, extra={"tag": "amilk"}
|
||||
wallet_id=milk.wallet,
|
||||
amount=withdraw_res.max_sats,
|
||||
memo=memo,
|
||||
extra={"tag": "amilk"},
|
||||
)
|
||||
|
||||
r = httpx.get(
|
||||
withdraw_res.callback.base,
|
||||
params={**withdraw_res.callback.query_params, **{"k1": withdraw_res.k1, "pr": payment_request}},
|
||||
params={
|
||||
**withdraw_res.callback.query_params,
|
||||
**{"k1": withdraw_res.k1, "pr": payment_request},
|
||||
},
|
||||
)
|
||||
|
||||
if r.is_error:
|
||||
@@ -68,7 +77,10 @@ async def api_amilkit(amilk_id):
|
||||
)
|
||||
async def api_amilk_create():
|
||||
amilk = await create_amilk(
|
||||
wallet_id=g.wallet.id, lnurl=g.data["lnurl"], atime=g.data["atime"], amount=g.data["amount"]
|
||||
wallet_id=g.wallet.id,
|
||||
lnurl=g.data["lnurl"],
|
||||
atime=g.data["atime"],
|
||||
amount=g.data["amount"],
|
||||
)
|
||||
|
||||
return jsonify(amilk._asdict()), HTTPStatus.CREATED
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_bleskomat")
|
||||
|
||||
bleskomat_ext: Blueprint = Blueprint("bleskomat", __name__, static_folder="static", template_folder="templates")
|
||||
bleskomat_ext: Blueprint = Blueprint(
|
||||
"bleskomat", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
from .lnurl_api import * # noqa
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -8,7 +8,12 @@ from .helpers import generate_bleskomat_lnurl_hash
|
||||
|
||||
|
||||
async def create_bleskomat(
|
||||
*, wallet_id: str, name: str, fiat_currency: str, exchange_rate_provider: str, fee: str
|
||||
*,
|
||||
wallet_id: str,
|
||||
name: str,
|
||||
fiat_currency: str,
|
||||
exchange_rate_provider: str,
|
||||
fee: str,
|
||||
) -> Bleskomat:
|
||||
bleskomat_id = uuid4().hex
|
||||
api_key_id = secrets.token_hex(8)
|
||||
@@ -42,7 +47,9 @@ async def get_bleskomat(bleskomat_id: str) -> Optional[Bleskomat]:
|
||||
|
||||
|
||||
async def get_bleskomat_by_api_key_id(api_key_id: str) -> Optional[Bleskomat]:
|
||||
row = await db.fetchone("SELECT * FROM bleskomats WHERE api_key_id = ?", (api_key_id,))
|
||||
row = await db.fetchone(
|
||||
"SELECT * FROM bleskomats WHERE api_key_id = ?", (api_key_id,)
|
||||
)
|
||||
return Bleskomat(**row) if row else None
|
||||
|
||||
|
||||
@@ -50,13 +57,17 @@ async def get_bleskomats(wallet_ids: Union[str, List[str]]) -> List[Bleskomat]:
|
||||
if isinstance(wallet_ids, str):
|
||||
wallet_ids = [wallet_ids]
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM bleskomats WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM bleskomats WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
return [Bleskomat(**row) for row in rows]
|
||||
|
||||
|
||||
async def update_bleskomat(bleskomat_id: str, **kwargs) -> Optional[Bleskomat]:
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
await db.execute(f"UPDATE bleskomats SET {q} WHERE id = ?", (*kwargs.values(), bleskomat_id))
|
||||
await db.execute(
|
||||
f"UPDATE bleskomats SET {q} WHERE id = ?", (*kwargs.values(), bleskomat_id)
|
||||
)
|
||||
row = await db.fetchone("SELECT * FROM bleskomats WHERE id = ?", (bleskomat_id,))
|
||||
return Bleskomat(**row) if row else None
|
||||
|
||||
|
||||
@@ -3,7 +3,12 @@ import json
|
||||
import os
|
||||
|
||||
fiat_currencies = json.load(
|
||||
open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "fiat_currencies.json"), "r")
|
||||
open(
|
||||
os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), "fiat_currencies.json"
|
||||
),
|
||||
"r",
|
||||
)
|
||||
)
|
||||
|
||||
exchange_rate_providers = {
|
||||
@@ -35,7 +40,9 @@ exchange_rate_providers = {
|
||||
"name": "Kraken",
|
||||
"domain": "kraken.com",
|
||||
"api_url": "https://api.kraken.com/0/public/Ticker?pair=XBT{TO}",
|
||||
"getter": lambda data, replacements: data["result"]["XXBTZ" + replacements["TO"]]["c"][0],
|
||||
"getter": lambda data, replacements: data["result"][
|
||||
"XXBTZ" + replacements["TO"]
|
||||
]["c"][0],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -50,7 +57,12 @@ for ref, exchange_rate_provider in exchange_rate_providers.items():
|
||||
|
||||
async def fetch_fiat_exchange_rate(currency: str, provider: str):
|
||||
|
||||
replacements = {"FROM": "BTC", "from": "btc", "TO": currency.upper(), "to": currency.lower()}
|
||||
replacements = {
|
||||
"FROM": "BTC",
|
||||
"from": "btc",
|
||||
"TO": currency.upper(),
|
||||
"to": currency.lower(),
|
||||
}
|
||||
|
||||
url = exchange_rate_providers[provider]["api_url"]
|
||||
for key in replacements.keys():
|
||||
|
||||
@@ -14,7 +14,9 @@ def generate_bleskomat_lnurl_hash(secret: str):
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def generate_bleskomat_lnurl_signature(payload: str, api_key_secret: str, api_key_encoding: str = "hex"):
|
||||
def generate_bleskomat_lnurl_signature(
|
||||
payload: str, api_key_secret: str, api_key_encoding: str = "hex"
|
||||
):
|
||||
if api_key_encoding == "hex":
|
||||
key = unhexlify(api_key_secret)
|
||||
elif api_key_encoding == "base64":
|
||||
@@ -41,7 +43,11 @@ def is_supported_lnurl_subprotocol(tag: str) -> bool:
|
||||
|
||||
|
||||
class LnurlHttpError(Exception):
|
||||
def __init__(self, message: str = "", http_status: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR):
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "",
|
||||
http_status: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
):
|
||||
self.message = message
|
||||
self.http_status = http_status
|
||||
super().__init__(self.message)
|
||||
@@ -62,11 +68,15 @@ def prepare_lnurl_params(tag: str, query: Dict[str, str]):
|
||||
if not params["minWithdrawable"] > 0:
|
||||
raise LnurlValidationError('"minWithdrawable" must be greater than zero')
|
||||
if not params["maxWithdrawable"] >= params["minWithdrawable"]:
|
||||
raise LnurlValidationError('"maxWithdrawable" must be greater than or equal to "minWithdrawable"')
|
||||
raise LnurlValidationError(
|
||||
'"maxWithdrawable" must be greater than or equal to "minWithdrawable"'
|
||||
)
|
||||
return params
|
||||
|
||||
|
||||
encode_uri_component_safe_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"
|
||||
encode_uri_component_safe_chars = (
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"
|
||||
)
|
||||
|
||||
|
||||
def query_to_signing_payload(query: Dict[str, str]) -> str:
|
||||
@@ -76,19 +86,30 @@ def query_to_signing_payload(query: Dict[str, str]) -> str:
|
||||
for key in sorted_keys:
|
||||
if not key == "signature":
|
||||
encoded_key = urllib.parse.quote(key, safe=encode_uri_component_safe_chars)
|
||||
encoded_value = urllib.parse.quote(query[key], safe=encode_uri_component_safe_chars)
|
||||
encoded_value = urllib.parse.quote(
|
||||
query[key], safe=encode_uri_component_safe_chars
|
||||
)
|
||||
payload.append(f"{encoded_key}={encoded_value}")
|
||||
return "&".join(payload)
|
||||
|
||||
|
||||
unshorten_rules = {
|
||||
"query": {"n": "nonce", "s": "signature", "t": "tag"},
|
||||
"tags": {"c": "channelRequest", "l": "login", "p": "payRequest", "w": "withdrawRequest"},
|
||||
"tags": {
|
||||
"c": "channelRequest",
|
||||
"l": "login",
|
||||
"p": "payRequest",
|
||||
"w": "withdrawRequest",
|
||||
},
|
||||
"params": {
|
||||
"channelRequest": {"pl": "localAmt", "pp": "pushAmt"},
|
||||
"login": {},
|
||||
"payRequest": {"pn": "minSendable", "px": "maxSendable", "pm": "metadata"},
|
||||
"withdrawRequest": {"pn": "minWithdrawable", "px": "maxWithdrawable", "pd": "defaultDescription"},
|
||||
"withdrawRequest": {
|
||||
"pn": "minWithdrawable",
|
||||
"px": "maxWithdrawable",
|
||||
"pd": "defaultDescription",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,10 @@ async def api_bleskomat_lnurl():
|
||||
# The API key ID, nonce, and tag should be present in the query string.
|
||||
for field in ["id", "nonce", "tag"]:
|
||||
if not field in query:
|
||||
raise LnurlHttpError(f'Failed API key signature check: Missing "{field}"', HTTPStatus.BAD_REQUEST)
|
||||
raise LnurlHttpError(
|
||||
f'Failed API key signature check: Missing "{field}"',
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
|
||||
# URL signing scheme is described here:
|
||||
# https://github.com/chill117/lnurl-node#how-to-implement-url-signing-scheme
|
||||
@@ -58,7 +61,9 @@ async def api_bleskomat_lnurl():
|
||||
raise LnurlHttpError("Unknown API key", HTTPStatus.BAD_REQUEST)
|
||||
api_key_secret = bleskomat.api_key_secret
|
||||
api_key_encoding = bleskomat.api_key_encoding
|
||||
expected_signature = generate_bleskomat_lnurl_signature(payload, api_key_secret, api_key_encoding)
|
||||
expected_signature = generate_bleskomat_lnurl_signature(
|
||||
payload, api_key_secret, api_key_encoding
|
||||
)
|
||||
if signature != expected_signature:
|
||||
raise LnurlHttpError("Invalid API key signature", HTTPStatus.FORBIDDEN)
|
||||
|
||||
@@ -72,13 +77,16 @@ async def api_bleskomat_lnurl():
|
||||
params = prepare_lnurl_params(tag, query)
|
||||
if "f" in query:
|
||||
rate = await fetch_fiat_exchange_rate(
|
||||
currency=query["f"], provider=bleskomat.exchange_rate_provider
|
||||
currency=query["f"],
|
||||
provider=bleskomat.exchange_rate_provider,
|
||||
)
|
||||
# Convert fee (%) to decimal:
|
||||
fee = float(bleskomat.fee) / 100
|
||||
if tag == "withdrawRequest":
|
||||
for key in ["minWithdrawable", "maxWithdrawable"]:
|
||||
amount_sats = int(math.floor((params[key] / rate) * 1e8))
|
||||
amount_sats = int(
|
||||
math.floor((params[key] / rate) * 1e8)
|
||||
)
|
||||
fee_sats = int(math.floor(amount_sats * fee))
|
||||
amount_sats_less_fee = amount_sats - fee_sats
|
||||
# Convert to msats:
|
||||
@@ -87,7 +95,9 @@ async def api_bleskomat_lnurl():
|
||||
raise LnurlHttpError(e.message, HTTPStatus.BAD_REQUEST)
|
||||
# Create a new LNURL using the query parameters provided in the signed URL.
|
||||
params = json.JSONEncoder().encode(params)
|
||||
lnurl = await create_bleskomat_lnurl(bleskomat=bleskomat, secret=secret, tag=tag, params=params, uses=1)
|
||||
lnurl = await create_bleskomat_lnurl(
|
||||
bleskomat=bleskomat, secret=secret, tag=tag, params=params, uses=1
|
||||
)
|
||||
|
||||
# Reply with LNURL response object.
|
||||
return jsonify(lnurl.get_info_response_object(secret)), HTTPStatus.OK
|
||||
@@ -104,7 +114,9 @@ async def api_bleskomat_lnurl():
|
||||
raise LnurlHttpError("Invalid secret", HTTPStatus.BAD_REQUEST)
|
||||
|
||||
if not lnurl.has_uses_remaining():
|
||||
raise LnurlHttpError("Maximum number of uses already reached", HTTPStatus.BAD_REQUEST)
|
||||
raise LnurlHttpError(
|
||||
"Maximum number of uses already reached", HTTPStatus.BAD_REQUEST
|
||||
)
|
||||
|
||||
try:
|
||||
await lnurl.execute_action(query)
|
||||
@@ -115,6 +127,9 @@ async def api_bleskomat_lnurl():
|
||||
return jsonify({"status": "ERROR", "reason": str(e)}), e.http_status
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return jsonify({"status": "ERROR", "reason": "Unexpected error"}), HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "Unexpected error"}),
|
||||
HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
|
||||
return jsonify({"status": "OK"}), HTTPStatus.OK
|
||||
|
||||
@@ -62,11 +62,17 @@ class BleskomatLnurl(NamedTuple):
|
||||
try:
|
||||
invoice = bolt11.decode(pr)
|
||||
except ValueError as e:
|
||||
raise LnurlValidationError('Invalid parameter ("pr"): Lightning payment request expected')
|
||||
raise LnurlValidationError(
|
||||
'Invalid parameter ("pr"): Lightning payment request expected'
|
||||
)
|
||||
if invoice.amount_msat < params["minWithdrawable"]:
|
||||
raise LnurlValidationError('Amount in invoice must be greater than or equal to "minWithdrawable"')
|
||||
raise LnurlValidationError(
|
||||
'Amount in invoice must be greater than or equal to "minWithdrawable"'
|
||||
)
|
||||
if invoice.amount_msat > params["maxWithdrawable"]:
|
||||
raise LnurlValidationError('Amount in invoice must be less than or equal to "maxWithdrawable"')
|
||||
raise LnurlValidationError(
|
||||
'Amount in invoice must be less than or equal to "maxWithdrawable"'
|
||||
)
|
||||
else:
|
||||
raise LnurlValidationError(f'Unknown subprotocol: "{tag}"')
|
||||
|
||||
|
||||
@@ -17,4 +17,6 @@ async def index():
|
||||
"exchange_rate_providers": exchange_rate_providers_serializable,
|
||||
"fiat_currencies": fiat_currencies,
|
||||
}
|
||||
return await render_template("bleskomat/index.html", user=g.user, bleskomat_vars=bleskomat_vars)
|
||||
return await render_template(
|
||||
"bleskomat/index.html", user=g.user, bleskomat_vars=bleskomat_vars
|
||||
)
|
||||
|
||||
@@ -28,7 +28,12 @@ async def api_bleskomats():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([bleskomat._asdict() for bleskomat in await get_bleskomats(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify(
|
||||
[bleskomat._asdict() for bleskomat in await get_bleskomats(wallet_ids)]
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@bleskomat_ext.route("/api/v1/bleskomat/<bleskomat_id>", methods=["GET"])
|
||||
@@ -37,7 +42,10 @@ async def api_bleskomat_retrieve(bleskomat_id):
|
||||
bleskomat = await get_bleskomat(bleskomat_id)
|
||||
|
||||
if not bleskomat or bleskomat.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Bleskomat configuration not found."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "Bleskomat configuration not found."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
|
||||
return jsonify(bleskomat._asdict()), HTTPStatus.OK
|
||||
|
||||
@@ -48,8 +56,16 @@ async def api_bleskomat_retrieve(bleskomat_id):
|
||||
@api_validate_post_request(
|
||||
schema={
|
||||
"name": {"type": "string", "empty": False, "required": True},
|
||||
"fiat_currency": {"type": "string", "allowed": fiat_currencies.keys(), "required": True},
|
||||
"exchange_rate_provider": {"type": "string", "allowed": exchange_rate_providers.keys(), "required": True},
|
||||
"fiat_currency": {
|
||||
"type": "string",
|
||||
"allowed": fiat_currencies.keys(),
|
||||
"required": True,
|
||||
},
|
||||
"exchange_rate_provider": {
|
||||
"type": "string",
|
||||
"allowed": exchange_rate_providers.keys(),
|
||||
"required": True,
|
||||
},
|
||||
"fee": {"type": ["string", "float", "number", "integer"], "required": True},
|
||||
}
|
||||
)
|
||||
@@ -58,23 +74,35 @@ async def api_bleskomat_create_or_update(bleskomat_id=None):
|
||||
try:
|
||||
fiat_currency = g.data["fiat_currency"]
|
||||
exchange_rate_provider = g.data["exchange_rate_provider"]
|
||||
rate = await fetch_fiat_exchange_rate(currency=fiat_currency, provider=exchange_rate_provider)
|
||||
rate = await fetch_fiat_exchange_rate(
|
||||
currency=fiat_currency, provider=exchange_rate_provider
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return (
|
||||
jsonify({"message": f'Failed to fetch BTC/{fiat_currency} currency pair from "{exchange_rate_provider}"'}),
|
||||
jsonify(
|
||||
{
|
||||
"message": f'Failed to fetch BTC/{fiat_currency} currency pair from "{exchange_rate_provider}"'
|
||||
}
|
||||
),
|
||||
HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
|
||||
if bleskomat_id:
|
||||
bleskomat = await get_bleskomat(bleskomat_id)
|
||||
if not bleskomat or bleskomat.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Bleskomat configuration not found."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "Bleskomat configuration not found."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
bleskomat = await update_bleskomat(bleskomat_id, **g.data)
|
||||
else:
|
||||
bleskomat = await create_bleskomat(wallet_id=g.wallet.id, **g.data)
|
||||
|
||||
return jsonify(bleskomat._asdict()), HTTPStatus.OK if bleskomat_id else HTTPStatus.CREATED
|
||||
return (
|
||||
jsonify(bleskomat._asdict()),
|
||||
HTTPStatus.OK if bleskomat_id else HTTPStatus.CREATED,
|
||||
)
|
||||
|
||||
|
||||
@bleskomat_ext.route("/api/v1/bleskomat/<bleskomat_id>", methods=["DELETE"])
|
||||
@@ -83,7 +111,10 @@ async def api_bleskomat_delete(bleskomat_id):
|
||||
bleskomat = await get_bleskomat(bleskomat_id)
|
||||
|
||||
if not bleskomat or bleskomat.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Bleskomat configuration not found."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "Bleskomat configuration not found."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
|
||||
await delete_bleskomat(bleskomat_id)
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_captcha")
|
||||
|
||||
captcha_ext: Blueprint = Blueprint("captcha", __name__, static_folder="static", template_folder="templates")
|
||||
captcha_ext: Blueprint = Blueprint(
|
||||
"captcha", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -7,7 +7,13 @@ from .models import Captcha
|
||||
|
||||
|
||||
async def create_captcha(
|
||||
*, wallet_id: str, url: str, memo: str, description: Optional[str] = None, amount: int = 0, remembers: bool = True
|
||||
*,
|
||||
wallet_id: str,
|
||||
url: str,
|
||||
memo: str,
|
||||
description: Optional[str] = None,
|
||||
amount: int = 0,
|
||||
remembers: bool = True,
|
||||
) -> Captcha:
|
||||
captcha_id = urlsafe_short_hash()
|
||||
await db.execute(
|
||||
@@ -34,7 +40,9 @@ async def get_captchas(wallet_ids: Union[str, List[str]]) -> List[Captcha]:
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM captchas WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM captchas WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [Captcha.from_row(row) for row in rows]
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@ async def m002_redux(db):
|
||||
)
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS wallet_idx ON captchas (wallet)")
|
||||
|
||||
for row in [list(row) for row in await db.fetchall("SELECT * FROM captchas_old")]:
|
||||
for row in [
|
||||
list(row) for row in await db.fetchall("SELECT * FROM captchas_old")
|
||||
]:
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO captchas (
|
||||
|
||||
@@ -16,5 +16,7 @@ async def index():
|
||||
|
||||
@captcha_ext.route("/<captcha_id>")
|
||||
async def display(captcha_id):
|
||||
captcha = await get_captcha(captcha_id) or abort(HTTPStatus.NOT_FOUND, "captcha does not exist.")
|
||||
captcha = await get_captcha(captcha_id) or abort(
|
||||
HTTPStatus.NOT_FOUND, "captcha does not exist."
|
||||
)
|
||||
return await render_template("captcha/display.html", captcha=captcha)
|
||||
|
||||
@@ -17,7 +17,10 @@ async def api_captchas():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([captcha._asdict() for captcha in await get_captchas(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([captcha._asdict() for captcha in await get_captchas(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@captcha_ext.route("/api/v1/captchas", methods=["POST"])
|
||||
@@ -26,7 +29,12 @@ async def api_captchas():
|
||||
schema={
|
||||
"url": {"type": "string", "empty": False, "required": True},
|
||||
"memo": {"type": "string", "empty": False, "required": True},
|
||||
"description": {"type": "string", "empty": True, "nullable": True, "required": False},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"empty": True,
|
||||
"nullable": True,
|
||||
"required": False,
|
||||
},
|
||||
"amount": {"type": "integer", "min": 0, "required": True},
|
||||
"remembers": {"type": "boolean", "required": True},
|
||||
}
|
||||
@@ -53,26 +61,41 @@ async def api_captcha_delete(captcha_id):
|
||||
|
||||
|
||||
@captcha_ext.route("/api/v1/captchas/<captcha_id>/invoice", methods=["POST"])
|
||||
@api_validate_post_request(schema={"amount": {"type": "integer", "min": 1, "required": True}})
|
||||
@api_validate_post_request(
|
||||
schema={"amount": {"type": "integer", "min": 1, "required": True}}
|
||||
)
|
||||
async def api_captcha_create_invoice(captcha_id):
|
||||
captcha = await get_captcha(captcha_id)
|
||||
|
||||
if g.data["amount"] < captcha.amount:
|
||||
return jsonify({"message": f"Minimum amount is {captcha.amount} sat."}), HTTPStatus.BAD_REQUEST
|
||||
return (
|
||||
jsonify({"message": f"Minimum amount is {captcha.amount} sat."}),
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
|
||||
try:
|
||||
amount = g.data["amount"] if g.data["amount"] > captcha.amount else captcha.amount
|
||||
amount = (
|
||||
g.data["amount"] if g.data["amount"] > captcha.amount else captcha.amount
|
||||
)
|
||||
payment_hash, payment_request = await create_invoice(
|
||||
wallet_id=captcha.wallet, amount=amount, memo=f"{captcha.memo}", extra={"tag": "captcha"}
|
||||
wallet_id=captcha.wallet,
|
||||
amount=amount,
|
||||
memo=f"{captcha.memo}",
|
||||
extra={"tag": "captcha"},
|
||||
)
|
||||
except Exception as e:
|
||||
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
|
||||
return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.CREATED
|
||||
return (
|
||||
jsonify({"payment_hash": payment_hash, "payment_request": payment_request}),
|
||||
HTTPStatus.CREATED,
|
||||
)
|
||||
|
||||
|
||||
@captcha_ext.route("/api/v1/captchas/<captcha_id>/check_invoice", methods=["POST"])
|
||||
@api_validate_post_request(schema={"payment_hash": {"type": "string", "empty": False, "required": True}})
|
||||
@api_validate_post_request(
|
||||
schema={"payment_hash": {"type": "string", "empty": False, "required": True}}
|
||||
)
|
||||
async def api_paywal_check_invoice(captcha_id):
|
||||
captcha = await get_captcha(captcha_id)
|
||||
|
||||
@@ -90,6 +113,9 @@ async def api_paywal_check_invoice(captcha_id):
|
||||
payment = await wallet.get_payment(g.data["payment_hash"])
|
||||
await payment.set_pending(False)
|
||||
|
||||
return jsonify({"paid": True, "url": captcha.url, "remembers": captcha.remembers}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"paid": True, "url": captcha.url, "remembers": captcha.remembers}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
return jsonify({"paid": False}), HTTPStatus.OK
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from quart import Blueprint
|
||||
|
||||
|
||||
diagonalley_ext: Blueprint = Blueprint("diagonalley", __name__, static_folder="static", template_folder="templates")
|
||||
diagonalley_ext: Blueprint = Blueprint(
|
||||
"diagonalley", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -21,7 +21,14 @@ regex = re.compile(
|
||||
|
||||
|
||||
def create_diagonalleys_product(
|
||||
*, wallet_id: str, product: str, categories: str, description: str, image: str, price: int, quantity: int
|
||||
*,
|
||||
wallet_id: str,
|
||||
product: str,
|
||||
categories: str,
|
||||
description: str,
|
||||
image: str,
|
||||
price: int,
|
||||
quantity: int,
|
||||
) -> Products:
|
||||
with open_ext_db("diagonalley") as db:
|
||||
product_id = urlsafe_b64encode(uuid4().bytes_le).decode("utf-8")
|
||||
@@ -30,7 +37,16 @@ def create_diagonalleys_product(
|
||||
INSERT INTO products (id, wallet, product, categories, description, image, price, quantity)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(product_id, wallet_id, product, categories, description, image, price, quantity),
|
||||
(
|
||||
product_id,
|
||||
wallet_id,
|
||||
product,
|
||||
categories,
|
||||
description,
|
||||
image,
|
||||
price,
|
||||
quantity,
|
||||
),
|
||||
)
|
||||
|
||||
return get_diagonalleys_product(product_id)
|
||||
@@ -40,7 +56,9 @@ def update_diagonalleys_product(product_id: str, **kwargs) -> Optional[Indexers]
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
|
||||
with open_ext_db("diagonalley") as db:
|
||||
db.execute(f"UPDATE products SET {q} WHERE id = ?", (*kwargs.values(), product_id))
|
||||
db.execute(
|
||||
f"UPDATE products SET {q} WHERE id = ?", (*kwargs.values(), product_id)
|
||||
)
|
||||
row = db.fetchone("SELECT * FROM products WHERE id = ?", (product_id,))
|
||||
|
||||
return get_diagonalleys_indexer(product_id)
|
||||
@@ -59,7 +77,9 @@ def get_diagonalleys_products(wallet_ids: Union[str, List[str]]) -> List[Product
|
||||
|
||||
with open_ext_db("diagonalley") as db:
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = db.fetchall(f"SELECT * FROM products WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = db.fetchall(
|
||||
f"SELECT * FROM products WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [Products(**row) for row in rows]
|
||||
|
||||
@@ -110,7 +130,9 @@ def update_diagonalleys_indexer(indexer_id: str, **kwargs) -> Optional[Indexers]
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
|
||||
with open_ext_db("diagonalley") as db:
|
||||
db.execute(f"UPDATE indexers SET {q} WHERE id = ?", (*kwargs.values(), indexer_id))
|
||||
db.execute(
|
||||
f"UPDATE indexers SET {q} WHERE id = ?", (*kwargs.values(), indexer_id)
|
||||
)
|
||||
row = db.fetchone("SELECT * FROM indexers WHERE id = ?", (indexer_id,))
|
||||
|
||||
return get_diagonalleys_indexer(indexer_id)
|
||||
@@ -154,7 +176,9 @@ def get_diagonalleys_indexers(wallet_ids: Union[str, List[str]]) -> List[Indexer
|
||||
|
||||
with open_ext_db("diagonalley") as db:
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = db.fetchall(f"SELECT * FROM indexers WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = db.fetchall(
|
||||
f"SELECT * FROM indexers WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
for r in rows:
|
||||
try:
|
||||
@@ -181,7 +205,9 @@ def get_diagonalleys_indexers(wallet_ids: Union[str, List[str]]) -> List[Indexer
|
||||
print("An exception occurred")
|
||||
with open_ext_db("diagonalley") as db:
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = db.fetchall(f"SELECT * FROM indexers WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = db.fetchall(
|
||||
f"SELECT * FROM indexers WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
return [Indexers(**row) for row in rows]
|
||||
|
||||
|
||||
@@ -213,7 +239,19 @@ def create_diagonalleys_order(
|
||||
INSERT INTO orders (id, productid, wallet, product, quantity, shippingzone, address, email, invoiceid, paid, shipped)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(order_id, productid, wallet, product, quantity, shippingzone, address, email, invoiceid, False, False),
|
||||
(
|
||||
order_id,
|
||||
productid,
|
||||
wallet,
|
||||
product,
|
||||
quantity,
|
||||
shippingzone,
|
||||
address,
|
||||
email,
|
||||
invoiceid,
|
||||
False,
|
||||
False,
|
||||
),
|
||||
)
|
||||
|
||||
return get_diagonalleys_order(order_id)
|
||||
@@ -232,7 +270,9 @@ def get_diagonalleys_orders(wallet_ids: Union[str, List[str]]) -> List[Orders]:
|
||||
|
||||
with open_ext_db("diagonalley") as db:
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = db.fetchall(f"SELECT * FROM orders WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = db.fetchall(
|
||||
f"SELECT * FROM orders WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
for r in rows:
|
||||
PAID = WALLET.get_invoice_status(r["invoiceid"]).paid
|
||||
if PAID:
|
||||
@@ -244,7 +284,9 @@ def get_diagonalleys_orders(wallet_ids: Union[str, List[str]]) -> List[Orders]:
|
||||
r["id"],
|
||||
),
|
||||
)
|
||||
rows = db.fetchall(f"SELECT * FROM orders WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = db.fetchall(
|
||||
f"SELECT * FROM orders WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
return [Orders(**row) for row in rows]
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,12 @@ async def api_diagonalley_products():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = get_user(g.wallet.user).wallet_ids
|
||||
|
||||
return jsonify([product._asdict() for product in get_diagonalleys_products(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify(
|
||||
[product._asdict() for product in get_diagonalleys_products(wallet_ids)]
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@diagonalley_ext.route("/api/v1/diagonalley/products", methods=["POST"])
|
||||
@@ -58,16 +63,25 @@ async def api_diagonalley_product_create(product_id=None):
|
||||
product = get_diagonalleys_indexer(product_id)
|
||||
|
||||
if not product:
|
||||
return jsonify({"message": "Withdraw product does not exist."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "Withdraw product does not exist."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
|
||||
if product.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Not your withdraw product."}), HTTPStatus.FORBIDDEN
|
||||
return (
|
||||
jsonify({"message": "Not your withdraw product."}),
|
||||
HTTPStatus.FORBIDDEN,
|
||||
)
|
||||
|
||||
product = update_diagonalleys_product(product_id, **g.data)
|
||||
else:
|
||||
product = create_diagonalleys_product(wallet_id=g.wallet.id, **g.data)
|
||||
|
||||
return jsonify(product._asdict()), HTTPStatus.OK if product_id else HTTPStatus.CREATED
|
||||
return (
|
||||
jsonify(product._asdict()),
|
||||
HTTPStatus.OK if product_id else HTTPStatus.CREATED,
|
||||
)
|
||||
|
||||
|
||||
@diagonalley_ext.route("/api/v1/diagonalley/products/<product_id>", methods=["DELETE"])
|
||||
@@ -97,7 +111,12 @@ async def api_diagonalley_indexers():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = get_user(g.wallet.user).wallet_ids
|
||||
|
||||
return jsonify([indexer._asdict() for indexer in get_diagonalleys_indexers(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify(
|
||||
[indexer._asdict() for indexer in get_diagonalleys_indexers(wallet_ids)]
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@diagonalley_ext.route("/api/v1/diagonalley/indexers", methods=["POST"])
|
||||
@@ -120,16 +139,25 @@ async def api_diagonalley_indexer_create(indexer_id=None):
|
||||
indexer = get_diagonalleys_indexer(indexer_id)
|
||||
|
||||
if not indexer:
|
||||
return jsonify({"message": "Withdraw indexer does not exist."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "Withdraw indexer does not exist."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
|
||||
if indexer.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Not your withdraw indexer."}), HTTPStatus.FORBIDDEN
|
||||
return (
|
||||
jsonify({"message": "Not your withdraw indexer."}),
|
||||
HTTPStatus.FORBIDDEN,
|
||||
)
|
||||
|
||||
indexer = update_diagonalleys_indexer(indexer_id, **g.data)
|
||||
else:
|
||||
indexer = create_diagonalleys_indexer(wallet_id=g.wallet.id, **g.data)
|
||||
|
||||
return jsonify(indexer._asdict()), HTTPStatus.OK if indexer_id else HTTPStatus.CREATED
|
||||
return (
|
||||
jsonify(indexer._asdict()),
|
||||
HTTPStatus.OK if indexer_id else HTTPStatus.CREATED,
|
||||
)
|
||||
|
||||
|
||||
@diagonalley_ext.route("/api/v1/diagonalley/indexers/<indexer_id>", methods=["DELETE"])
|
||||
@@ -159,7 +187,10 @@ async def api_diagonalley_orders():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = get_user(g.wallet.user).wallet_ids
|
||||
|
||||
return jsonify([order._asdict() for order in get_diagonalleys_orders(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([order._asdict() for order in get_diagonalleys_orders(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@diagonalley_ext.route("/api/v1/diagonalley/orders", methods=["POST"])
|
||||
@@ -221,13 +252,20 @@ async def api_diagonalleys_order_shipped(order_id):
|
||||
)
|
||||
order = db.fetchone("SELECT * FROM orders WHERE id = ?", (order_id,))
|
||||
|
||||
return jsonify([order._asdict() for order in get_diagonalleys_orders(order["wallet"])]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify(
|
||||
[order._asdict() for order in get_diagonalleys_orders(order["wallet"])]
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
###List products based on indexer id
|
||||
|
||||
|
||||
@diagonalley_ext.route("/api/v1/diagonalley/stall/products/<indexer_id>", methods=["GET"])
|
||||
@diagonalley_ext.route(
|
||||
"/api/v1/diagonalley/stall/products/<indexer_id>", methods=["GET"]
|
||||
)
|
||||
async def api_diagonalleys_stall_products(indexer_id):
|
||||
with open_ext_db("diagonalley") as db:
|
||||
rows = db.fetchone("SELECT * FROM indexers WHERE id = ?", (indexer_id,))
|
||||
@@ -239,13 +277,20 @@ async def api_diagonalleys_stall_products(indexer_id):
|
||||
if not products:
|
||||
return jsonify({"message": "No products"}), HTTPStatus.NOT_FOUND
|
||||
|
||||
return jsonify([products._asdict() for products in get_diagonalleys_products(rows[1])]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify(
|
||||
[products._asdict() for products in get_diagonalleys_products(rows[1])]
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
###Check a product has been shipped
|
||||
|
||||
|
||||
@diagonalley_ext.route("/api/v1/diagonalley/stall/checkshipped/<checking_id>", methods=["GET"])
|
||||
@diagonalley_ext.route(
|
||||
"/api/v1/diagonalley/stall/checkshipped/<checking_id>", methods=["GET"]
|
||||
)
|
||||
async def api_diagonalleys_stall_checkshipped(checking_id):
|
||||
with open_ext_db("diagonalley") as db:
|
||||
rows = db.fetchone("SELECT * FROM orders WHERE invoiceid = ?", (checking_id,))
|
||||
@@ -276,7 +321,9 @@ async def api_diagonalley_stall_order(indexer_id):
|
||||
shippingcost = shipping.zone2cost
|
||||
|
||||
checking_id, payment_request = create_invoice(
|
||||
wallet_id=product.wallet, amount=shippingcost + (g.data["quantity"] * product.price), memo=g.data["id"]
|
||||
wallet_id=product.wallet,
|
||||
amount=shippingcost + (g.data["quantity"] * product.price),
|
||||
memo=g.data["id"],
|
||||
)
|
||||
selling_id = urlsafe_b64encode(uuid4().bytes_le).decode("utf-8")
|
||||
with open_ext_db("diagonalley") as db:
|
||||
@@ -299,4 +346,7 @@ async def api_diagonalley_stall_order(indexer_id):
|
||||
False,
|
||||
),
|
||||
)
|
||||
return jsonify({"checking_id": checking_id, "payment_request": payment_request}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"checking_id": checking_id, "payment_request": payment_request}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,9 @@ from lnbits.db import Database
|
||||
db = Database("ext_events")
|
||||
|
||||
|
||||
events_ext: Blueprint = Blueprint("events", __name__, static_folder="static", template_folder="templates")
|
||||
events_ext: Blueprint = Blueprint(
|
||||
"events", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -9,7 +9,9 @@ from .models import Tickets, Events
|
||||
# TICKETS
|
||||
|
||||
|
||||
async def create_ticket(payment_hash: str, wallet: str, event: str, name: str, email: str) -> Tickets:
|
||||
async def create_ticket(
|
||||
payment_hash: str, wallet: str, event: str, name: str, email: str
|
||||
) -> Tickets:
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO ticket (id, wallet, event, name, email, registered, paid)
|
||||
@@ -64,7 +66,9 @@ async def get_tickets(wallet_ids: Union[str, List[str]]) -> List[Tickets]:
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM ticket WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM ticket WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
return [Tickets(**row) for row in rows]
|
||||
|
||||
|
||||
@@ -113,7 +117,9 @@ async def create_event(
|
||||
|
||||
async def update_event(event_id: str, **kwargs) -> Events:
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
await db.execute(f"UPDATE events SET {q} WHERE id = ?", (*kwargs.values(), event_id))
|
||||
await db.execute(
|
||||
f"UPDATE events SET {q} WHERE id = ?", (*kwargs.values(), event_id)
|
||||
)
|
||||
event = await get_event(event_id)
|
||||
assert event, "Newly updated event couldn't be retrieved"
|
||||
return event
|
||||
@@ -129,7 +135,9 @@ async def get_events(wallet_ids: Union[str, List[str]]) -> List[Events]:
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM events WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM events WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [Events(**row) for row in rows]
|
||||
|
||||
@@ -142,7 +150,9 @@ async def delete_event(event_id: str) -> None:
|
||||
|
||||
|
||||
async def get_event_tickets(event_id: str, wallet_id: str) -> List[Tickets]:
|
||||
rows = await db.fetchall("SELECT * FROM ticket WHERE wallet = ? AND event = ?", (wallet_id, event_id))
|
||||
rows = await db.fetchall(
|
||||
"SELECT * FROM ticket WHERE wallet = ? AND event = ?", (wallet_id, event_id)
|
||||
)
|
||||
return [Tickets(**row) for row in rows]
|
||||
|
||||
|
||||
|
||||
@@ -23,12 +23,16 @@ async def display(event_id):
|
||||
|
||||
if event.amount_tickets < 1:
|
||||
return await render_template(
|
||||
"events/error.html", event_name=event.name, event_error="Sorry, tickets are sold out :("
|
||||
"events/error.html",
|
||||
event_name=event.name,
|
||||
event_error="Sorry, tickets are sold out :(",
|
||||
)
|
||||
datetime_object = datetime.strptime(event.closing_date, "%Y-%m-%d").date()
|
||||
if date.today() > datetime_object:
|
||||
return await render_template(
|
||||
"events/error.html", event_name=event.name, event_error="Sorry, ticket closing date has passed :("
|
||||
"events/error.html",
|
||||
event_name=event.name,
|
||||
event_error="Sorry, ticket closing date has passed :(",
|
||||
)
|
||||
|
||||
return await render_template(
|
||||
@@ -51,7 +55,10 @@ async def ticket(ticket_id):
|
||||
abort(HTTPStatus.NOT_FOUND, "Event does not exist.")
|
||||
|
||||
return await render_template(
|
||||
"events/ticket.html", ticket_id=ticket_id, ticket_name=event.name, ticket_info=event.info
|
||||
"events/ticket.html",
|
||||
ticket_id=ticket_id,
|
||||
ticket_name=event.name,
|
||||
ticket_info=event.info,
|
||||
)
|
||||
|
||||
|
||||
@@ -62,5 +69,8 @@ async def register(event_id):
|
||||
abort(HTTPStatus.NOT_FOUND, "Event does not exist.")
|
||||
|
||||
return await render_template(
|
||||
"events/register.html", event_id=event_id, event_name=event.name, wallet_id=event.wallet
|
||||
"events/register.html",
|
||||
event_id=event_id,
|
||||
event_name=event.name,
|
||||
wallet_id=event.wallet,
|
||||
)
|
||||
|
||||
@@ -33,7 +33,10 @@ async def api_events():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([event._asdict() for event in await get_events(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([event._asdict() for event in await get_events(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@events_ext.route("/api/v1/events", methods=["POST"])
|
||||
@@ -92,7 +95,10 @@ async def api_tickets():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([ticket._asdict() for ticket in await get_tickets(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([ticket._asdict() for ticket in await get_tickets(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@events_ext.route("/api/v1/tickets/<event_id>/<sats>", methods=["POST"])
|
||||
@@ -108,17 +114,25 @@ async def api_ticket_make_ticket(event_id, sats):
|
||||
return jsonify({"message": "Event does not exist."}), HTTPStatus.NOT_FOUND
|
||||
try:
|
||||
payment_hash, payment_request = await create_invoice(
|
||||
wallet_id=event.wallet, amount=int(sats), memo=f"{event_id}", extra={"tag": "events"}
|
||||
wallet_id=event.wallet,
|
||||
amount=int(sats),
|
||||
memo=f"{event_id}",
|
||||
extra={"tag": "events"},
|
||||
)
|
||||
except Exception as e:
|
||||
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
|
||||
ticket = await create_ticket(payment_hash=payment_hash, wallet=event.wallet, event=event_id, **g.data)
|
||||
ticket = await create_ticket(
|
||||
payment_hash=payment_hash, wallet=event.wallet, event=event_id, **g.data
|
||||
)
|
||||
|
||||
if not ticket:
|
||||
return jsonify({"message": "Event could not be fetched."}), HTTPStatus.NOT_FOUND
|
||||
|
||||
return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"payment_hash": payment_hash, "payment_request": payment_request}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@events_ext.route("/api/v1/tickets/<payment_hash>", methods=["GET"])
|
||||
@@ -163,7 +177,14 @@ async def api_ticket_delete(ticket_id):
|
||||
@events_ext.route("/api/v1/eventtickets/<wallet_id>/<event_id>", methods=["GET"])
|
||||
async def api_event_tickets(wallet_id, event_id):
|
||||
return (
|
||||
jsonify([ticket._asdict() for ticket in await get_event_tickets(wallet_id=wallet_id, event_id=event_id)]),
|
||||
jsonify(
|
||||
[
|
||||
ticket._asdict()
|
||||
for ticket in await get_event_tickets(
|
||||
wallet_id=wallet_id, event_id=event_id
|
||||
)
|
||||
]
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
@@ -177,4 +198,7 @@ async def api_event_register_ticket(ticket_id):
|
||||
if ticket.registered == True:
|
||||
return jsonify({"message": "Ticket already registered"}), HTTPStatus.FORBIDDEN
|
||||
|
||||
return jsonify([ticket._asdict() for ticket in await reg_ticket(ticket_id)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([ticket._asdict() for ticket in await reg_ticket(ticket_id)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_example")
|
||||
|
||||
example_ext: Blueprint = Blueprint("example", __name__, static_folder="static", template_folder="templates")
|
||||
example_ext: Blueprint = Blueprint(
|
||||
"example", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_lndhub")
|
||||
|
||||
lndhub_ext: Blueprint = Blueprint("lndhub", __name__, static_folder="static", template_folder="templates")
|
||||
lndhub_ext: Blueprint = Blueprint(
|
||||
"lndhub", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -13,11 +13,15 @@ def check_wallet(requires_admin=False):
|
||||
key_type, key = b64decode(token).decode("utf-8").split(":")
|
||||
|
||||
if requires_admin and key_type != "admin":
|
||||
return jsonify({"error": True, "code": 2, "message": "insufficient permissions"})
|
||||
return jsonify(
|
||||
{"error": True, "code": 2, "message": "insufficient permissions"}
|
||||
)
|
||||
|
||||
g.wallet = await get_wallet_for_key(key, key_type)
|
||||
if not g.wallet:
|
||||
return jsonify({"error": True, "code": 2, "message": "insufficient permissions"})
|
||||
return jsonify(
|
||||
{"error": True, "code": 2, "message": "insufficient permissions"}
|
||||
)
|
||||
return await view(**kwargs)
|
||||
|
||||
return wrapped_view
|
||||
|
||||
@@ -23,14 +23,20 @@ async def lndhub_getinfo():
|
||||
schema={
|
||||
"login": {"type": "string", "required": True, "excludes": "refresh_token"},
|
||||
"password": {"type": "string", "required": True, "excludes": "refresh_token"},
|
||||
"refresh_token": {"type": "string", "required": True, "excludes": ["login", "password"]},
|
||||
"refresh_token": {
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"excludes": ["login", "password"],
|
||||
},
|
||||
}
|
||||
)
|
||||
async def lndhub_auth():
|
||||
token = (
|
||||
g.data["refresh_token"]
|
||||
if "refresh_token" in g.data and g.data["refresh_token"]
|
||||
else urlsafe_b64encode((g.data["login"] + ":" + g.data["password"]).encode("utf-8")).decode("ascii")
|
||||
else urlsafe_b64encode(
|
||||
(g.data["login"] + ":" + g.data["password"]).encode("utf-8")
|
||||
).decode("ascii")
|
||||
)
|
||||
return jsonify({"refresh_token": token, "access_token": token})
|
||||
|
||||
@@ -120,9 +126,15 @@ async def lndhub_balance():
|
||||
@check_wallet()
|
||||
async def lndhub_gettxs():
|
||||
for payment in await g.wallet.get_payments(
|
||||
complete=False, pending=True, outgoing=True, incoming=False, exclude_uncheckable=True
|
||||
complete=False,
|
||||
pending=True,
|
||||
outgoing=True,
|
||||
incoming=False,
|
||||
exclude_uncheckable=True,
|
||||
):
|
||||
await payment.set_pending(WALLET.get_payment_status(payment.checking_id).pending)
|
||||
await payment.set_pending(
|
||||
WALLET.get_payment_status(payment.checking_id).pending
|
||||
)
|
||||
|
||||
limit = int(request.args.get("limit", 200))
|
||||
return jsonify(
|
||||
@@ -135,10 +147,16 @@ async def lndhub_gettxs():
|
||||
"fee": payment.fee,
|
||||
"value": int(payment.amount / 1000),
|
||||
"timestamp": payment.time,
|
||||
"memo": payment.memo if not payment.pending else "Payment in transition",
|
||||
"memo": payment.memo
|
||||
if not payment.pending
|
||||
else "Payment in transition",
|
||||
}
|
||||
for payment in reversed(
|
||||
(await g.wallet.get_payments(pending=True, complete=True, outgoing=True, incoming=False))[:limit]
|
||||
(
|
||||
await g.wallet.get_payments(
|
||||
pending=True, complete=True, outgoing=True, incoming=False
|
||||
)
|
||||
)[:limit]
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -149,9 +167,15 @@ async def lndhub_gettxs():
|
||||
async def lndhub_getuserinvoices():
|
||||
await delete_expired_invoices()
|
||||
for invoice in await g.wallet.get_payments(
|
||||
complete=False, pending=True, outgoing=False, incoming=True, exclude_uncheckable=True
|
||||
complete=False,
|
||||
pending=True,
|
||||
outgoing=False,
|
||||
incoming=True,
|
||||
exclude_uncheckable=True,
|
||||
):
|
||||
await invoice.set_pending(WALLET.get_invoice_status(invoice.checking_id).pending)
|
||||
await invoice.set_pending(
|
||||
WALLET.get_invoice_status(invoice.checking_id).pending
|
||||
)
|
||||
|
||||
limit = int(request.args.get("limit", 200))
|
||||
return jsonify(
|
||||
@@ -169,7 +193,11 @@ async def lndhub_getuserinvoices():
|
||||
"type": "user_invoice",
|
||||
}
|
||||
for invoice in reversed(
|
||||
(await g.wallet.get_payments(pending=True, complete=True, incoming=True, outgoing=False))[:limit]
|
||||
(
|
||||
await g.wallet.get_payments(
|
||||
pending=True, complete=True, incoming=True, outgoing=False
|
||||
)
|
||||
)[:limit]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_lnticket")
|
||||
|
||||
lnticket_ext: Blueprint = Blueprint("lnticket", __name__, static_folder="static", template_folder="templates")
|
||||
lnticket_ext: Blueprint = Blueprint(
|
||||
"lnticket", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -60,7 +60,12 @@ async def set_ticket_paid(payment_hash: str) -> Tickets:
|
||||
try:
|
||||
r = await client.post(
|
||||
formdata.webhook,
|
||||
json={"form": ticket.form, "name": ticket.name, "email": ticket.email, "content": ticket.ltext},
|
||||
json={
|
||||
"form": ticket.form,
|
||||
"name": ticket.name,
|
||||
"email": ticket.email,
|
||||
"content": ticket.ltext,
|
||||
},
|
||||
timeout=40,
|
||||
)
|
||||
except AssertionError:
|
||||
@@ -80,7 +85,9 @@ async def get_tickets(wallet_ids: Union[str, List[str]]) -> List[Tickets]:
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM ticket WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM ticket WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [Tickets(**row) for row in rows]
|
||||
|
||||
@@ -93,7 +100,12 @@ async def delete_ticket(ticket_id: str) -> None:
|
||||
|
||||
|
||||
async def create_form(
|
||||
*, wallet: str, name: str, webhook: Optional[str] = None, description: str, costpword: int
|
||||
*,
|
||||
wallet: str,
|
||||
name: str,
|
||||
webhook: Optional[str] = None,
|
||||
description: str,
|
||||
costpword: int,
|
||||
) -> Forms:
|
||||
form_id = urlsafe_short_hash()
|
||||
await db.execute(
|
||||
@@ -127,7 +139,9 @@ async def get_forms(wallet_ids: Union[str, List[str]]) -> List[Forms]:
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM form WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM form WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [Forms(**row) for row in rows]
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ async def api_forms():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([form._asdict() for form in await get_forms(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([form._asdict() for form in await get_forms(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@lnticket_ext.route("/api/v1/forms", methods=["POST"])
|
||||
@@ -90,7 +93,10 @@ async def api_tickets():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([form._asdict() for form in await get_tickets(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([form._asdict() for form in await get_tickets(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@lnticket_ext.route("/api/v1/tickets/<form_id>", methods=["POST"])
|
||||
@@ -117,12 +123,20 @@ async def api_ticket_make_ticket(form_id):
|
||||
extra={"tag": "lnticket"},
|
||||
)
|
||||
|
||||
ticket = await create_ticket(payment_hash=payment_hash, wallet=form.wallet, **g.data)
|
||||
ticket = await create_ticket(
|
||||
payment_hash=payment_hash, wallet=form.wallet, **g.data
|
||||
)
|
||||
|
||||
if not ticket:
|
||||
return jsonify({"message": "LNTicket could not be fetched."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "LNTicket could not be fetched."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
|
||||
return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"payment_hash": payment_hash, "payment_request": payment_request}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@lnticket_ext.route("/api/v1/tickets/<payment_hash>", methods=["GET"])
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_lnurlp")
|
||||
|
||||
lnurlp_ext: Blueprint = Blueprint("lnurlp", __name__, static_folder="static", template_folder="templates")
|
||||
lnurlp_ext: Blueprint = Blueprint(
|
||||
"lnurlp", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -74,14 +74,18 @@ async def get_pay_links(wallet_ids: Union[str, List[str]]) -> List[PayLink]:
|
||||
|
||||
async def update_pay_link(link_id: int, **kwargs) -> Optional[PayLink]:
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
await db.execute(f"UPDATE pay_links SET {q} WHERE id = ?", (*kwargs.values(), link_id))
|
||||
await db.execute(
|
||||
f"UPDATE pay_links SET {q} WHERE id = ?", (*kwargs.values(), link_id)
|
||||
)
|
||||
row = await db.fetchone("SELECT * FROM pay_links WHERE id = ?", (link_id,))
|
||||
return PayLink.from_row(row) if row else None
|
||||
|
||||
|
||||
async def increment_pay_link(link_id: int, **kwargs) -> Optional[PayLink]:
|
||||
q = ", ".join([f"{field[0]} = {field[0]} + ?" for field in kwargs.items()])
|
||||
await db.execute(f"UPDATE pay_links SET {q} WHERE id = ?", (*kwargs.values(), link_id))
|
||||
await db.execute(
|
||||
f"UPDATE pay_links SET {q} WHERE id = ?", (*kwargs.values(), link_id)
|
||||
)
|
||||
row = await db.fetchone("SELECT * FROM pay_links WHERE id = ?", (link_id,))
|
||||
return PayLink.from_row(row) if row else None
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@ from .crud import increment_pay_link
|
||||
async def api_lnurl_response(link_id):
|
||||
link = await increment_pay_link(link_id, served_meta=1)
|
||||
if not link:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-pay not found."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "LNURL-pay not found."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
rate = await get_fiat_rate_satoshis(link.currency) if link.currency else 1
|
||||
resp = LnurlPayResponse(
|
||||
@@ -36,7 +39,10 @@ async def api_lnurl_response(link_id):
|
||||
async def api_lnurl_callback(link_id):
|
||||
link = await increment_pay_link(link_id, served_pr=1)
|
||||
if not link:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-pay not found."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "LNURL-pay not found."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
min, max = link.min, link.max
|
||||
rate = await get_fiat_rate_satoshis(link.currency) if link.currency else 1
|
||||
@@ -51,12 +57,20 @@ async def api_lnurl_callback(link_id):
|
||||
amount_received = int(request.args.get("amount"))
|
||||
if amount_received < min:
|
||||
return (
|
||||
jsonify(LnurlErrorResponse(reason=f"Amount {amount_received} is smaller than minimum {min}.").dict()),
|
||||
jsonify(
|
||||
LnurlErrorResponse(
|
||||
reason=f"Amount {amount_received} is smaller than minimum {min}."
|
||||
).dict()
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
elif amount_received > max:
|
||||
return (
|
||||
jsonify(LnurlErrorResponse(reason=f"Amount {amount_received} is greater than maximum {max}.").dict()),
|
||||
jsonify(
|
||||
LnurlErrorResponse(
|
||||
reason=f"Amount {amount_received} is greater than maximum {max}."
|
||||
).dict()
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
@@ -75,7 +89,9 @@ async def api_lnurl_callback(link_id):
|
||||
wallet_id=link.wallet,
|
||||
amount=int(amount_received / 1000),
|
||||
memo=link.description,
|
||||
description_hash=hashlib.sha256(link.lnurlpay_metadata.encode("utf-8")).digest(),
|
||||
description_hash=hashlib.sha256(
|
||||
link.lnurlpay_metadata.encode("utf-8")
|
||||
).digest(),
|
||||
extra={"tag": "lnurlp", "link": link.id, "comment": comment},
|
||||
)
|
||||
|
||||
|
||||
@@ -40,8 +40,12 @@ async def m003_min_max_comment_fiat(db):
|
||||
Support for min/max amounts, comments and fiat prices that get
|
||||
converted automatically to satoshis based on some API.
|
||||
"""
|
||||
await db.execute("ALTER TABLE pay_links ADD COLUMN currency TEXT;") # null = satoshis
|
||||
await db.execute("ALTER TABLE pay_links ADD COLUMN comment_chars INTEGER DEFAULT 0;")
|
||||
await db.execute(
|
||||
"ALTER TABLE pay_links ADD COLUMN currency TEXT;"
|
||||
) # null = satoshis
|
||||
await db.execute(
|
||||
"ALTER TABLE pay_links ADD COLUMN comment_chars INTEGER DEFAULT 0;"
|
||||
)
|
||||
await db.execute("ALTER TABLE pay_links RENAME COLUMN amount TO min;")
|
||||
await db.execute("ALTER TABLE pay_links ADD COLUMN max INTEGER;")
|
||||
await db.execute("UPDATE pay_links SET max = min;")
|
||||
|
||||
@@ -31,12 +31,21 @@ async def api_links():
|
||||
|
||||
try:
|
||||
return (
|
||||
jsonify([{**link._asdict(), **{"lnurl": link.lnurl}} for link in await get_pay_links(wallet_ids)]),
|
||||
jsonify(
|
||||
[
|
||||
{**link._asdict(), **{"lnurl": link.lnurl}}
|
||||
for link in await get_pay_links(wallet_ids)
|
||||
]
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
except LnurlInvalidUrl:
|
||||
return (
|
||||
jsonify({"message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor."}),
|
||||
jsonify(
|
||||
{
|
||||
"message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor."
|
||||
}
|
||||
),
|
||||
HTTPStatus.UPGRADE_REQUIRED,
|
||||
)
|
||||
|
||||
@@ -83,7 +92,10 @@ async def api_link_create_or_update(link_id=None):
|
||||
link = await get_pay_link(link_id)
|
||||
|
||||
if not link:
|
||||
return jsonify({"message": "Pay link does not exist."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "Pay link does not exist."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
|
||||
if link.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Not your pay link."}), HTTPStatus.FORBIDDEN
|
||||
@@ -92,7 +104,10 @@ async def api_link_create_or_update(link_id=None):
|
||||
else:
|
||||
link = await create_pay_link(wallet_id=g.wallet.id, **g.data)
|
||||
|
||||
return jsonify({**link._asdict(), **{"lnurl": link.lnurl}}), HTTPStatus.OK if link_id else HTTPStatus.CREATED
|
||||
return (
|
||||
jsonify({**link._asdict(), **{"lnurl": link.lnurl}}),
|
||||
HTTPStatus.OK if link_id else HTTPStatus.CREATED,
|
||||
)
|
||||
|
||||
|
||||
@lnurlp_ext.route("/api/v1/links/<link_id>", methods=["DELETE"])
|
||||
|
||||
@@ -4,7 +4,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_offlineshop")
|
||||
|
||||
offlineshop_ext: Blueprint = Blueprint("offlineshop", __name__, static_folder="static", template_folder="templates")
|
||||
offlineshop_ext: Blueprint = Blueprint(
|
||||
"offlineshop", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -18,7 +18,11 @@ async def lnurl_response(item_id):
|
||||
if not item.enabled:
|
||||
return jsonify({"status": "ERROR", "reason": "Item disabled."})
|
||||
|
||||
price_msat = (await fiat_amount_as_satoshis(item.price, item.unit) if item.unit != "sat" else item.price) * 1000
|
||||
price_msat = (
|
||||
await fiat_amount_as_satoshis(item.price, item.unit)
|
||||
if item.unit != "sat"
|
||||
else item.price
|
||||
) * 1000
|
||||
|
||||
resp = LnurlPayResponse(
|
||||
callback=url_for("offlineshop.lnurl_callback", item_id=item.id, _external=True),
|
||||
@@ -47,16 +51,26 @@ async def lnurl_callback(item_id):
|
||||
|
||||
amount_received = int(request.args.get("amount"))
|
||||
if amount_received < min:
|
||||
return jsonify(LnurlErrorResponse(reason=f"Amount {amount_received} is smaller than minimum {min}.").dict())
|
||||
return jsonify(
|
||||
LnurlErrorResponse(
|
||||
reason=f"Amount {amount_received} is smaller than minimum {min}."
|
||||
).dict()
|
||||
)
|
||||
elif amount_received > max:
|
||||
return jsonify(LnurlErrorResponse(reason=f"Amount {amount_received} is greater than maximum {max}.").dict())
|
||||
return jsonify(
|
||||
LnurlErrorResponse(
|
||||
reason=f"Amount {amount_received} is greater than maximum {max}."
|
||||
).dict()
|
||||
)
|
||||
|
||||
shop = await get_shop(item.shop)
|
||||
payment_hash, payment_request = await create_invoice(
|
||||
wallet_id=shop.wallet,
|
||||
amount=int(amount_received / 1000),
|
||||
memo=item.name,
|
||||
description_hash=hashlib.sha256((await item.lnurlpay_metadata()).encode("utf-8")).digest(),
|
||||
description_hash=hashlib.sha256(
|
||||
(await item.lnurlpay_metadata()).encode("utf-8")
|
||||
).digest(),
|
||||
extra={"tag": "offlineshop", "item": item.id},
|
||||
)
|
||||
|
||||
|
||||
@@ -89,7 +89,9 @@ class Item(NamedTuple):
|
||||
|
||||
@property
|
||||
def lnurl(self) -> str:
|
||||
return lnurl_encode(url_for("offlineshop.lnurl_response", item_id=self.id, _external=True))
|
||||
return lnurl_encode(
|
||||
url_for("offlineshop.lnurl_response", item_id=self.id, _external=True)
|
||||
)
|
||||
|
||||
def values(self):
|
||||
values = self._asdict()
|
||||
@@ -104,11 +106,15 @@ class Item(NamedTuple):
|
||||
|
||||
return LnurlPayMetadata(json.dumps(metadata))
|
||||
|
||||
def success_action(self, shop: Shop, payment_hash: str) -> Optional[LnurlPaySuccessAction]:
|
||||
def success_action(
|
||||
self, shop: Shop, payment_hash: str
|
||||
) -> Optional[LnurlPaySuccessAction]:
|
||||
if not shop.wordlist:
|
||||
return None
|
||||
|
||||
return UrlAction(
|
||||
url=url_for("offlineshop.confirmation_code", p=payment_hash, _external=True),
|
||||
url=url_for(
|
||||
"offlineshop.confirmation_code", p=payment_hash, _external=True
|
||||
),
|
||||
description="Open to get the confirmation code for your purchase.",
|
||||
)
|
||||
|
||||
@@ -24,7 +24,13 @@ async def print_qr_codes():
|
||||
for item_id in request.args.get("items").split(","):
|
||||
item = await get_item(item_id)
|
||||
if item:
|
||||
items.append({"lnurl": item.lnurl, "name": item.name, "price": f"{item.price} {item.unit}"})
|
||||
items.append(
|
||||
{
|
||||
"lnurl": item.lnurl,
|
||||
"name": item.name,
|
||||
"price": f"{item.price} {item.unit}",
|
||||
}
|
||||
)
|
||||
|
||||
return await render_template("offlineshop/print.html", items=items)
|
||||
|
||||
@@ -36,10 +42,14 @@ async def confirmation_code():
|
||||
payment_hash = request.args.get("p")
|
||||
payment: Payment = await get_standalone_payment(payment_hash)
|
||||
if not payment:
|
||||
return f"Couldn't find the payment {payment_hash}." + style, HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
f"Couldn't find the payment {payment_hash}." + style,
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
if payment.pending:
|
||||
return (
|
||||
f"Payment {payment_hash} wasn't received yet. Please try again in a minute." + style,
|
||||
f"Payment {payment_hash} wasn't received yet. Please try again in a minute."
|
||||
+ style,
|
||||
HTTPStatus.PAYMENT_REQUIRED,
|
||||
)
|
||||
|
||||
|
||||
@@ -43,7 +43,11 @@ async def api_shop_from_wallet():
|
||||
)
|
||||
except LnurlInvalidUrl:
|
||||
return (
|
||||
jsonify({"message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor."}),
|
||||
jsonify(
|
||||
{
|
||||
"message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor."
|
||||
}
|
||||
),
|
||||
HTTPStatus.UPGRADE_REQUIRED,
|
||||
)
|
||||
|
||||
@@ -98,7 +102,12 @@ async def api_delete_item(item_id):
|
||||
@api_validate_post_request(
|
||||
schema={
|
||||
"method": {"type": "string", "required": True, "nullable": False},
|
||||
"wordlist": {"type": "string", "empty": True, "nullable": True, "required": False},
|
||||
"wordlist": {
|
||||
"type": "string",
|
||||
"empty": True,
|
||||
"nullable": True,
|
||||
"required": False,
|
||||
},
|
||||
}
|
||||
)
|
||||
async def api_set_method():
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_paywall")
|
||||
|
||||
paywall_ext: Blueprint = Blueprint("paywall", __name__, static_folder="static", template_folder="templates")
|
||||
paywall_ext: Blueprint = Blueprint(
|
||||
"paywall", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -7,7 +7,13 @@ from .models import Paywall
|
||||
|
||||
|
||||
async def create_paywall(
|
||||
*, wallet_id: str, url: str, memo: str, description: Optional[str] = None, amount: int = 0, remembers: bool = True
|
||||
*,
|
||||
wallet_id: str,
|
||||
url: str,
|
||||
memo: str,
|
||||
description: Optional[str] = None,
|
||||
amount: int = 0,
|
||||
remembers: bool = True,
|
||||
) -> Paywall:
|
||||
paywall_id = urlsafe_short_hash()
|
||||
await db.execute(
|
||||
@@ -34,7 +40,9 @@ async def get_paywalls(wallet_ids: Union[str, List[str]]) -> List[Paywall]:
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM paywalls WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM paywalls WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [Paywall.from_row(row) for row in rows]
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@ async def m002_redux(db):
|
||||
)
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS wallet_idx ON paywalls (wallet)")
|
||||
|
||||
for row in [list(row) for row in await db.fetchall("SELECT * FROM paywalls_old")]:
|
||||
for row in [
|
||||
list(row) for row in await db.fetchall("SELECT * FROM paywalls_old")
|
||||
]:
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO paywalls (
|
||||
|
||||
@@ -16,5 +16,7 @@ async def index():
|
||||
|
||||
@paywall_ext.route("/<paywall_id>")
|
||||
async def display(paywall_id):
|
||||
paywall = await get_paywall(paywall_id) or abort(HTTPStatus.NOT_FOUND, "Paywall does not exist.")
|
||||
paywall = await get_paywall(paywall_id) or abort(
|
||||
HTTPStatus.NOT_FOUND, "Paywall does not exist."
|
||||
)
|
||||
return await render_template("paywall/display.html", paywall=paywall)
|
||||
|
||||
@@ -17,7 +17,10 @@ async def api_paywalls():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([paywall._asdict() for paywall in await get_paywalls(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([paywall._asdict() for paywall in await get_paywalls(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@paywall_ext.route("/api/v1/paywalls", methods=["POST"])
|
||||
@@ -26,7 +29,12 @@ async def api_paywalls():
|
||||
schema={
|
||||
"url": {"type": "string", "empty": False, "required": True},
|
||||
"memo": {"type": "string", "empty": False, "required": True},
|
||||
"description": {"type": "string", "empty": True, "nullable": True, "required": False},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"empty": True,
|
||||
"nullable": True,
|
||||
"required": False,
|
||||
},
|
||||
"amount": {"type": "integer", "min": 0, "required": True},
|
||||
"remembers": {"type": "boolean", "required": True},
|
||||
}
|
||||
@@ -53,26 +61,41 @@ async def api_paywall_delete(paywall_id):
|
||||
|
||||
|
||||
@paywall_ext.route("/api/v1/paywalls/<paywall_id>/invoice", methods=["POST"])
|
||||
@api_validate_post_request(schema={"amount": {"type": "integer", "min": 1, "required": True}})
|
||||
@api_validate_post_request(
|
||||
schema={"amount": {"type": "integer", "min": 1, "required": True}}
|
||||
)
|
||||
async def api_paywall_create_invoice(paywall_id):
|
||||
paywall = await get_paywall(paywall_id)
|
||||
|
||||
if g.data["amount"] < paywall.amount:
|
||||
return jsonify({"message": f"Minimum amount is {paywall.amount} sat."}), HTTPStatus.BAD_REQUEST
|
||||
return (
|
||||
jsonify({"message": f"Minimum amount is {paywall.amount} sat."}),
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
|
||||
try:
|
||||
amount = g.data["amount"] if g.data["amount"] > paywall.amount else paywall.amount
|
||||
amount = (
|
||||
g.data["amount"] if g.data["amount"] > paywall.amount else paywall.amount
|
||||
)
|
||||
payment_hash, payment_request = await create_invoice(
|
||||
wallet_id=paywall.wallet, amount=amount, memo=f"{paywall.memo}", extra={"tag": "paywall"}
|
||||
wallet_id=paywall.wallet,
|
||||
amount=amount,
|
||||
memo=f"{paywall.memo}",
|
||||
extra={"tag": "paywall"},
|
||||
)
|
||||
except Exception as e:
|
||||
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
|
||||
return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.CREATED
|
||||
return (
|
||||
jsonify({"payment_hash": payment_hash, "payment_request": payment_request}),
|
||||
HTTPStatus.CREATED,
|
||||
)
|
||||
|
||||
|
||||
@paywall_ext.route("/api/v1/paywalls/<paywall_id>/check_invoice", methods=["POST"])
|
||||
@api_validate_post_request(schema={"payment_hash": {"type": "string", "empty": False, "required": True}})
|
||||
@api_validate_post_request(
|
||||
schema={"payment_hash": {"type": "string", "empty": False, "required": True}}
|
||||
)
|
||||
async def api_paywal_check_invoice(paywall_id):
|
||||
paywall = await get_paywall(paywall_id)
|
||||
|
||||
@@ -90,6 +113,9 @@ async def api_paywal_check_invoice(paywall_id):
|
||||
payment = await wallet.get_payment(g.data["payment_hash"])
|
||||
await payment.set_pending(False)
|
||||
|
||||
return jsonify({"paid": True, "url": paywall.url, "remembers": paywall.remembers}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"paid": True, "url": paywall.url, "remembers": paywall.remembers}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
return jsonify({"paid": False}), HTTPStatus.OK
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_subdomains")
|
||||
|
||||
subdomains_ext: Blueprint = Blueprint("subdomains", __name__, static_folder="static", template_folder="templates")
|
||||
subdomains_ext: Blueprint = Blueprint(
|
||||
"subdomains", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -2,11 +2,20 @@ from lnbits.extensions.subdomains.models import Domains
|
||||
import httpx, json
|
||||
|
||||
|
||||
async def cloudflare_create_subdomain(domain: Domains, subdomain: str, record_type: str, ip: str):
|
||||
async def cloudflare_create_subdomain(
|
||||
domain: Domains, subdomain: str, record_type: str, ip: str
|
||||
):
|
||||
# Call to cloudflare sort of a dry-run - if success delete the domain and wait for payment
|
||||
### SEND REQUEST TO CLOUDFLARE
|
||||
url = "https://api.cloudflare.com/client/v4/zones/" + domain.cf_zone_id + "/dns_records"
|
||||
header = {"Authorization": "Bearer " + domain.cf_token, "Content-Type": "application/json"}
|
||||
url = (
|
||||
"https://api.cloudflare.com/client/v4/zones/"
|
||||
+ domain.cf_zone_id
|
||||
+ "/dns_records"
|
||||
)
|
||||
header = {
|
||||
"Authorization": "Bearer " + domain.cf_token,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
aRecord = subdomain + "." + domain.domain
|
||||
cf_response = ""
|
||||
async with httpx.AsyncClient() as client:
|
||||
@@ -30,8 +39,15 @@ async def cloudflare_create_subdomain(domain: Domains, subdomain: str, record_ty
|
||||
|
||||
|
||||
async def cloudflare_deletesubdomain(domain: Domains, domain_id: str):
|
||||
url = "https://api.cloudflare.com/client/v4/zones/" + domain.cf_zone_id + "/dns_records"
|
||||
header = {"Authorization": "Bearer " + domain.cf_token, "Content-Type": "application/json"}
|
||||
url = (
|
||||
"https://api.cloudflare.com/client/v4/zones/"
|
||||
+ domain.cf_zone_id
|
||||
+ "/dns_records"
|
||||
)
|
||||
header = {
|
||||
"Authorization": "Bearer " + domain.cf_token,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
r = await client.delete(
|
||||
|
||||
@@ -23,7 +23,18 @@ async def create_subdomain(
|
||||
INSERT INTO subdomain (id, domain, email, subdomain, ip, wallet, sats, duration, paid, record_type)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(payment_hash, domain, email, subdomain, ip, wallet, sats, duration, False, record_type),
|
||||
(
|
||||
payment_hash,
|
||||
domain,
|
||||
email,
|
||||
subdomain,
|
||||
ip,
|
||||
wallet,
|
||||
sats,
|
||||
duration,
|
||||
False,
|
||||
record_type,
|
||||
),
|
||||
)
|
||||
|
||||
subdomain = await get_subdomain(payment_hash)
|
||||
@@ -118,7 +129,18 @@ async def create_domain(
|
||||
INSERT INTO domain (id, wallet, domain, webhook, cf_token, cf_zone_id, description, cost, amountmade, allowed_record_types)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(domain_id, wallet, domain, webhook, cf_token, cf_zone_id, description, cost, 0, allowed_record_types),
|
||||
(
|
||||
domain_id,
|
||||
wallet,
|
||||
domain,
|
||||
webhook,
|
||||
cf_token,
|
||||
cf_zone_id,
|
||||
description,
|
||||
cost,
|
||||
0,
|
||||
allowed_record_types,
|
||||
),
|
||||
)
|
||||
|
||||
domain = await get_domain(domain_id)
|
||||
@@ -128,7 +150,9 @@ async def create_domain(
|
||||
|
||||
async def update_domain(domain_id: str, **kwargs) -> Domains:
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
await db.execute(f"UPDATE domain SET {q} WHERE id = ?", (*kwargs.values(), domain_id))
|
||||
await db.execute(
|
||||
f"UPDATE domain SET {q} WHERE id = ?", (*kwargs.values(), domain_id)
|
||||
)
|
||||
row = await db.fetchone("SELECT * FROM domain WHERE id = ?", (domain_id,))
|
||||
assert row, "Newly updated domain couldn't be retrieved"
|
||||
return Domains(**row)
|
||||
@@ -144,7 +168,9 @@ async def get_domains(wallet_ids: Union[str, List[str]]) -> List[Domains]:
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM domain WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM domain WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [Domains(**row) for row in rows]
|
||||
|
||||
|
||||
@@ -33,7 +33,10 @@ async def on_invoice_paid(payment: Payment) -> None:
|
||||
|
||||
### Create subdomain
|
||||
cf_response = cloudflare_create_subdomain(
|
||||
domain=domain, subdomain=subdomain.subdomain, record_type=subdomain.record_type, ip=subdomain.ip
|
||||
domain=domain,
|
||||
subdomain=subdomain.subdomain,
|
||||
record_type=subdomain.record_type,
|
||||
ip=subdomain.ip,
|
||||
)
|
||||
|
||||
### Use webhook to notify about cloudflare registration
|
||||
|
||||
@@ -19,7 +19,9 @@ async def display(domain_id):
|
||||
domain = await get_domain(domain_id)
|
||||
if not domain:
|
||||
abort(HTTPStatus.NOT_FOUND, "Domain does not exist.")
|
||||
allowed_records = domain.allowed_record_types.replace('"', "").replace(" ", "").split(",")
|
||||
allowed_records = (
|
||||
domain.allowed_record_types.replace('"', "").replace(" ", "").split(",")
|
||||
)
|
||||
print(allowed_records)
|
||||
return await render_template(
|
||||
"subdomains/display.html",
|
||||
|
||||
@@ -37,7 +37,10 @@ async def api_domains():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([domain._asdict() for domain in await get_domains(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([domain._asdict() for domain in await get_domains(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@subdomains_ext.route("/api/v1/domains", methods=["POST"])
|
||||
@@ -98,7 +101,10 @@ async def api_subdomains():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([domain._asdict() for domain in await get_subdomains(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([domain._asdict() for domain in await get_subdomains(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@subdomains_ext.route("/api/v1/subdomains/<domain_id>", methods=["POST"])
|
||||
@@ -122,24 +128,42 @@ async def api_subdomain_make_subdomain(domain_id):
|
||||
|
||||
## If record_type is not one of the allowed ones reject the request
|
||||
if g.data["record_type"] not in domain.allowed_record_types:
|
||||
return jsonify({"message": g.data["record_type"] + "Not a valid record"}), HTTPStatus.BAD_REQUEST
|
||||
return (
|
||||
jsonify({"message": g.data["record_type"] + "Not a valid record"}),
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
|
||||
## If domain already exist in our database reject it
|
||||
if await get_subdomainBySubdomain(g.data["subdomain"]) is not None:
|
||||
return (
|
||||
jsonify({"message": g.data["subdomain"] + "." + domain.domain + " domain already taken"}),
|
||||
jsonify(
|
||||
{
|
||||
"message": g.data["subdomain"]
|
||||
+ "."
|
||||
+ domain.domain
|
||||
+ " domain already taken"
|
||||
}
|
||||
),
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
|
||||
## Dry run cloudflare... (create and if create is sucessful delete it)
|
||||
cf_response = await cloudflare_create_subdomain(
|
||||
domain=domain, subdomain=g.data["subdomain"], record_type=g.data["record_type"], ip=g.data["ip"]
|
||||
domain=domain,
|
||||
subdomain=g.data["subdomain"],
|
||||
record_type=g.data["record_type"],
|
||||
ip=g.data["ip"],
|
||||
)
|
||||
if cf_response["success"] == True:
|
||||
cloudflare_deletesubdomain(domain=domain, domain_id=cf_response["result"]["id"])
|
||||
else:
|
||||
return (
|
||||
jsonify({"message": "Problem with cloudflare: " + cf_response["errors"][0]["message"]}),
|
||||
jsonify(
|
||||
{
|
||||
"message": "Problem with cloudflare: "
|
||||
+ cf_response["errors"][0]["message"]
|
||||
}
|
||||
),
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
|
||||
@@ -152,12 +176,20 @@ async def api_subdomain_make_subdomain(domain_id):
|
||||
extra={"tag": "lnsubdomain"},
|
||||
)
|
||||
|
||||
subdomain = await create_subdomain(payment_hash=payment_hash, wallet=domain.wallet, **g.data)
|
||||
subdomain = await create_subdomain(
|
||||
payment_hash=payment_hash, wallet=domain.wallet, **g.data
|
||||
)
|
||||
|
||||
if not subdomain:
|
||||
return jsonify({"message": "LNsubdomain could not be fetched."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "LNsubdomain could not be fetched."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
|
||||
return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"payment_hash": payment_hash, "payment_request": payment_request}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@subdomains_ext.route("/api/v1/subdomains/<payment_hash>", methods=["GET"])
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_tpos")
|
||||
|
||||
tpos_ext: Blueprint = Blueprint("tpos", __name__, static_folder="static", template_folder="templates")
|
||||
tpos_ext: Blueprint = Blueprint(
|
||||
"tpos", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -31,7 +31,9 @@ async def get_tposs(wallet_ids: Union[str, List[str]]) -> List[TPoS]:
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM tposs WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM tposs WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [TPoS.from_row(row) for row in rows]
|
||||
|
||||
|
||||
@@ -16,7 +16,10 @@ async def api_tposs():
|
||||
if "all_wallets" in request.args:
|
||||
wallet_ids = (await get_user(g.wallet.user)).wallet_ids
|
||||
|
||||
return jsonify([tpos._asdict() for tpos in await get_tposs(wallet_ids)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([tpos._asdict() for tpos in await get_tposs(wallet_ids)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@tpos_ext.route("/api/v1/tposs", methods=["POST"])
|
||||
@@ -49,7 +52,9 @@ async def api_tpos_delete(tpos_id):
|
||||
|
||||
|
||||
@tpos_ext.route("/api/v1/tposs/<tpos_id>/invoices/", methods=["POST"])
|
||||
@api_validate_post_request(schema={"amount": {"type": "integer", "min": 1, "required": True}})
|
||||
@api_validate_post_request(
|
||||
schema={"amount": {"type": "integer", "min": 1, "required": True}}
|
||||
)
|
||||
async def api_tpos_create_invoice(tpos_id):
|
||||
tpos = await get_tpos(tpos_id)
|
||||
|
||||
@@ -58,12 +63,18 @@ async def api_tpos_create_invoice(tpos_id):
|
||||
|
||||
try:
|
||||
payment_hash, payment_request = await create_invoice(
|
||||
wallet_id=tpos.wallet, amount=g.data["amount"], memo=f"{tpos.name}", extra={"tag": "tpos"}
|
||||
wallet_id=tpos.wallet,
|
||||
amount=g.data["amount"],
|
||||
memo=f"{tpos.name}",
|
||||
extra={"tag": "tpos"},
|
||||
)
|
||||
except Exception as e:
|
||||
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
|
||||
return jsonify({"payment_hash": payment_hash, "payment_request": payment_request}), HTTPStatus.CREATED
|
||||
return (
|
||||
jsonify({"payment_hash": payment_hash, "payment_request": payment_request}),
|
||||
HTTPStatus.CREATED,
|
||||
)
|
||||
|
||||
|
||||
@tpos_ext.route("/api/v1/tposs/<tpos_id>/invoices/<payment_hash>", methods=["GET"])
|
||||
|
||||
@@ -3,7 +3,9 @@ from lnbits.db import Database
|
||||
|
||||
db = Database("ext_usermanager")
|
||||
|
||||
usermanager_ext: Blueprint = Blueprint("usermanager", __name__, static_folder="static", template_folder="templates")
|
||||
usermanager_ext: Blueprint = Blueprint(
|
||||
"usermanager", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -16,7 +16,9 @@ from .models import Users, Wallets
|
||||
### Users
|
||||
|
||||
|
||||
async def create_usermanager_user(user_name: str, wallet_name: str, admin_id: str) -> Users:
|
||||
async def create_usermanager_user(
|
||||
user_name: str, wallet_name: str, admin_id: str
|
||||
) -> Users:
|
||||
account = await create_account()
|
||||
user = await get_user(account.id)
|
||||
assert user, "Newly created user couldn't be retrieved"
|
||||
@@ -66,7 +68,9 @@ async def delete_usermanager_user(user_id: str) -> None:
|
||||
### Wallets
|
||||
|
||||
|
||||
async def create_usermanager_wallet(user_id: str, wallet_name: str, admin_id: str) -> Wallets:
|
||||
async def create_usermanager_wallet(
|
||||
user_id: str, wallet_name: str, admin_id: str
|
||||
) -> Wallets:
|
||||
wallet = await create_wallet(user_id=user_id, wallet_name=wallet_name)
|
||||
await db.execute(
|
||||
"""
|
||||
@@ -91,7 +95,9 @@ async def get_usermanager_wallets(user_id: str) -> List[Wallets]:
|
||||
|
||||
|
||||
async def get_usermanager_wallet_transactions(wallet_id: str) -> List[Payment]:
|
||||
return await get_payments(wallet_id=wallet_id, complete=True, pending=False, outgoing=True, incoming=True)
|
||||
return await get_payments(
|
||||
wallet_id=wallet_id, complete=True, pending=False, outgoing=True, incoming=True
|
||||
)
|
||||
|
||||
|
||||
async def delete_usermanager_wallet(wallet_id: str, user_id: str) -> None:
|
||||
|
||||
@@ -26,7 +26,10 @@ from lnbits.core import update_user_extension
|
||||
@api_check_wallet_key(key_type="invoice")
|
||||
async def api_usermanager_users():
|
||||
user_id = g.wallet.user
|
||||
return jsonify([user._asdict() for user in await get_usermanager_users(user_id)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify([user._asdict() for user in await get_usermanager_users(user_id)]),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@usermanager_ext.route("/api/v1/users", methods=["POST"])
|
||||
@@ -39,7 +42,9 @@ async def api_usermanager_users():
|
||||
}
|
||||
)
|
||||
async def api_usermanager_users_create():
|
||||
user = await create_usermanager_user(g.data["user_name"], g.data["wallet_name"], g.data["admin_id"])
|
||||
user = await create_usermanager_user(
|
||||
g.data["user_name"], g.data["wallet_name"], g.data["admin_id"]
|
||||
)
|
||||
return jsonify(user._asdict()), HTTPStatus.CREATED
|
||||
|
||||
|
||||
@@ -69,7 +74,9 @@ async def api_usermanager_activate_extension():
|
||||
user = await get_user(g.data["userid"])
|
||||
if not user:
|
||||
return jsonify({"message": "no such user"}), HTTPStatus.NOT_FOUND
|
||||
update_user_extension(user_id=g.data["userid"], extension=g.data["extension"], active=g.data["active"])
|
||||
update_user_extension(
|
||||
user_id=g.data["userid"], extension=g.data["extension"], active=g.data["active"]
|
||||
)
|
||||
return jsonify({"extension": "updated"}), HTTPStatus.CREATED
|
||||
|
||||
|
||||
@@ -80,7 +87,12 @@ async def api_usermanager_activate_extension():
|
||||
@api_check_wallet_key(key_type="invoice")
|
||||
async def api_usermanager_wallets():
|
||||
user_id = g.wallet.user
|
||||
return jsonify([wallet._asdict() for wallet in await get_usermanager_wallets(user_id)]), HTTPStatus.OK
|
||||
return (
|
||||
jsonify(
|
||||
[wallet._asdict() for wallet in await get_usermanager_wallets(user_id)]
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
|
||||
@usermanager_ext.route("/api/v1/wallets", methods=["POST"])
|
||||
@@ -93,7 +105,9 @@ async def api_usermanager_wallets():
|
||||
}
|
||||
)
|
||||
async def api_usermanager_wallets_create():
|
||||
user = await create_usermanager_wallet(g.data["user_id"], g.data["wallet_name"], g.data["admin_id"])
|
||||
user = await create_usermanager_wallet(
|
||||
g.data["user_id"], g.data["wallet_name"], g.data["admin_id"]
|
||||
)
|
||||
return jsonify(user._asdict()), HTTPStatus.CREATED
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ from lnbits.db import Database
|
||||
db = Database("ext_withdraw")
|
||||
|
||||
|
||||
withdraw_ext: Blueprint = Blueprint("withdraw", __name__, static_folder="static", template_folder="templates")
|
||||
withdraw_ext: Blueprint = Blueprint(
|
||||
"withdraw", __name__, static_folder="static", template_folder="templates"
|
||||
)
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
|
||||
@@ -66,7 +66,9 @@ async def get_withdraw_link(link_id: str, num=0) -> Optional[WithdrawLink]:
|
||||
|
||||
|
||||
async def get_withdraw_link_by_hash(unique_hash: str, num=0) -> Optional[WithdrawLink]:
|
||||
row = await db.fetchone("SELECT * FROM withdraw_link WHERE unique_hash = ?", (unique_hash,))
|
||||
row = await db.fetchone(
|
||||
"SELECT * FROM withdraw_link WHERE unique_hash = ?", (unique_hash,)
|
||||
)
|
||||
link = []
|
||||
for item in row:
|
||||
link.append(item)
|
||||
@@ -79,14 +81,18 @@ async def get_withdraw_links(wallet_ids: Union[str, List[str]]) -> List[Withdraw
|
||||
wallet_ids = [wallet_ids]
|
||||
|
||||
q = ",".join(["?"] * len(wallet_ids))
|
||||
rows = await db.fetchall(f"SELECT * FROM withdraw_link WHERE wallet IN ({q})", (*wallet_ids,))
|
||||
rows = await db.fetchall(
|
||||
f"SELECT * FROM withdraw_link WHERE wallet IN ({q})", (*wallet_ids,)
|
||||
)
|
||||
|
||||
return [WithdrawLink.from_row(row) for row in rows]
|
||||
|
||||
|
||||
async def update_withdraw_link(link_id: str, **kwargs) -> Optional[WithdrawLink]:
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
await db.execute(f"UPDATE withdraw_link SET {q} WHERE id = ?", (*kwargs.values(), link_id))
|
||||
await db.execute(
|
||||
f"UPDATE withdraw_link SET {q} WHERE id = ?", (*kwargs.values(), link_id)
|
||||
)
|
||||
row = await db.fetchone("SELECT * FROM withdraw_link WHERE id = ?", (link_id,))
|
||||
return WithdrawLink.from_row(row) if row else None
|
||||
|
||||
@@ -123,7 +129,9 @@ async def create_hash_check(
|
||||
|
||||
async def get_hash_check(the_hash: str, lnurl_id: str) -> Optional[HashCheck]:
|
||||
rowid = await db.fetchone("SELECT * FROM hash_check WHERE id = ?", (the_hash,))
|
||||
rowlnurl = await db.fetchone("SELECT * FROM hash_check WHERE lnurl_id = ?", (lnurl_id,))
|
||||
rowlnurl = await db.fetchone(
|
||||
"SELECT * FROM hash_check WHERE lnurl_id = ?", (lnurl_id,)
|
||||
)
|
||||
if not rowlnurl:
|
||||
await create_hash_check(the_hash, lnurl_id)
|
||||
return {"lnurl": True, "hash": False}
|
||||
@@ -132,4 +140,4 @@ async def get_hash_check(the_hash: str, lnurl_id: str) -> Optional[HashCheck]:
|
||||
await create_hash_check(the_hash, lnurl_id)
|
||||
return {"lnurl": True, "hash": False}
|
||||
else:
|
||||
return {"lnurl": True, "hash": True}
|
||||
return {"lnurl": True, "hash": True}
|
||||
|
||||
@@ -17,10 +17,16 @@ async def api_lnurl_response(unique_hash):
|
||||
link = await get_withdraw_link_by_hash(unique_hash)
|
||||
|
||||
if not link:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
if link.is_spent:
|
||||
return jsonify({"status": "ERROR", "reason": "Withdraw is spent."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "Withdraw is spent."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
return jsonify(link.lnurl_response.dict()), HTTPStatus.OK
|
||||
|
||||
@@ -33,10 +39,16 @@ async def api_lnurl_multi_response(unique_hash, id_unique_hash):
|
||||
link = await get_withdraw_link_by_hash(unique_hash)
|
||||
|
||||
if not link:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
if link.is_spent:
|
||||
return jsonify({"status": "ERROR", "reason": "Withdraw is spent."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "Withdraw is spent."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
useslist = link.usescsv.split(",")
|
||||
found = False
|
||||
@@ -45,7 +57,10 @@ async def api_lnurl_multi_response(unique_hash, id_unique_hash):
|
||||
if id_unique_hash == shortuuid.uuid(name=tohash):
|
||||
found = True
|
||||
if not found:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
return jsonify(link.lnurl_response.dict()), HTTPStatus.OK
|
||||
|
||||
@@ -61,16 +76,27 @@ async def api_lnurl_callback(unique_hash):
|
||||
now = int(datetime.now().timestamp())
|
||||
|
||||
if not link:
|
||||
return jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "LNURL-withdraw not found."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
if link.is_spent:
|
||||
return jsonify({"status": "ERROR", "reason": "Withdraw is spent."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "Withdraw is spent."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
if link.k1 != k1:
|
||||
return jsonify({"status": "ERROR", "reason": "Bad request."}), HTTPStatus.OK
|
||||
|
||||
if now < link.open_time:
|
||||
return jsonify({"status": "ERROR", "reason": f"Wait {link.open_time - now} seconds."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify(
|
||||
{"status": "ERROR", "reason": f"Wait {link.open_time - now} seconds."}
|
||||
),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
try:
|
||||
await pay_invoice(
|
||||
@@ -85,12 +111,19 @@ async def api_lnurl_callback(unique_hash):
|
||||
usecv = link.usescsv.split(",")
|
||||
usescsv += "," + str(usecv[x])
|
||||
usescsv = usescsv[1:]
|
||||
changes = {"open_time": link.wait_time + now, "used": link.used + 1, "usescsv": usescsv}
|
||||
changes = {
|
||||
"open_time": link.wait_time + now,
|
||||
"used": link.used + 1,
|
||||
"usescsv": usescsv,
|
||||
}
|
||||
|
||||
await update_withdraw_link(link.id, **changes)
|
||||
except ValueError as e:
|
||||
return jsonify({"status": "ERROR", "reason": str(e)}), HTTPStatus.OK
|
||||
except PermissionError:
|
||||
return jsonify({"status": "ERROR", "reason": "Withdraw link is empty."}), HTTPStatus.OK
|
||||
return (
|
||||
jsonify({"status": "ERROR", "reason": "Withdraw link is empty."}),
|
||||
HTTPStatus.OK,
|
||||
)
|
||||
|
||||
return jsonify({"status": "OK"}), HTTPStatus.OK
|
||||
|
||||
@@ -47,7 +47,9 @@ async def m002_change_withdraw_table(db):
|
||||
"""
|
||||
)
|
||||
await db.execute("CREATE INDEX IF NOT EXISTS wallet_idx ON withdraw_link (wallet)")
|
||||
await db.execute("CREATE UNIQUE INDEX IF NOT EXISTS unique_hash_idx ON withdraw_link (unique_hash)")
|
||||
await db.execute(
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS unique_hash_idx ON withdraw_link (unique_hash)"
|
||||
)
|
||||
|
||||
for row in [list(row) for row in await db.fetchall("SELECT * FROM withdraw_links")]:
|
||||
usescsv = ""
|
||||
@@ -107,4 +109,4 @@ async def m003_make_hash_check(db):
|
||||
lnurl_id TEXT
|
||||
);
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -45,13 +45,19 @@ class WithdrawLink(NamedTuple):
|
||||
_external=True,
|
||||
)
|
||||
else:
|
||||
url = url_for("withdraw.api_lnurl_response", unique_hash=self.unique_hash, _external=True)
|
||||
url = url_for(
|
||||
"withdraw.api_lnurl_response",
|
||||
unique_hash=self.unique_hash,
|
||||
_external=True,
|
||||
)
|
||||
|
||||
return lnurl_encode(url)
|
||||
|
||||
@property
|
||||
def lnurl_response(self) -> LnurlWithdrawResponse:
|
||||
url = url_for("withdraw.api_lnurl_callback", unique_hash=self.unique_hash, _external=True)
|
||||
url = url_for(
|
||||
"withdraw.api_lnurl_callback", unique_hash=self.unique_hash, _external=True
|
||||
)
|
||||
return LnurlWithdrawResponse(
|
||||
callback=url,
|
||||
k1=self.k1,
|
||||
@@ -67,4 +73,4 @@ class HashCheck(NamedTuple):
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Row) -> "Hash":
|
||||
return cls(**dict(row))
|
||||
return cls(**dict(row))
|
||||
|
||||
@@ -17,13 +17,17 @@ async def index():
|
||||
|
||||
@withdraw_ext.route("/<link_id>")
|
||||
async def display(link_id):
|
||||
link = await get_withdraw_link(link_id, 0) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
|
||||
link = await get_withdraw_link(link_id, 0) or abort(
|
||||
HTTPStatus.NOT_FOUND, "Withdraw link does not exist."
|
||||
)
|
||||
return await render_template("withdraw/display.html", link=link, unique=True)
|
||||
|
||||
|
||||
@withdraw_ext.route("/img/<link_id>")
|
||||
async def img(link_id):
|
||||
link = await get_withdraw_link(link_id, 0) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
|
||||
link = await get_withdraw_link(link_id, 0) or abort(
|
||||
HTTPStatus.NOT_FOUND, "Withdraw link does not exist."
|
||||
)
|
||||
qr = pyqrcode.create(link.lnurl)
|
||||
stream = BytesIO()
|
||||
qr.svg(stream, scale=3)
|
||||
@@ -41,13 +45,17 @@ async def img(link_id):
|
||||
|
||||
@withdraw_ext.route("/print/<link_id>")
|
||||
async def print_qr(link_id):
|
||||
link = await get_withdraw_link(link_id) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
|
||||
link = await get_withdraw_link(link_id) or abort(
|
||||
HTTPStatus.NOT_FOUND, "Withdraw link does not exist."
|
||||
)
|
||||
if link.uses == 0:
|
||||
return await render_template("withdraw/print_qr.html", link=link, unique=False)
|
||||
links = []
|
||||
count = 0
|
||||
for x in link.usescsv.split(","):
|
||||
linkk = await get_withdraw_link(link_id, count) or abort(HTTPStatus.NOT_FOUND, "Withdraw link does not exist.")
|
||||
linkk = await get_withdraw_link(link_id, count) or abort(
|
||||
HTTPStatus.NOT_FOUND, "Withdraw link does not exist."
|
||||
)
|
||||
links.append(str(linkk.lnurl))
|
||||
count = count + 1
|
||||
page_link = list(chunks(links, 2))
|
||||
|
||||
@@ -39,7 +39,11 @@ async def api_links():
|
||||
)
|
||||
except LnurlInvalidUrl:
|
||||
return (
|
||||
jsonify({"message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor."}),
|
||||
jsonify(
|
||||
{
|
||||
"message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor."
|
||||
}
|
||||
),
|
||||
HTTPStatus.UPGRADE_REQUIRED,
|
||||
)
|
||||
|
||||
@@ -50,7 +54,10 @@ async def api_link_retrieve(link_id):
|
||||
link = await get_withdraw_link(link_id, 0)
|
||||
|
||||
if not link:
|
||||
return jsonify({"message": "Withdraw link does not exist."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "Withdraw link does not exist."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
|
||||
if link.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Not your withdraw link."}), HTTPStatus.FORBIDDEN
|
||||
@@ -74,7 +81,11 @@ async def api_link_retrieve(link_id):
|
||||
async def api_link_create_or_update(link_id=None):
|
||||
if g.data["max_withdrawable"] < g.data["min_withdrawable"]:
|
||||
return (
|
||||
jsonify({"message": "`max_withdrawable` needs to be at least `min_withdrawable`."}),
|
||||
jsonify(
|
||||
{
|
||||
"message": "`max_withdrawable` needs to be at least `min_withdrawable`."
|
||||
}
|
||||
),
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
|
||||
@@ -89,14 +100,22 @@ async def api_link_create_or_update(link_id=None):
|
||||
if link_id:
|
||||
link = await get_withdraw_link(link_id, 0)
|
||||
if not link:
|
||||
return jsonify({"message": "Withdraw link does not exist."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "Withdraw link does not exist."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
if link.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Not your withdraw link."}), HTTPStatus.FORBIDDEN
|
||||
link = await update_withdraw_link(link_id, **g.data, usescsv=usescsv, used=0)
|
||||
else:
|
||||
link = await create_withdraw_link(wallet_id=g.wallet.id, **g.data, usescsv=usescsv)
|
||||
link = await create_withdraw_link(
|
||||
wallet_id=g.wallet.id, **g.data, usescsv=usescsv
|
||||
)
|
||||
|
||||
return jsonify({**link._asdict(), **{"lnurl": link.lnurl}}), HTTPStatus.OK if link_id else HTTPStatus.CREATED
|
||||
return (
|
||||
jsonify({**link._asdict(), **{"lnurl": link.lnurl}}),
|
||||
HTTPStatus.OK if link_id else HTTPStatus.CREATED,
|
||||
)
|
||||
|
||||
|
||||
@withdraw_ext.route("/api/v1/links/<link_id>", methods=["DELETE"])
|
||||
@@ -105,7 +124,10 @@ async def api_link_delete(link_id):
|
||||
link = await get_withdraw_link(link_id)
|
||||
|
||||
if not link:
|
||||
return jsonify({"message": "Withdraw link does not exist."}), HTTPStatus.NOT_FOUND
|
||||
return (
|
||||
jsonify({"message": "Withdraw link does not exist."}),
|
||||
HTTPStatus.NOT_FOUND,
|
||||
)
|
||||
|
||||
if link.wallet != g.wallet.id:
|
||||
return jsonify({"message": "Not your withdraw link."}), HTTPStatus.FORBIDDEN
|
||||
@@ -119,4 +141,4 @@ async def api_link_delete(link_id):
|
||||
@api_check_wallet_key("invoice")
|
||||
async def api_hash_retrieve(the_hash, lnurl_id):
|
||||
hashCheck = await get_hash_check(the_hash, lnurl_id)
|
||||
return jsonify(hashCheck), HTTPStatus.OK
|
||||
return jsonify(hashCheck), HTTPStatus.OK
|
||||
|
||||
Reference in New Issue
Block a user