I want them to turn black

This commit is contained in:
benarc 2021-10-17 18:33:29 +01:00
parent 70facdaa93
commit 1d3bb016a2
84 changed files with 899 additions and 1008 deletions

View File

@ -4,8 +4,15 @@ import uvloop
from starlette.requests import Request
from .commands import bundle_vendored, migrate_databases, transpile_scss
from .settings import (DEBUG, LNBITS_COMMIT, LNBITS_DATA_FOLDER,
LNBITS_SITE_TITLE, PORT, SERVICE_FEE, WALLET)
from .settings import (
DEBUG,
LNBITS_COMMIT,
LNBITS_DATA_FOLDER,
LNBITS_SITE_TITLE,
PORT,
SERVICE_FEE,
WALLET,
)
uvloop.install()

View File

@ -16,12 +16,23 @@ import lnbits.settings
from .commands import db_migrate, handle_assets
from .core import core_app
from .core.views.generic import core_html_routes
from .helpers import (get_css_vendored, get_js_vendored, get_valid_extensions,
template_renderer, url_for_vendored)
from .helpers import (
get_css_vendored,
get_js_vendored,
get_valid_extensions,
template_renderer,
url_for_vendored,
)
from .requestvars import g
from .settings import WALLET
from .tasks import (catch_everything_and_restart, check_pending_payments, internal_invoice_listener,
invoice_listener, run_deferred_async, webhook_handler)
from .tasks import (
catch_everything_and_restart,
check_pending_payments,
internal_invoice_listener,
invoice_listener,
run_deferred_async,
webhook_handler,
)
def create_app(config_object="lnbits.settings") -> FastAPI:
@ -30,12 +41,11 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"""
app = FastAPI()
app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
app.mount("/core/static", StaticFiles(directory="lnbits/core/static"), name="core_static")
app.mount(
"/core/static", StaticFiles(directory="lnbits/core/static"), name="core_static"
)
origins = [
"http://localhost",
"http://localhost:5000",
]
origins = ["http://localhost", "http://localhost:5000"]
app.add_middleware(
CORSMiddleware,
@ -44,14 +54,19 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
allow_methods=["*"],
allow_headers=["*"],
)
g().config = lnbits.settings
g().base_url = f"http://{lnbits.settings.HOST}:{lnbits.settings.PORT}"
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return template_renderer().TemplateResponse("error.html", {"request": request, "err": f"`{exc.errors()}` is not a valid UUID."})
async def validation_exception_handler(
request: Request, exc: RequestValidationError
):
return template_renderer().TemplateResponse(
"error.html",
{"request": request, "err": f"`{exc.errors()}` is not a valid UUID."},
)
# return HTMLResponse(
# status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
# content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
@ -69,6 +84,7 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
return app
def check_funding_source(app: FastAPI) -> None:
@app.on_event("startup")
async def check_wallet_status():
@ -95,7 +111,7 @@ def register_routes(app: FastAPI) -> None:
try:
ext_module = importlib.import_module(f"lnbits.extensions.{ext.code}")
ext_route = getattr(ext_module, f"{ext.code}_ext")
if hasattr(ext_module, f"{ext.code}_start"):
ext_start_func = getattr(ext_module, f"{ext.code}_start")
ext_start_func()
@ -150,6 +166,7 @@ def register_async_tasks(app):
async def stop_listeners():
pass
def register_exception_handlers(app: FastAPI):
@app.exception_handler(Exception)
async def basic_error(request: Request, err):
@ -157,5 +174,6 @@ def register_exception_handlers(app: FastAPI):
etype, _, tb = sys.exc_info()
traceback.print_exception(etype, err, tb)
exc = traceback.format_exc()
return template_renderer().TemplateResponse("error.html", {"request": request, "err": err})
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": err}
)

View File

@ -8,7 +8,6 @@ from fastapi.security.api_key import APIKeyQuery, APIKeyCookie, APIKeyHeader, AP
from fastapi.security.base import SecurityBase
API_KEY = "usr"
API_KEY_NAME = "X-API-key"
@ -16,12 +15,11 @@ api_key_query = APIKeyQuery(name=API_KEY_NAME, auto_error=False)
api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False)
class AuthBearer(SecurityBase):
def __init__(self, scheme_name: str = None, auto_error: bool = True):
self.scheme_name = scheme_name or self.__class__.__name__
self.auto_error = auto_error
async def __call__(self, request: Request):
key = await self.get_api_key()
print(key)
@ -37,7 +35,9 @@ class AuthBearer(SecurityBase):
# else:
# raise HTTPException(
# status_code=403, detail="Invalid authorization code.")
async def get_api_key(self,
async def get_api_key(
self,
api_key_query: str = Security(api_key_query),
api_key_header: str = Security(api_key_header),
):
@ -46,4 +46,6 @@ class AuthBearer(SecurityBase):
elif api_key_header == API_KEY:
return api_key_header
else:
raise HTTPException(status_code=403, detail="Could not validate credentials")
raise HTTPException(
status_code=403, detail="Could not validate credentials"
)

View File

@ -125,12 +125,7 @@ def _unshorten_amount(amount: str) -> int:
# * `u` (micro): multiply by 0.000001
# * `n` (nano): multiply by 0.000000001
# * `p` (pico): multiply by 0.000000000001
units = {
"p": 10 ** 12,
"n": 10 ** 9,
"u": 10 ** 6,
"m": 10 ** 3,
}
units = {"p": 10 ** 12, "n": 10 ** 9, "u": 10 ** 6, "m": 10 ** 3}
unit = str(amount)[-1]
# BOLT #11:
@ -161,9 +156,9 @@ def _trim_to_bytes(barr):
def _readable_scid(short_channel_id: int) -> str:
return "{blockheight}x{transactionindex}x{outputindex}".format(
blockheight=((short_channel_id >> 40) & 0xffffff),
transactionindex=((short_channel_id >> 16) & 0xffffff),
outputindex=(short_channel_id & 0xffff),
blockheight=((short_channel_id >> 40) & 0xFFFFFF),
transactionindex=((short_channel_id >> 16) & 0xFFFFFF),
outputindex=(short_channel_id & 0xFFFF),
)

View File

@ -9,5 +9,3 @@ core_app: APIRouter = APIRouter()
from .views.api import * # noqa
from .views.generic import * # noqa
from .views.public_api import * # noqa

View File

@ -58,10 +58,11 @@ async def get_user(user_id: str, conn: Optional[Connection] = None) -> Optional[
return None
return User(
id = user['id'],
email = user['email'],
extensions = [e[0] for e in extensions],
wallets = [Wallet(**w) for w in wallets])
id=user["id"],
email=user["email"],
extensions=[e[0] for e in extensions],
wallets=[Wallet(**w) for w in wallets],
)
async def update_user_extension(
@ -106,6 +107,7 @@ async def create_wallet(
return new_wallet
async def update_wallet(
wallet_id: str, new_name: str, conn: Optional[Connection] = None
) -> Optional[Wallet]:
@ -115,7 +117,7 @@ async def update_wallet(
name = ?
WHERE id = ?
""",
(new_name, wallet_id)
(new_name, wallet_id),
)
@ -276,9 +278,7 @@ async def get_payments(
return [Payment.from_row(row) for row in rows]
async def delete_expired_invoices(
conn: Optional[Connection] = None,
) -> None:
async def delete_expired_invoices(conn: Optional[Connection] = None,) -> None:
# first we delete all invoices older than one month
await (conn or db).execute(
f"""
@ -367,31 +367,22 @@ async def create_payment(
async def update_payment_status(
checking_id: str,
pending: bool,
conn: Optional[Connection] = None,
checking_id: str, pending: bool, conn: Optional[Connection] = None
) -> None:
await (conn or db).execute(
"UPDATE apipayments SET pending = ? WHERE checking_id = ?",
(
pending,
checking_id,
),
(pending, checking_id),
)
async def delete_payment(
checking_id: str,
conn: Optional[Connection] = None,
) -> None:
async def delete_payment(checking_id: str, conn: Optional[Connection] = None) -> None:
await (conn or db).execute(
"DELETE FROM apipayments WHERE checking_id = ?", (checking_id,)
)
async def check_internal(
payment_hash: str,
conn: Optional[Connection] = None,
payment_hash: str, conn: Optional[Connection] = None
) -> Optional[str]:
row = await (conn or db).fetchone(
"""
@ -411,9 +402,7 @@ async def check_internal(
async def save_balance_check(
wallet_id: str,
url: str,
conn: Optional[Connection] = None,
wallet_id: str, url: str, conn: Optional[Connection] = None
):
domain = urlparse(url).netloc
@ -427,9 +416,7 @@ async def save_balance_check(
async def get_balance_check(
wallet_id: str,
domain: str,
conn: Optional[Connection] = None,
wallet_id: str, domain: str, conn: Optional[Connection] = None
) -> Optional[BalanceCheck]:
row = await (conn or db).fetchone(
"""
@ -452,9 +439,7 @@ async def get_balance_checks(conn: Optional[Connection] = None) -> List[BalanceC
async def save_balance_notify(
wallet_id: str,
url: str,
conn: Optional[Connection] = None,
wallet_id: str, url: str, conn: Optional[Connection] = None
):
await (conn or db).execute(
"""
@ -466,8 +451,7 @@ async def save_balance_notify(
async def get_balance_notify(
wallet_id: str,
conn: Optional[Connection] = None,
wallet_id: str, conn: Optional[Connection] = None
) -> Optional[str]:
row = await (conn or db).fetchone(
"""

View File

@ -30,13 +30,8 @@ class Wallet(BaseModel):
@property
def lnurlwithdraw_full(self) -> str:
url = url_for(
"/withdraw",
external=True,
usr=self.user,
wal=self.id,
)
url = url_for("/withdraw", external=True, usr=self.user, wal=self.id)
try:
return lnurl_encode(url)
except:
@ -47,9 +42,7 @@ class Wallet(BaseModel):
linking_key = hmac.digest(hashing_key, domain.encode("utf-8"), "sha256")
return SigningKey.from_string(
linking_key,
curve=SECP256k1,
hashfunc=hashlib.sha256,
linking_key, curve=SECP256k1, hashfunc=hashlib.sha256
)
async def get_payment(self, payment_hash: str) -> Optional["Payment"]:

View File

@ -147,9 +147,7 @@ async def pay_invoice(
# so the other side only has access to his new money when we are sure
# the payer has enough to deduct from
await update_payment_status(
checking_id=internal_checking_id,
pending=False,
conn=conn,
checking_id=internal_checking_id, pending=False, conn=conn
)
# notify receiver asynchronously
@ -213,10 +211,7 @@ async def redeem_lnurl_withdraw(
if wait_seconds:
await asyncio.sleep(wait_seconds)
params = {
"k1": res["k1"],
"pr": payment_request,
}
params = {"k1": res["k1"], "pr": payment_request}
try:
params["balanceNotify"] = url_for(
@ -235,8 +230,7 @@ async def redeem_lnurl_withdraw(
async def perform_lnurlauth(
callback: str,
conn: Optional[Connection] = None,
callback: str, conn: Optional[Connection] = None
) -> Optional[LnurlErrorResponse]:
cb = urlparse(callback)
@ -304,14 +298,12 @@ async def perform_lnurlauth(
return LnurlErrorResponse(reason=resp["reason"])
except (KeyError, json.decoder.JSONDecodeError):
return LnurlErrorResponse(
reason=r.text[:200] + "..." if len(r.text) > 200 else r.text,
reason=r.text[:200] + "..." if len(r.text) > 200 else r.text
)
async def check_invoice_status(
wallet_id: str,
payment_hash: str,
conn: Optional[Connection] = None,
wallet_id: str, payment_hash: str, conn: Optional[Connection] = None
) -> PaymentStatus:
payment = await get_wallet_payment(wallet_id, payment_hash, conn=conn)
if not payment:

View File

@ -33,10 +33,7 @@ async def wait_for_paid_invoices(invoice_paid_queue: asyncio.Queue):
if url:
async with httpx.AsyncClient() as client:
try:
r = await client.post(
url,
timeout=4,
)
r = await client.post(url, timeout=4)
await mark_webhook_sent(payment, r.status_code)
except (httpx.ConnectError, httpx.RequestError):
pass
@ -55,11 +52,7 @@ async def dispatch_webhook(payment: Payment):
async with httpx.AsyncClient() as client:
data = payment._asdict()
try:
r = await client.post(
payment.webhook,
json=data,
timeout=40,
)
r = await client.post(payment.webhook, json=data, timeout=40)
await mark_webhook_sent(payment, r.status_code)
except (httpx.ConnectError, httpx.RequestError):
await mark_webhook_sent(payment, -1)

View File

@ -16,53 +16,67 @@ from pydantic import BaseModel
from lnbits import bolt11, lnurl
from lnbits.core.models import Payment, Wallet
from lnbits.decorators import (WalletAdminKeyChecker, WalletInvoiceKeyChecker,
WalletTypeInfo, get_key_type)
from lnbits.decorators import (
WalletAdminKeyChecker,
WalletInvoiceKeyChecker,
WalletTypeInfo,
get_key_type,
)
from lnbits.helpers import url_for
from lnbits.requestvars import g
from lnbits.utils.exchange_rates import currencies, fiat_amount_as_satoshis
from .. import core_app, db
from ..crud import get_payments, save_balance_check, update_wallet
from ..services import (InvoiceFailure, PaymentFailure, create_invoice,
pay_invoice, perform_lnurlauth)
from ..services import (
InvoiceFailure,
PaymentFailure,
create_invoice,
pay_invoice,
perform_lnurlauth,
)
from ..tasks import api_invoice_listeners
@core_app.get("/api/v1/wallet")
async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
return {"id": wallet.wallet.id, "name": wallet.wallet.name, "balance": wallet.wallet.balance_msat}
return {
"id": wallet.wallet.id,
"name": wallet.wallet.name,
"balance": wallet.wallet.balance_msat,
}
@core_app.put("/api/v1/wallet/{new_name}")
async def api_update_wallet(new_name: str, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_update_wallet(
new_name: str, wallet: WalletTypeInfo = Depends(get_key_type)
):
await update_wallet(wallet.wallet.id, new_name)
return {
"id": wallet.wallet.id,
"name": wallet.wallet.name,
"balance": wallet.wallet.balance_msat,
}
@core_app.get("/api/v1/payments")
async def api_payments(wallet: WalletTypeInfo = Depends(get_key_type)):
return await get_payments(wallet_id=wallet.wallet.id, pending=True, complete=True)
class CreateInvoiceData(BaseModel):
out: Optional[bool] = True
amount: int = Query(None, ge=1)
memo: str = None
unit: Optional[str] = None
description_hash: str = None
lnurl_callback: Optional[str] = None
lnurl_balance_check: Optional[str] = None
extra: Optional[dict] = None
webhook: Optional[str] = None
amount: int = Query(None, ge=1)
memo: str = None
unit: Optional[str] = None
description_hash: str = None
lnurl_callback: Optional[str] = None
lnurl_balance_check: Optional[str] = None
extra: Optional[dict] = None
webhook: Optional[str] = None
bolt11: Optional[str] = None
async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
if "description_hash" in data:
description_hash = unhexlify(data.description_hash)
@ -89,7 +103,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
conn=conn,
)
except InvoiceFailure as e:
raise HTTPException(status_code=520, detail=str(e))
raise HTTPException(status_code=520, detail=str(e))
except Exception as exc:
raise exc
@ -132,31 +146,17 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
"checking_id": invoice.payment_hash,
"lnurl_response": lnurl_response,
}
async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
try:
payment_hash = await pay_invoice(
wallet_id=wallet.id,
payment_request=bolt11,
)
payment_hash = await pay_invoice(wallet_id=wallet.id, payment_request=bolt11)
except ValueError as e:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=str(e)
)
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
except PermissionError as e:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail=str(e)
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail=str(e))
except PaymentFailure as e:
raise HTTPException(
status_code=520,
detail=str(e)
)
raise HTTPException(status_code=520, detail=str(e))
except Exception as exc:
raise exc
@ -165,34 +165,46 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
# maintain backwards compatibility with API clients:
"checking_id": payment_hash,
}
@core_app.post("/api/v1/payments", deprecated=True,
description="DEPRECATED. Use /api/v2/TBD and /api/v2/TBD instead",
status_code=HTTPStatus.CREATED)
async def api_payments_create(wallet: WalletTypeInfo = Depends(get_key_type),
invoiceData: CreateInvoiceData = Body(...)):
@core_app.post(
"/api/v1/payments",
deprecated=True,
description="DEPRECATED. Use /api/v2/TBD and /api/v2/TBD instead",
status_code=HTTPStatus.CREATED,
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(get_key_type),
invoiceData: CreateInvoiceData = Body(...),
):
if wallet.wallet_type < 0 or wallet.wallet_type > 2:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Key is invalid")
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="BOLT11 string is invalid or not given")
return await api_payments_pay_invoice(invoiceData.bolt11, wallet.wallet) # admin key
return await api_payments_create_invoice(invoiceData, wallet.wallet) # invoice key
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="BOLT11 string is invalid or not given",
)
return await api_payments_pay_invoice(
invoiceData.bolt11, wallet.wallet
) # admin key
return await api_payments_create_invoice(invoiceData, wallet.wallet) # invoice key
class CreateLNURLData(BaseModel):
description_hash: str
callback: str
amount: int
comment: Optional[str] = None
description: Optional[str] = None
description_hash: str
callback: str
amount: int
comment: Optional[str] = None
description: Optional[str] = None
@core_app.post("/api/v1/payments/lnurl")
async def api_payments_pay_lnurl(data: CreateLNURLData,
wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_payments_pay_lnurl(
data: CreateLNURLData, wallet: WalletTypeInfo = Depends(get_key_type)
):
domain = urlparse(data.callback).netloc
async with httpx.AsyncClient() as client:
@ -207,30 +219,28 @@ async def api_payments_pay_lnurl(data: CreateLNURLData,
except (httpx.ConnectError, httpx.RequestError):
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=f"Failed to connect to {domain}."
detail=f"Failed to connect to {domain}.",
)
params = json.loads(r.text)
if params.get("status") == "ERROR":
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=f"{domain} said: '{params.get('reason', '')}'"
status_code=HTTPStatus.BAD_REQUEST,
detail=f"{domain} said: '{params.get('reason', '')}'",
)
invoice = bolt11.decode(params["pr"])
if invoice.amount_msat != data.amount:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=f"{domain} returned an invalid invoice. Expected {data['amount']} msat, got {invoice.amount_msat}."
status_code=HTTPStatus.BAD_REQUEST,
detail=f"{domain} returned an invalid invoice. Expected {data['amount']} msat, got {invoice.amount_msat}.",
)
if invoice.description_hash != data.description_hash:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=f"{domain} returned an invalid invoice. Expected description_hash == {data['description_hash']}, got {invoice.description_hash}."
status_code=HTTPStatus.BAD_REQUEST,
detail=f"{domain} returned an invalid invoice. Expected description_hash == {data['description_hash']}, got {invoice.description_hash}.",
)
extra = {}
@ -252,7 +262,7 @@ async def api_payments_pay_lnurl(data: CreateLNURLData,
# maintain backwards compatibility with API clients:
"checking_id": payment_hash,
}
async def subscribe(request: Request, wallet: Wallet):
this_wallet_id = wallet.wallet.id
@ -278,7 +288,7 @@ async def subscribe(request: Request, wallet: Wallet):
if data:
jdata = json.dumps(dict(data.dict(), pending=False))
# yield dict(id=1, event="this", data="1234")
# await asyncio.sleep(2)
yield dict(data=jdata, event=typ)
@ -288,8 +298,12 @@ async def subscribe(request: Request, wallet: Wallet):
@core_app.get("/api/v1/payments/sse")
async def api_payments_sse(request: Request, wallet: WalletTypeInfo = Depends(get_key_type)):
return EventSourceResponse(subscribe(request, wallet), ping=20, media_type="text/event-stream")
async def api_payments_sse(
request: Request, wallet: WalletTypeInfo = Depends(get_key_type)
):
return EventSourceResponse(
subscribe(request, wallet), ping=20, media_type="text/event-stream"
)
@core_app.get("/api/v1/payments/{payment_hash}")
@ -307,9 +321,11 @@ async def api_payment(payment_hash, wallet: WalletTypeInfo = Depends(get_key_typ
return {"paid": False}
return {"paid": not payment.pending, "preimage": payment.preimage}
@core_app.get("/api/v1/lnurlscan/{code}", dependencies=[Depends(WalletInvoiceKeyChecker())])
@core_app.get(
"/api/v1/lnurlscan/{code}", dependencies=[Depends(WalletInvoiceKeyChecker())]
)
async def api_lnurlscan(code: str):
try:
url = lnurl.decode(code)
@ -327,7 +343,9 @@ async def api_lnurlscan(code: str):
)
# will proceed with these values
else:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl")
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl"
)
# params is what will be returned to the client
params: Dict = {"domain": domain}
@ -343,24 +361,31 @@ async def api_lnurlscan(code: str):
r = await client.get(url, timeout=5)
if r.is_error:
raise HTTPException(
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
detail={"domain": domain, "message": "failed to get parameters"}
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
detail={"domain": domain, "message": "failed to get parameters"},
)
try:
data = json.loads(r.text)
except json.decoder.JSONDecodeError:
raise HTTPException(
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
detail={"domain": domain, "message": f"got invalid response '{r.text[:200]}'"}
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
detail={
"domain": domain,
"message": f"got invalid response '{r.text[:200]}'",
},
)
try:
tag = data["tag"]
if tag == "channelRequest":
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail={"domain": domain, "kind": "channel", "message": "unsupported"}
status_code=HTTPStatus.BAD_REQUEST,
detail={
"domain": domain,
"kind": "channel",
"message": "unsupported",
},
)
params.update(**data)
@ -410,8 +435,9 @@ async def api_lnurlscan(code: str):
detail={
"domain": domain,
"message": f"lnurl JSON response invalid: {exc}",
})
},
)
return params
@ -419,8 +445,10 @@ async def api_lnurlscan(code: str):
async def api_perform_lnurlauth(callback: str):
err = await perform_lnurlauth(callback)
if err:
raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=err.reason)
raise HTTPException(
status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=err.reason
)
return ""

View File

@ -17,38 +17,48 @@ from lnbits.helpers import template_renderer, url_for
from lnbits.requestvars import g
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from lnbits.settings import (LNBITS_ALLOWED_USERS, LNBITS_SITE_TITLE,
SERVICE_FEE)
from lnbits.settings import LNBITS_ALLOWED_USERS, LNBITS_SITE_TITLE, SERVICE_FEE
from ..crud import (create_account, create_wallet, delete_wallet,
get_balance_check, get_user, save_balance_notify,
update_user_extension)
from ..crud import (
create_account,
create_wallet,
delete_wallet,
get_balance_check,
get_user,
save_balance_notify,
update_user_extension,
)
from ..services import pay_invoice, redeem_lnurl_withdraw
core_html_routes: APIRouter = APIRouter(tags=["Core NON-API Website Routes"])
@core_html_routes.get("/favicon.ico")
async def favicon():
return FileResponse("lnbits/core/static/favicon.ico")
@core_html_routes.get("/", response_class=HTMLResponse)
async def home(request: Request, lightning: str = None):
return template_renderer().TemplateResponse("core/index.html", {"request": request, "lnurl": lightning})
return template_renderer().TemplateResponse(
"core/index.html", {"request": request, "lnurl": lightning}
)
@core_html_routes.get("/extensions", name="core.extensions")
async def extensions(
request: Request,
request: Request,
user: User = Depends(check_user_exists),
enable: str= Query(None),
disable: str = Query(None)
):
enable: str = Query(None),
disable: str = Query(None),
):
extension_to_enable = enable
extension_to_disable = disable
if extension_to_enable and extension_to_disable:
raise HTTPException(HTTPStatus.BAD_REQUEST, "You can either `enable` or `disable` an extension.")
raise HTTPException(
HTTPStatus.BAD_REQUEST, "You can either `enable` or `disable` an extension."
)
if extension_to_enable:
await update_user_extension(
@ -63,14 +73,20 @@ async def extensions(
if extension_to_enable or extension_to_disable:
user = await get_user(user.id)
return template_renderer().TemplateResponse("core/extensions.html", {"request": request, "user": user.dict()})
return template_renderer().TemplateResponse(
"core/extensions.html", {"request": request, "user": user.dict()}
)
@core_html_routes.get("/wallet", response_class=HTMLResponse)
#Not sure how to validate
# Not sure how to validate
# @validate_uuids(["usr", "nme"])
async def wallet(request: Request = Query(None), nme: Optional[str] = Query(None),
usr: Optional[UUID4] = Query(None), wal: Optional[UUID4] = Query(None)):
async def wallet(
request: Request = Query(None),
nme: Optional[str] = Query(None),
usr: Optional[UUID4] = Query(None),
wal: Optional[UUID4] = Query(None),
):
user_id = usr.hex if usr else None
wallet_id = wal.hex if wal else None
wallet_name = nme
@ -87,23 +103,38 @@ async def wallet(request: Request = Query(None), nme: Optional[str] = Query(None
else:
user = await get_user(user_id)
if not user:
return template_renderer().TemplateResponse("error.html", {"request": request, "err": "User does not exist."})
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "User does not exist."}
)
if LNBITS_ALLOWED_USERS and user_id not in LNBITS_ALLOWED_USERS:
return template_renderer().TemplateResponse("error.html", {"request": request, "err": "User not authorized."})
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "User not authorized."}
)
if not wallet_id:
if user.wallets and not wallet_name:
wallet = user.wallets[0]
else:
wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name)
return RedirectResponse(f"/wallet?usr={user.id}&wal={wallet.id}", status_code=status.HTTP_307_TEMPORARY_REDIRECT)
return RedirectResponse(
f"/wallet?usr={user.id}&wal={wallet.id}",
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
wallet = user.get_wallet(wallet_id)
if not wallet:
return template_renderer().TemplateResponse("error.html", {"request": request, "err": "Wallet not found"})
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "Wallet not found"}
)
return template_renderer().TemplateResponse(
"core/wallet.html", {"request":request,"user":user.dict(), "wallet":wallet.dict(), "service_fee":service_fee}
"core/wallet.html",
{
"request": request,
"user": user.dict(),
"wallet": wallet.dict(),
"service_fee": service_fee,
},
)
@ -116,22 +147,17 @@ async def lnurl_full_withdraw(request: Request):
wallet = user.get_wallet(request.args.get("wal"))
if not wallet:
return{"status": "ERROR", "reason": "Wallet does not exist."}
return {"status": "ERROR", "reason": "Wallet does not exist."}
return {
"tag": "withdrawRequest",
"callback": url_for(
"/withdraw/cb",
external=True,
usr=user.id,
wal=wallet.id,
),
"k1": "0",
"minWithdrawable": 1000 if wallet.withdrawable_balance else 0,
"maxWithdrawable": wallet.withdrawable_balance,
"defaultDescription": f"{LNBITS_SITE_TITLE} balance withdraw from {wallet.id[0:5]}",
"balanceCheck": url_for("/withdraw", external=True, usr=user.id, wal=wallet.id),
}
"tag": "withdrawRequest",
"callback": url_for("/withdraw/cb", external=True, usr=user.id, wal=wallet.id),
"k1": "0",
"minWithdrawable": 1000 if wallet.withdrawable_balance else 0,
"maxWithdrawable": wallet.withdrawable_balance,
"defaultDescription": f"{LNBITS_SITE_TITLE} balance withdraw from {wallet.id[0:5]}",
"balanceCheck": url_for("/withdraw", external=True, usr=user.id, wal=wallet.id),
}
@core_html_routes.get("/withdraw/cb")
@ -176,10 +202,14 @@ async def deletewallet(request: Request):
user_wallet_ids.remove(wallet_id)
if user_wallet_ids:
return RedirectResponse(url_for("/wallet", usr=g().user.id, wal=user_wallet_ids[0]),
status_code=status.HTTP_307_TEMPORARY_REDIRECT)
return RedirectResponse(
url_for("/wallet", usr=g().user.id, wal=user_wallet_ids[0]),
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
return RedirectResponse(url_for("/"), status_code=status.HTTP_307_TEMPORARY_REDIRECT)
return RedirectResponse(
url_for("/"), status_code=status.HTTP_307_TEMPORARY_REDIRECT
)
@core_html_routes.get("/withdraw/notify/{service}")
@ -203,11 +233,14 @@ async def lnurlwallet(request: Request):
request.args.get("lightning"),
"LNbits initial funding: voucher redeem.",
{"tag": "lnurlwallet"},
5 # wait 5 seconds before sending the invoice to the service
5, # wait 5 seconds before sending the invoice to the service
)
)
return RedirectResponse(f"/wallet?usr={user.id}&wal={wallet.id}", status_code=status.HTTP_307_TEMPORARY_REDIRECT)
return RedirectResponse(
f"/wallet?usr={user.id}&wal={wallet.id}",
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
@core_html_routes.get("/manifest/{usr}.webmanifest")
@ -217,27 +250,28 @@ async def manifest(usr: str):
raise HTTPException(status_code=HTTPStatus.NOT_FOUND)
return {
"short_name": "LNbits",
"name": "LNbits Wallet",
"icons": [
{
"src": "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
"type": "image/png",
"sizes": "900x900",
}
],
"start_url": "/wallet?usr=" + usr,
"background_color": "#3367D6",
"description": "Weather forecast information",
"display": "standalone",
"scope": "/",
"theme_color": "#3367D6",
"shortcuts": [
{
"name": wallet.name,
"short_name": wallet.name,
"description": wallet.name,
"url": "/wallet?usr=" + usr + "&wal=" + wallet.id,
}
for wallet in user.wallets
],}
"short_name": "LNbits",
"name": "LNbits Wallet",
"icons": [
{
"src": "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png",
"type": "image/png",
"sizes": "900x900",
}
],
"start_url": "/wallet?usr=" + usr,
"background_color": "#3367D6",
"description": "Weather forecast information",
"display": "standalone",
"scope": "/",
"theme_color": "#3367D6",
"shortcuts": [
{
"name": wallet.name,
"short_name": wallet.name,
"description": wallet.name,
"url": "/wallet?usr=" + usr + "&wal=" + wallet.id,
}
for wallet in user.wallets
],
}

View File

@ -15,8 +15,7 @@ async def api_public_payment_longpolling(payment_hash):
if not payment:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Payment does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist."
)
elif not payment.pending:
return {"status": "paid"}
@ -28,8 +27,7 @@ async def api_public_payment_longpolling(payment_hash):
return {"status": "expired"}
except:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Invalid bolt11 invoice."
status_code=HTTPStatus.BAD_REQUEST, detail="Invalid bolt11 invoice."
)
payment_queue = asyncio.Queue(0)
@ -50,14 +48,10 @@ async def api_public_payment_longpolling(payment_hash):
await asyncio.sleep(45)
cancel_scope.cancel()
asyncio.create_task(payment_info_receiver())
asyncio.create_task(timeouter())
if response:
return response
else:
raise HTTPException(
status_code=HTTPStatus.REQUEST_TIMEOUT,
detail="timeout"
)
raise HTTPException(status_code=HTTPStatus.REQUEST_TIMEOUT, detail="timeout")

View File

@ -24,34 +24,49 @@ from lnbits.settings import LNBITS_ALLOWED_USERS
class KeyChecker(SecurityBase):
def __init__(self, scheme_name: str = None, auto_error: bool = True, api_key: str = None):
def __init__(
self, scheme_name: str = None, auto_error: bool = True, api_key: str = None
):
self.scheme_name = scheme_name or self.__class__.__name__
self.auto_error = auto_error
self._key_type = "invoice"
self._api_key = api_key
if api_key:
self.model: APIKey= APIKey(
**{"in": APIKeyIn.query}, name="X-API-KEY", description="Wallet API Key - QUERY"
self.model: APIKey = APIKey(
**{"in": APIKeyIn.query},
name="X-API-KEY",
description="Wallet API Key - QUERY",
)
else:
self.model: APIKey= APIKey(
**{"in": APIKeyIn.header}, name="X-API-KEY", description="Wallet API Key - HEADER"
self.model: APIKey = APIKey(
**{"in": APIKeyIn.header},
name="X-API-KEY",
description="Wallet API Key - HEADER",
)
self.wallet = None
async def __call__(self, request: Request) -> Wallet:
try:
key_value = self._api_key if self._api_key else request.headers.get("X-API-KEY") or request.query_params["api-key"]
key_value = (
self._api_key
if self._api_key
else request.headers.get("X-API-KEY") or request.query_params["api-key"]
)
# FIXME: Find another way to validate the key. A fetch from DB should be avoided here.
# Also, we should not return the wallet here - thats silly.
# Possibly store it in a Redis DB
self.wallet = await get_wallet_for_key(key_value, self._key_type)
if not self.wallet:
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="Invalid key or expired key.")
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
detail="Invalid key or expired key.",
)
except KeyError:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST,
detail="`X-API-KEY` header missing.")
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="`X-API-KEY` header missing."
)
class WalletInvoiceKeyChecker(KeyChecker):
"""
@ -61,10 +76,14 @@ class WalletInvoiceKeyChecker(KeyChecker):
The checker will raise an HTTPException when the key is wrong in some ways.
"""
def __init__(self, scheme_name: str = None, auto_error: bool = True, api_key: str = None):
def __init__(
self, scheme_name: str = None, auto_error: bool = True, api_key: str = None
):
super().__init__(scheme_name, auto_error, api_key)
self._key_type = "invoice"
class WalletAdminKeyChecker(KeyChecker):
"""
WalletAdminKeyChecker will ensure that the provided admin
@ -73,11 +92,15 @@ class WalletAdminKeyChecker(KeyChecker):
The checker will raise an HTTPException when the key is wrong in some ways.
"""
def __init__(self, scheme_name: str = None, auto_error: bool = True, api_key: str = None):
def __init__(
self, scheme_name: str = None, auto_error: bool = True, api_key: str = None
):
super().__init__(scheme_name, auto_error, api_key)
self._key_type = "admin"
class WalletTypeInfo():
class WalletTypeInfo:
wallet_type: int
wallet: Wallet
@ -85,20 +108,33 @@ class WalletTypeInfo():
self.wallet_type = wallet_type
self.wallet = wallet
api_key_header = APIKeyHeader(name="X-API-KEY", auto_error=False, description="Admin or Invoice key for wallet API's")
api_key_query = APIKeyQuery(name="api-key", auto_error=False, description="Admin or Invoice key for wallet API's")
async def get_key_type(r: Request,
api_key_header: str = Security(api_key_header),
api_key_query: str = Security(api_key_query)) -> WalletTypeInfo:
api_key_header = APIKeyHeader(
name="X-API-KEY",
auto_error=False,
description="Admin or Invoice key for wallet API's",
)
api_key_query = APIKeyQuery(
name="api-key",
auto_error=False,
description="Admin or Invoice key for wallet API's",
)
async def get_key_type(
r: Request,
api_key_header: str = Security(api_key_header),
api_key_query: str = Security(api_key_query),
) -> WalletTypeInfo:
# 0: admin
# 1: invoice
# 2: invalid
if not api_key_header and not api_key_query:
if not api_key_header and not api_key_query:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST)
token = api_key_header if api_key_header else api_key_query
try:
checker = WalletAdminKeyChecker(api_key=token)
await checker.__call__(r)
@ -122,6 +158,8 @@ async def get_key_type(r: Request,
return WalletTypeInfo(2, None)
except:
raise
# api_key_header = APIKeyHeader(name="X-API-KEY", auto_error=False, description="Admin or Invoice key for wallet API's")
# api_key_query = APIKeyQuery(name="api-key", auto_error=False, description="Admin or Invoice key for wallet API's")
# oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@ -159,6 +197,7 @@ async def get_key_type(r: Request,
# except:
# raise
def api_validate_post_request(*, schema: dict):
def wrap(view):
@wraps(view)
@ -166,7 +205,9 @@ def api_validate_post_request(*, schema: dict):
if "application/json" not in request.headers["Content-Type"]:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=jsonify({"message": "Content-Type must be `application/json`."})
detail=jsonify(
{"message": "Content-Type must be `application/json`."}
),
)
v = Validator(schema)
@ -176,10 +217,9 @@ def api_validate_post_request(*, schema: dict):
if not v.validate(g().data):
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=jsonify({"message": f"Errors in request data: {v.errors}"})
detail=jsonify({"message": f"Errors in request data: {v.errors}"}),
)
return await view(**kwargs)
return wrapped_view
@ -191,14 +231,12 @@ async def check_user_exists(usr: UUID4) -> User:
g().user = await get_user(usr.hex)
if not g().user:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="User does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
)
if LNBITS_ALLOWED_USERS and g().user.id not in LNBITS_ALLOWED_USERS:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
detail="User not authorized."
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
)
return g().user

View File

@ -19,11 +19,7 @@ copilot_ext: APIRouter = APIRouter(prefix="/copilot", tags=["copilot"])
def copilot_renderer():
return template_renderer(
[
"lnbits/extensions/copilot/templates",
]
)
return template_renderer(["lnbits/extensions/copilot/templates"])
from .views_api import * # noqa

View File

@ -8,7 +8,11 @@ from http import HTTPStatus
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse, JSONResponse # type: ignore
import base64
from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore
from lnurl import (
LnurlPayResponse,
LnurlPayActionResponse,
LnurlErrorResponse,
) # type: ignore
from lnurl.types import LnurlPayMetadata
from lnbits.core.services import create_invoice
from .models import Copilots, CreateCopilotData
@ -24,8 +28,7 @@ async def lnurl_response(req: Request, cp_id: str = Query(None)):
cp = await get_copilot(cp_id)
if not cp:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot not found",
status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found"
)
resp = LnurlPayResponse(
@ -49,8 +52,7 @@ async def lnurl_callback(
cp = await get_copilot(cp_id)
if not cp:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot not found",
status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found"
)
amount_received = int(amount)
@ -84,9 +86,6 @@ async def lnurl_callback(
extra={"tag": "copilot", "copilot": cp.id, "comment": comment},
)
resp = LnurlPayActionResponse(
pr=payment_request,
success_action=None,
disposable=False,
routes=[],
pr=payment_request, success_action=None, disposable=False, routes=[]
)
return resp.dict()

View File

@ -38,8 +38,7 @@ async def on_invoice_paid(payment: Payment) -> None:
if not copilot:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot does not exist",
status_code=HTTPStatus.NOT_FOUND, detail="Copilot does not exist"
)
if copilot.animation1threshold:
if int(payment.amount / 1000) >= copilot.animation1threshold:

View File

@ -44,10 +44,7 @@ async def api_copilots_retrieve(
try:
return copilots
except:
raise HTTPException(
status_code=HTTPStatus.NO_CONTENT,
detail="No copilots",
)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT, detail="No copilots")
@copilot_ext.get("/api/v1/copilot/{copilot_id}")
@ -57,8 +54,7 @@ async def api_copilot_retrieve(
copilot = await get_copilot(copilot_id)
if not copilot:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot not found",
status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found"
)
if not copilot.lnurl_toggle:
return copilot.dict()
@ -83,15 +79,13 @@ async def api_copilot_create_or_update(
@copilot_ext.delete("/api/v1/copilot/{copilot_id}")
async def api_copilot_delete(
copilot_id: str = Query(None),
wallet: WalletTypeInfo = Depends(get_key_type),
copilot_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)
):
copilot = await get_copilot(copilot_id)
if not copilot:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot does not exist",
status_code=HTTPStatus.NOT_FOUND, detail="Copilot does not exist"
)
await delete_copilot(copilot_id)
@ -101,22 +95,16 @@ async def api_copilot_delete(
@copilot_ext.get("/api/v1/copilot/ws/{copilot_id}/{comment}/{data}")
async def api_copilot_ws_relay(
copilot_id: str = Query(None),
comment: str = Query(None),
data: str = Query(None),
copilot_id: str = Query(None), comment: str = Query(None), data: str = Query(None)
):
copilot = await get_copilot(copilot_id)
print(copilot)
if not copilot:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Copilot does not exist",
status_code=HTTPStatus.NOT_FOUND, detail="Copilot does not exist"
)
try:
await updater(copilot_id, data, comment)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Not your copilot",
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your copilot")
return ""

View File

@ -20,11 +20,7 @@ jukebox_ext: APIRouter = APIRouter(prefix="/jukebox", tags=["jukebox"])
def jukebox_renderer():
return template_renderer(
[
"lnbits/extensions/jukebox/templates",
]
)
return template_renderer(["lnbits/extensions/jukebox/templates"])
from .views_api import * # noqa

View File

@ -6,8 +6,7 @@ from lnbits.helpers import urlsafe_short_hash
async def create_jukebox(
data: CreateJukeLinkData,
inkey: Optional[str] = "",
data: CreateJukeLinkData, inkey: Optional[str] = ""
) -> Jukebox:
juke_id = urlsafe_short_hash()
result = await db.execute(
@ -87,12 +86,7 @@ async def create_jukebox_payment(data: CreateJukeboxPayment) -> JukeboxPayment:
INSERT INTO jukebox.jukebox_payment (payment_hash, juke_id, song_id, paid)
VALUES (?, ?, ?, ?)
""",
(
data.payment_hash,
data.juke_id,
data.song_id,
False,
),
(data.payment_hash, data.juke_id, data.song_id, False),
)
jukebox_payment = await get_jukebox_payment(data.payment_hash)
assert jukebox_payment, "Newly created Jukebox Payment couldn't be retrieved"

View File

@ -44,10 +44,7 @@ async def api_get_jukeboxs(
return jukeboxs
except:
raise HTTPException(
status_code=HTTPStatus.NO_CONTENT,
detail="No Jukeboxes",
)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT, detail="No Jukeboxes")
##################SPOTIFY AUTH#####################
@ -102,18 +99,12 @@ async def api_create_update_jukebox(
@jukebox_ext.delete("/api/v1/jukebox/{juke_id}")
async def api_delete_item(
juke_id=None,
wallet: WalletTypeInfo = Depends(get_key_type),
):
async def api_delete_item(juke_id=None, wallet: WalletTypeInfo = Depends(get_key_type)):
await delete_jukebox(juke_id)
try:
return [{**jukebox} for jukebox in await get_jukeboxs(wallet.wallet.user)]
except:
raise HTTPException(
status_code=HTTPStatus.NO_CONTENT,
detail="No Jukebox",
)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT, detail="No Jukebox")
################JUKEBOX ENDPOINTS##################
@ -130,10 +121,7 @@ async def api_get_jukebox_song(
try:
jukebox = await get_jukebox(juke_id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No Jukeboxes",
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No Jukeboxes")
tracks = []
async with httpx.AsyncClient() as client:
try:
@ -176,10 +164,7 @@ async def api_get_token(juke_id=None):
try:
jukebox = await get_jukebox(juke_id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No Jukeboxes",
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No Jukeboxes")
async with httpx.AsyncClient() as client:
try:
@ -215,16 +200,12 @@ async def api_get_token(juke_id=None):
@jukebox_ext.get("/api/v1/jukebox/jb/{juke_id}")
async def api_get_jukebox_device_check(
juke_id: str = Query(None),
retry: bool = Query(False),
juke_id: str = Query(None), retry: bool = Query(False)
):
try:
jukebox = await get_jukebox(juke_id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No Jukeboxes",
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No Jukeboxes")
async with httpx.AsyncClient() as client:
rDevice = await client.get(
"https://api.spotify.com/v1/me/player/devices",
@ -237,20 +218,17 @@ async def api_get_jukebox_device_check(
token = await api_get_token(juke_id)
if token == False:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No devices connected",
status_code=HTTPStatus.FORBIDDEN, detail="No devices connected"
)
elif retry:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Failed to get auth",
status_code=HTTPStatus.FORBIDDEN, detail="Failed to get auth"
)
else:
return api_get_jukebox_device_check(juke_id, retry=True)
else:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No device connected",
status_code=HTTPStatus.FORBIDDEN, detail="No device connected"
)
@ -263,10 +241,7 @@ async def api_get_jukebox_invoice(juke_id, song_id):
jukebox = await get_jukebox(juke_id)
print(jukebox)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No jukebox",
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No jukebox")
try:
devices = await api_get_jukebox_device_check(juke_id)
@ -276,13 +251,11 @@ async def api_get_jukebox_invoice(juke_id, song_id):
deviceConnected = True
if not deviceConnected:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="No device connected",
status_code=HTTPStatus.NOT_FOUND, detail="No device connected"
)
except:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="No device connected",
status_code=HTTPStatus.NOT_FOUND, detail="No device connected"
)
invoice = await create_invoice(
@ -304,16 +277,12 @@ async def api_get_jukebox_invoice(juke_id, song_id):
@jukebox_ext.get("/api/v1/jukebox/jb/checkinvoice/{pay_hash}/{juke_id}")
async def api_get_jukebox_invoice_check(
pay_hash: str = Query(None),
juke_id: str = Query(None),
pay_hash: str = Query(None), juke_id: str = Query(None)
):
try:
jukebox = await get_jukebox(juke_id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No jukebox",
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No jukebox")
try:
status = await check_invoice_status(jukebox.wallet, pay_hash)
is_paid = not status.pending
@ -338,10 +307,7 @@ async def api_get_jukebox_invoice_paid(
try:
jukebox = await get_jukebox(juke_id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No jukebox",
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No jukebox")
await api_get_jukebox_invoice_check(pay_hash, juke_id)
jukebox_payment = await get_jukebox_payment(pay_hash)
if jukebox_payment.paid:
@ -390,8 +356,7 @@ async def api_get_jukebox_invoice_paid(
)
else:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Invoice not paid",
status_code=HTTPStatus.FORBIDDEN, detail="Invoice not paid"
)
elif r.status_code == 200:
async with httpx.AsyncClient() as client:
@ -424,29 +389,23 @@ async def api_get_jukebox_invoice_paid(
)
else:
raise HTTPException(
status_code=HTTPStatus.OK,
detail="Invoice not paid",
status_code=HTTPStatus.OK, detail="Invoice not paid"
)
elif r.status_code == 401 or r.status_code == 403:
token = await api_get_token(juke_id)
if token == False:
raise HTTPException(
status_code=HTTPStatus.OK,
detail="Invoice not paid",
status_code=HTTPStatus.OK, detail="Invoice not paid"
)
elif retry:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Failed to get auth",
status_code=HTTPStatus.FORBIDDEN, detail="Failed to get auth"
)
else:
return await api_get_jukebox_invoice_paid(
song_id, juke_id, pay_hash
)
raise HTTPException(
status_code=HTTPStatus.OK,
detail="Invoice not paid",
)
raise HTTPException(status_code=HTTPStatus.OK, detail="Invoice not paid")
############################GET TRACKS
@ -454,16 +413,12 @@ async def api_get_jukebox_invoice_paid(
@jukebox_ext.get("/api/v1/jukebox/jb/currently/{juke_id}")
async def api_get_jukebox_currently(
retry: bool = Query(False),
juke_id: str = Query(None),
retry: bool = Query(False), juke_id: str = Query(None)
):
try:
jukebox = await get_jukebox(juke_id)
except:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="No jukebox",
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="No jukebox")
async with httpx.AsyncClient() as client:
try:
r = await client.get(
@ -472,10 +427,7 @@ async def api_get_jukebox_currently(
headers={"Authorization": "Bearer " + jukebox.sp_access_token},
)
if r.status_code == 204:
raise HTTPException(
status_code=HTTPStatus.OK,
detail="Nothing",
)
raise HTTPException(status_code=HTTPStatus.OK, detail="Nothing")
elif r.status_code == 200:
try:
response = r.json()
@ -490,31 +442,26 @@ async def api_get_jukebox_currently(
return track
except:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Something went wrong",
status_code=HTTPStatus.NOT_FOUND, detail="Something went wrong"
)
elif r.status_code == 401:
token = await api_get_token(juke_id)
if token == False:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="INvoice not paid",
status_code=HTTPStatus.FORBIDDEN, detail="INvoice not paid"
)
elif retry:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Failed to get auth",
status_code=HTTPStatus.FORBIDDEN, detail="Failed to get auth"
)
else:
return await api_get_jukebox_currently(retry=True, juke_id=juke_id)
else:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Something went wrong",
status_code=HTTPStatus.NOT_FOUND, detail="Something went wrong"
)
except:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Something went wrong",
status_code=HTTPStatus.NOT_FOUND, detail="Something went wrong"
)

View File

@ -12,11 +12,7 @@ lndhub_ext: APIRouter = APIRouter(prefix="/lndhub", tags=["lndhub"])
def lndhub_renderer():
return template_renderer(
[
"lnbits/extensions/lndhub/templates",
]
)
return template_renderer(["lnbits/extensions/lndhub/templates"])
from .views_api import * # noqa

View File

@ -12,12 +12,19 @@ from starlette.responses import HTMLResponse, JSONResponse
from lnbits.decorators import WalletTypeInfo, get_key_type # type: ignore
api_key_header_auth = APIKeyHeader(name="AUTHORIZATION", auto_error=False, description="Admin or Invoice key for LNDHub API's")
async def check_wallet(r: Request, api_key_header_auth: str = Security(api_key_header_auth)) -> WalletTypeInfo:
api_key_header_auth = APIKeyHeader(
name="AUTHORIZATION",
auto_error=False,
description="Admin or Invoice key for LNDHub API's",
)
async def check_wallet(
r: Request, api_key_header_auth: str = Security(api_key_header_auth)
) -> WalletTypeInfo:
if not api_key_header_auth:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid auth key"
status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid auth key"
)
t = api_key_header_auth.split(" ")[1]
@ -26,14 +33,15 @@ async def check_wallet(r: Request, api_key_header_auth: str = Security(api_key_h
return await get_key_type(r, api_key_header=token)
async def require_admin_key(r: Request, api_key_header_auth: str = Security(api_key_header_auth)):
async def require_admin_key(
r: Request, api_key_header_auth: str = Security(api_key_header_auth)
):
wallet = await check_wallet(r, api_key_header_auth)
if wallet.wallet_type != 0:
# If wallet type is not admin then return the unauthorized status
# This also covers when the user passes an invalid key type
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Admin key required.",
status_code=status.HTTP_401_UNAUTHORIZED, detail="Admin key required."
)
else:
return wallet
else:
return wallet

View File

@ -23,10 +23,8 @@ from fastapi.security import OAuth2PasswordBearer
@lndhub_ext.get("/ext/getinfo")
async def lndhub_getinfo():
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
detail="bad auth",
)
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="bad auth")
class AuthData(BaseModel):
login: str = Query(None)
@ -35,16 +33,17 @@ class AuthData(BaseModel):
@lndhub_ext.post("/ext/auth")
async def lndhub_auth(
data: AuthData
):
async def lndhub_auth(data: AuthData):
token = (
data.refresh_token
if data.refresh_token
else urlsafe_b64encode((data.login + ":" + data.password).encode("utf-8")).decode("ascii")
else urlsafe_b64encode(
(data.login + ":" + data.password).encode("utf-8")
).decode("ascii")
)
return {"refresh_token": token, "access_token": token}
class AddInvoice(BaseModel):
amt: str = Query(None)
memo: str = Query(None)
@ -53,8 +52,7 @@ class AddInvoice(BaseModel):
@lndhub_ext.post("/ext/addinvoice")
async def lndhub_addinvoice(
data: AddInvoice,
wallet: WalletTypeInfo = Depends(check_wallet)
data: AddInvoice, wallet: WalletTypeInfo = Depends(check_wallet)
):
try:
_, pr = await create_invoice(
@ -65,8 +63,7 @@ async def lndhub_addinvoice(
)
except:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Failed to create invoice",
status_code=HTTPStatus.NOT_FOUND, detail="Failed to create invoice"
)
invoice = bolt11.decode(pr)
@ -78,9 +75,11 @@ async def lndhub_addinvoice(
"hash": invoice.payment_hash,
}
class Invoice(BaseModel):
invoice: str
@lndhub_ext.post("/ext/payinvoice")
async def lndhub_payinvoice(
r_invoice: Invoice, wallet: WalletTypeInfo = Depends(require_admin_key)
@ -92,10 +91,7 @@ async def lndhub_payinvoice(
extra={"tag": "lndhub"},
)
except:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Payment failed",
)
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Payment failed")
invoice: bolt11.Invoice = bolt11.decode(r_invoice.invoice)
print("INV2", invoice)
@ -116,9 +112,7 @@ async def lndhub_payinvoice(
@lndhub_ext.get("/ext/balance")
# @check_wallet()
async def lndhub_balance(
wallet: WalletTypeInfo = Depends(check_wallet),
):
async def lndhub_balance(wallet: WalletTypeInfo = Depends(check_wallet),):
return {"BTC": {"AvailableBalance": wallet.wallet.balance}}

View File

@ -14,12 +14,9 @@ lnticket_ext: APIRouter = APIRouter(
# "lnticket", __name__, static_folder="static", template_folder="templates"
)
def lnticket_renderer():
return template_renderer(
[
"lnbits/extensions/lnticket/templates",
]
)
return template_renderer(["lnbits/extensions/lnticket/templates"])
from .views_api import * # noqa
@ -30,4 +27,3 @@ from .tasks import wait_for_paid_invoices
def lnticket_start():
loop = asyncio.get_event_loop()
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))

View File

@ -9,16 +9,23 @@ import httpx
async def create_ticket(
payment_hash: str,
wallet: str,
data: CreateTicketData
payment_hash: str, wallet: str, data: CreateTicketData
) -> Tickets:
await db.execute(
"""
INSERT INTO lnticket.ticket (id, form, email, ltext, name, wallet, sats, paid)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(payment_hash, data.form, data.email, data.ltext, data.name, wallet, data.sats, False),
(
payment_hash,
data.form,
data.email,
data.ltext,
data.name,
wallet,
data.sats,
False,
),
)
ticket = await get_ticket(payment_hash)
@ -99,17 +106,23 @@ async def delete_ticket(ticket_id: str) -> None:
# FORMS
async def create_form(
data: CreateFormData,
wallet: Wallet,
) -> Forms:
async def create_form(data: CreateFormData, wallet: Wallet) -> Forms:
form_id = urlsafe_short_hash()
await db.execute(
"""
INSERT INTO lnticket.form2 (id, wallet, name, webhook, description, flatrate, amount, amountmade)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(form_id, wallet.id, wallet.name, data.webhook, data.description, data.flatrate, data.amount, 0),
(
form_id,
wallet.id,
wallet.name,
data.webhook,
data.description,
data.flatrate,
data.amount,
0,
),
)
form = await get_form(form_id)

View File

@ -79,16 +79,7 @@ async def m002_changed(db):
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
row[0],
row[1],
row[2],
row[3],
row[4],
row[5],
row[6],
True,
),
(row[0], row[1], row[2], row[3], row[4], row[5], row[6], True),
)
await db.execute("DROP TABLE lnticket.tickets")
@ -134,15 +125,7 @@ async def m003_changed(db):
)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(
row[0],
row[1],
row[2],
row[3],
row[4],
row[5],
row[6],
),
(row[0], row[1], row[2], row[3], row[4], row[5], row[6]),
)
await db.execute("DROP TABLE lnticket.forms")
@ -189,14 +172,6 @@ async def m004_changed(db):
)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(
row[0],
row[1],
row[2],
row[3],
row[4],
row[5],
row[6],
),
(row[0], row[1], row[2], row[3], row[4], row[5], row[6]),
)
await db.execute("DROP TABLE lnticket.form")

View File

@ -2,6 +2,7 @@ from typing import Optional
from fastapi.param_functions import Query
from pydantic import BaseModel
class CreateFormData(BaseModel):
name: str = Query(...)
webhook: str = Query(None)
@ -9,6 +10,7 @@ class CreateFormData(BaseModel):
amount: int = Query(..., ge=0)
flatrate: int = Query(...)
class CreateTicketData(BaseModel):
form: str = Query(...)
name: str = Query(...)
@ -16,6 +18,7 @@ class CreateTicketData(BaseModel):
ltext: str = Query(...)
sats: int = Query(..., ge=0)
class Forms(BaseModel):
id: str
wallet: str

View File

@ -14,13 +14,16 @@ from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates")
@lnticket_ext.get("/", response_class=HTMLResponse)
# not needed as we automatically get the user with the given ID
# If no user with this ID is found, an error is raised
# @validate_uuids(["usr"], required=True)
# @check_user_exists()
async def index(request: Request, user: User = Depends(check_user_exists)):
return lnticket_renderer().TemplateResponse("lnticket/index.html", {"request": request,"user": user.dict()})
return lnticket_renderer().TemplateResponse(
"lnticket/index.html", {"request": request, "user": user.dict()}
)
@lnticket_ext.get("/{form_id}")
@ -28,8 +31,7 @@ async def display(request: Request, form_id):
form = await get_form(form_id)
if not form:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="LNTicket does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="LNTicket does not exist."
)
# abort(HTTPStatus.NOT_FOUND, "LNTicket does not exist.")
@ -37,11 +39,13 @@ async def display(request: Request, form_id):
return lnticket_renderer().TemplateResponse(
"lnticket/display.html",
{"request": request,
"form_id":form.id,
"form_name":form.name,
"form_desc":form.description,
"form_amount":form.amount,
"form_flatrate":form.flatrate,
"form_wallet":wallet.inkey}
{
"request": request,
"form_id": form.id,
"form_name": form.name,
"form_desc": form.description,
"form_amount": form.amount,
"form_flatrate": form.flatrate,
"form_wallet": wallet.inkey,
},
)

View File

@ -34,7 +34,11 @@ from .crud import (
@lnticket_ext.get("/api/v1/forms")
async def api_forms_get(r: Request, all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_forms_get(
r: Request,
all_wallets: bool = Query(False),
wallet: WalletTypeInfo = Depends(get_key_type),
):
wallet_ids = [wallet.wallet.id]
if all_wallets:
@ -42,6 +46,7 @@ async def api_forms_get(r: Request, all_wallets: bool = Query(False), wallet: Wa
return [form.dict() for form in await get_forms(wallet_ids)]
@lnticket_ext.post("/api/v1/forms", status_code=HTTPStatus.CREATED)
@lnticket_ext.put("/api/v1/forms/{form_id}")
# @api_check_wallet_key("invoice")
@ -55,21 +60,21 @@ async def api_forms_get(r: Request, all_wallets: bool = Query(False), wallet: Wa
# "flatrate": {"type": "integer", "required": True},
# }
# )
async def api_form_create(data: CreateFormData, form_id=None, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_form_create(
data: CreateFormData, form_id=None, wallet: WalletTypeInfo = Depends(get_key_type)
):
if form_id:
form = await get_form(form_id)
if not form:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"Form does not exist."
status_code=HTTPStatus.NOT_FOUND, detail=f"Form does not exist."
)
# return {"message": "Form does not exist."}, HTTPStatus.NOT_FOUND
if form.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail=f"Not your form."
status_code=HTTPStatus.FORBIDDEN, detail=f"Not your form."
)
# return {"message": "Not your form."}, HTTPStatus.FORBIDDEN
@ -86,16 +91,12 @@ async def api_form_delete(form_id, wallet: WalletTypeInfo = Depends(get_key_type
if not form:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"Form does not exist."
status_code=HTTPStatus.NOT_FOUND, detail=f"Form does not exist."
)
# return {"message": "Form does not exist."}, HTTPStatus.NOT_FOUND
if form.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail=f"Not your form."
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail=f"Not your form.")
# return {"message": "Not your form."}, HTTPStatus.FORBIDDEN
await delete_form(form_id)
@ -109,7 +110,9 @@ async def api_form_delete(form_id, wallet: WalletTypeInfo = Depends(get_key_type
@lnticket_ext.get("/api/v1/tickets")
# @api_check_wallet_key("invoice")
async def api_tickets(all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_tickets(
all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type)
):
wallet_ids = [wallet.wallet.id]
if all_wallets:
@ -117,6 +120,7 @@ async def api_tickets(all_wallets: bool = Query(False), wallet: WalletTypeInfo =
return [form.dict() for form in await get_tickets(wallet_ids)]
@lnticket_ext.post("/api/v1/tickets/{form_id}", status_code=HTTPStatus.CREATED)
# @api_validate_post_request(
# schema={
@ -131,8 +135,7 @@ async def api_ticket_make_ticket(data: CreateTicketData, form_id):
form = await get_form(form_id)
if not form:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"LNTicket does not exist."
status_code=HTTPStatus.NOT_FOUND, detail=f"LNTicket does not exist."
)
# return {"message": "LNTicket does not exist."}, HTTPStatus.NOT_FOUND
@ -146,10 +149,7 @@ async def api_ticket_make_ticket(data: CreateTicketData, form_id):
extra={"tag": "lnticket"},
)
except Exception as e:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail=str(e)
)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
# return {"message": str(e)}, HTTPStatus.INTERNAL_SERVER_ERROR
ticket = await create_ticket(
@ -158,18 +158,14 @@ async def api_ticket_make_ticket(data: CreateTicketData, form_id):
if not ticket:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="LNTicket could not be fetched."
status_code=HTTPStatus.NOT_FOUND, detail="LNTicket could not be fetched."
)
# return (
# {"message": "LNTicket could not be fetched."},
# HTTPStatus.NOT_FOUND,
# )
return {
"payment_hash": payment_hash,
"payment_request": payment_request
}
return {"payment_hash": payment_hash, "payment_request": payment_request}
@lnticket_ext.get("/api/v1/tickets/{payment_hash}", status_code=HTTPStatus.OK)
@ -198,16 +194,12 @@ async def api_ticket_delete(ticket_id, wallet: WalletTypeInfo = Depends(get_key_
if not ticket:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"LNTicket does not exist."
status_code=HTTPStatus.NOT_FOUND, detail=f"LNTicket does not exist."
)
# return {"message": "Paywall does not exist."}, HTTPStatus.NOT_FOUND
if ticket.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Not your ticket."
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your ticket.")
# return {"message": "Not your ticket."}, HTTPStatus.FORBIDDEN
await delete_ticket(ticket_id)

View File

@ -26,11 +26,7 @@ lnurlp_ext: APIRouter = APIRouter(
def lnurlp_renderer():
return template_renderer(
[
"lnbits/extensions/lnurlp/templates",
]
)
return template_renderer(["lnbits/extensions/lnurlp/templates"])
from .views_api import * # noqa

View File

@ -5,10 +5,7 @@ from . import db
from .models import PayLink, CreatePayLinkData
async def create_pay_link(
data: CreatePayLinkData,
wallet_id: str
) -> PayLink:
async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
returning = "" if db.type == SQLITE else "RETURNING ID"
method = db.execute if db.type == SQLITE else db.fetchone

View File

@ -3,7 +3,11 @@ import math
from http import HTTPStatus
from fastapi import FastAPI, Request
from starlette.exceptions import HTTPException
from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore
from lnurl import (
LnurlPayResponse,
LnurlPayActionResponse,
LnurlErrorResponse,
) # type: ignore
from lnbits.core.services import create_invoice
from lnbits.utils.exchange_rates import get_fiat_rate_satoshis
@ -12,13 +16,16 @@ from . import lnurlp_ext
from .crud import increment_pay_link
@lnurlp_ext.get("/api/v1/lnurl/{link_id}", status_code=HTTPStatus.OK, name="lnurlp.api_lnurl_response")
@lnurlp_ext.get(
"/api/v1/lnurl/{link_id}",
status_code=HTTPStatus.OK,
name="lnurlp.api_lnurl_response",
)
async def api_lnurl_response(request: Request, link_id):
link = await increment_pay_link(link_id, served_meta=1)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Pay link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
)
rate = await get_fiat_rate_satoshis(link.currency) if link.currency else 1
@ -37,13 +44,16 @@ async def api_lnurl_response(request: Request, link_id):
return params
@lnurlp_ext.get("/api/v1/lnurl/cb/{link_id}", status_code=HTTPStatus.OK, name="lnurlp.api_lnurl_callback")
@lnurlp_ext.get(
"/api/v1/lnurl/cb/{link_id}",
status_code=HTTPStatus.OK,
name="lnurlp.api_lnurl_callback",
)
async def api_lnurl_callback(request: Request, link_id):
link = await increment_pay_link(link_id, served_pr=1)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Pay link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
)
min, max = link.min, link.max
rate = await get_fiat_rate_satoshis(link.currency) if link.currency else 1
@ -55,23 +65,22 @@ async def api_lnurl_callback(request: Request, link_id):
min = link.min * 1000
max = link.max * 1000
amount_received = int(request.query_params.get('amount') or 0)
amount_received = int(request.query_params.get("amount") or 0)
if amount_received < min:
return LnurlErrorResponse(
reason=f"Amount {amount_received} is smaller than minimum {min}."
).dict()
reason=f"Amount {amount_received} is smaller than minimum {min}."
).dict()
elif amount_received > max:
return LnurlErrorResponse(
reason=f"Amount {amount_received} is greater than maximum {max}."
).dict()
reason=f"Amount {amount_received} is greater than maximum {max}."
).dict()
comment = request.query_params.get("comment")
if len(comment or "") > link.comment_chars:
return LnurlErrorResponse(
reason=f"Got a comment with {len(comment)} characters, but can only accept {link.comment_chars}"
).dict()
reason=f"Got a comment with {len(comment)} characters, but can only accept {link.comment_chars}"
).dict()
payment_hash, payment_request = await create_invoice(
wallet_id=link.wallet,
@ -80,20 +89,20 @@ async def api_lnurl_callback(request: Request, link_id):
description_hash=hashlib.sha256(
link.lnurlpay_metadata.encode("utf-8")
).digest(),
extra={"tag": "lnurlp", "link": link.id, "comment": comment, 'extra': request.query_params.get('amount')},
extra={
"tag": "lnurlp",
"link": link.id,
"comment": comment,
"extra": request.query_params.get("amount"),
},
)
success_action = link.success_action(payment_hash)
if success_action:
resp = LnurlPayActionResponse(
pr=payment_request,
success_action=success_action,
routes=[],
pr=payment_request, success_action=success_action, routes=[]
)
else:
resp = LnurlPayActionResponse(
pr=payment_request,
routes=[],
)
resp = LnurlPayActionResponse(pr=payment_request, routes=[])
return resp.dict()

View File

@ -8,15 +8,17 @@ from lnurl.types import LnurlPayMetadata # type: ignore
from sqlite3 import Row
from pydantic import BaseModel
class CreatePayLinkData(BaseModel):
description: str
min: int = Query(0.01, ge=0.01)
max: int = Query(0.01, ge=0.01)
currency: str = Query(None)
comment_chars: int = Query(0, ge=0, lt=800)
webhook_url: str = Query(None)
success_text: str = Query(None)
success_url: str = Query(None)
description: str
min: int = Query(0.01, ge=0.01)
max: int = Query(0.01, ge=0.01)
currency: str = Query(None)
comment_chars: int = Query(0, ge=0, lt=800)
webhook_url: str = Query(None)
success_text: str = Query(None)
success_url: str = Query(None)
class PayLink(BaseModel):
id: int
@ -37,7 +39,6 @@ class PayLink(BaseModel):
data = dict(row)
return cls(**data)
def lnurl(self, req: Request) -> str:
url = req.url_for("lnurlp.api_lnurl_response", link_id=self.id)
return lnurl_encode(url)
@ -58,9 +59,6 @@ class PayLink(BaseModel):
"url": urlunparse(url),
}
elif self.success_text:
return {
"tag": "message",
"message": self.success_text,
}
return {"tag": "message", "message": self.success_text}
else:
return None

View File

@ -17,6 +17,7 @@ async def wait_for_paid_invoices():
payment = await invoice_queue.get()
await on_invoice_paid(payment)
async def on_invoice_paid(payment: Payment) -> None:
if "lnurlp" != payment.extra.get("tag"):
# not an lnurlp invoice

View File

@ -14,34 +14,35 @@ from lnbits.core.models import User
templates = Jinja2Templates(directory="templates")
@lnurlp_ext.get("/", response_class=HTMLResponse)
# @validate_uuids(["usr"], required=True)
# @check_user_exists()
async def index(request: Request, user: User = Depends(check_user_exists)):
return lnurlp_renderer().TemplateResponse("lnurlp/index.html", {"request": request, "user": user.dict()})
return lnurlp_renderer().TemplateResponse(
"lnurlp/index.html", {"request": request, "user": user.dict()}
)
@lnurlp_ext.get("/{link_id}", response_class=HTMLResponse)
async def display(request: Request,link_id):
async def display(request: Request, link_id):
link = await get_pay_link(link_id)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Pay link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
)
# abort(HTTPStatus.NOT_FOUND, "Pay link does not exist.")
ctx = {"request": request, "lnurl":link.lnurl(req=request)}
ctx = {"request": request, "lnurl": link.lnurl(req=request)}
return lnurlp_renderer().TemplateResponse("lnurlp/display.html", ctx)
@lnurlp_ext.get("/print/{link_id}", response_class=HTMLResponse)
async def print_qr(request: Request,link_id):
async def print_qr(request: Request, link_id):
link = await get_pay_link(link_id)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Pay link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
)
# abort(HTTPStatus.NOT_FOUND, "Pay link does not exist.")
ctx = {"request": request, "lnurl":link.lnurl(req=request)}
ctx = {"request": request, "lnurl": link.lnurl(req=request)}
return lnurlp_renderer().TemplateResponse("lnurlp/print_qr.html", ctx)

View File

@ -29,11 +29,7 @@ offlineshop_ext: APIRouter = APIRouter(
def offlineshop_renderer():
return template_renderer(
[
"lnbits/extensions/offlineshop/templates",
]
)
return template_renderer(["lnbits/extensions/offlineshop/templates"])
from .lnurl import * # noqa

View File

@ -51,12 +51,7 @@ async def set_method(shop: int, method: str, wordlist: str = "") -> Optional[Sho
async def add_item(
shop: int,
name: str,
description: str,
image: Optional[str],
price: int,
unit: str,
shop: int, name: str, description: str, image: Optional[str], price: int, unit: str
) -> int:
result = await db.execute(
"""

View File

@ -8,8 +8,8 @@ def hotp(key, counter, digits=6, digest="sha1"):
key = base64.b32decode(key.upper() + "=" * ((8 - len(key)) % 8))
counter = struct.pack(">Q", counter)
mac = hmac.new(key, counter, digest).digest()
offset = mac[-1] & 0x0f
binary = struct.unpack(">L", mac[offset : offset + 4])[0] & 0x7fffffff
offset = mac[-1] & 0x0F
binary = struct.unpack(">L", mac[offset : offset + 4])[0] & 0x7FFFFFFF
return str(binary)[-digits:].zfill(digits)

View File

@ -4,7 +4,11 @@ from fastapi.params import Query
from starlette.requests import Request
from lnbits.helpers import url_for
from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore
from lnurl import (
LnurlPayResponse,
LnurlPayActionResponse,
LnurlErrorResponse,
) # type: ignore
from lnbits.core.services import create_invoice
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
@ -15,7 +19,7 @@ from .crud import get_shop, get_item
@offlineshop_ext.get("/lnurl/{item_id}", name="offlineshop.lnurl_response")
async def lnurl_response(req: Request, item_id: int = Query(...)):
item = await get_item(item_id) # type: Item
item = await get_item(item_id) # type: Item
if not item:
return {"status": "ERROR", "reason": "Item not found."}
@ -40,7 +44,7 @@ async def lnurl_response(req: Request, item_id: int = Query(...)):
@offlineshop_ext.get("/lnurl/cb/{item_id}", name="offlineshop.lnurl_callback")
async def lnurl_callback(request: Request, item_id: int):
item = await get_item(item_id) # type: Item
item = await get_item(item_id) # type: Item
if not item:
return {"status": "ERROR", "reason": "Couldn't find item."}
@ -80,7 +84,9 @@ async def lnurl_callback(request: Request, item_id: int):
resp = LnurlPayActionResponse(
pr=payment_request,
success_action=item.success_action(shop, payment_hash, request) if shop.method else None,
success_action=item.success_action(shop, payment_hash, request)
if shop.method
else None,
routes=[],
)

View File

@ -14,7 +14,7 @@ from .helpers import totp
shop_counters: Dict = {}
class ShopCounter():
class ShopCounter:
wordlist: List[str]
fulfilled_payments: OrderedDict
counter: int
@ -66,7 +66,7 @@ class Shop(BaseModel):
def otp_key(self) -> str:
return base64.b32encode(
hashlib.sha256(
("otpkey" + str(self.id) + self.wallet).encode("ascii"),
("otpkey" + str(self.id) + self.wallet).encode("ascii")
).digest()
).decode("ascii")
@ -90,9 +90,7 @@ class Item(BaseModel):
unit: str
def lnurl(self, req: Request) -> str:
return lnurl_encode(
req.url_for("offlineshop.lnurl_response", item_id=self.id)
)
return lnurl_encode(req.url_for("offlineshop.lnurl_response", item_id=self.id))
def values(self, req: Request):
values = self.dict()
@ -116,8 +114,6 @@ class Item(BaseModel):
return None
return UrlAction(
url=req.url_for(
"offlineshop.confirmation_code", p=payment_hash
),
url=req.url_for("offlineshop.confirmation_code", p=payment_hash),
description="Open to get the confirmation code for your purchase.",
)

View File

@ -18,14 +18,16 @@ from fastapi import Request, HTTPException
@offlineshop_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
return offlineshop_renderer().TemplateResponse("offlineshop/index.html", {"request": request, "user": user.dict()})
return offlineshop_renderer().TemplateResponse(
"offlineshop/index.html", {"request": request, "user": user.dict()}
)
@offlineshop_ext.get("/print", response_class=HTMLResponse)
async def print_qr_codes(request: Request, items: List[int] = None):
items = []
for item_id in request.query_params.get("items").split(","):
item = await get_item(item_id) # type: Item
item = await get_item(item_id) # type: Item
if item:
items.append(
{
@ -35,11 +37,16 @@ async def print_qr_codes(request: Request, items: List[int] = None):
}
)
return offlineshop_renderer().TemplateResponse("offlineshop/print.html", {"request": request,"items":items})
return offlineshop_renderer().TemplateResponse(
"offlineshop/print.html", {"request": request, "items": items}
)
@offlineshop_ext.get("/confirmation/{p}", name="offlineshop.confirmation_code",
response_class=HTMLResponse)
@offlineshop_ext.get(
"/confirmation/{p}",
name="offlineshop.confirmation_code",
response_class=HTMLResponse,
)
async def confirmation_code(p: str = Query(...)):
style = "<style>* { font-size: 100px}</style>"
@ -48,20 +55,20 @@ async def confirmation_code(p: str = Query(...)):
if not payment:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"Couldn't find the payment {payment_hash}." + style
detail=f"Couldn't find the payment {payment_hash}." + style,
)
if payment.pending:
raise HTTPException(
status_code=HTTPStatus.PAYMENT_REQUIRED,
detail=f"Payment {payment_hash} wasn't received yet. Please try again in a minute." + style
detail=f"Payment {payment_hash} wasn't received yet. Please try again in a minute."
+ style,
)
if payment.time + 60 * 15 < time.time():
raise HTTPException(
status_code=HTTPStatus.REQUEST_TIMEOUT,
detail="Too much time has passed." + style
detail="Too much time has passed." + style,
)
item = await get_item(payment.extra.get("item"))
shop = await get_shop(item.shop)

View File

@ -33,17 +33,16 @@ async def api_list_currencies_available():
@offlineshop_ext.get("/api/v1/offlineshop")
# @api_check_wallet_key("invoice")
async def api_shop_from_wallet(r: Request, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_shop_from_wallet(
r: Request, wallet: WalletTypeInfo = Depends(get_key_type)
):
shop = await get_or_create_shop_by_wallet(wallet.wallet.id)
items = await get_items(shop.id)
try:
return {
**shop.dict(),
**{
"otp_key": shop.otp_key,
"items": [item.values(r) for item in items],
},
**{"otp_key": shop.otp_key, "items": [item.values(r) for item in items]},
}
except LnurlInvalidUrl:
raise HTTPException(
@ -63,18 +62,15 @@ class CreateItemsData(BaseModel):
@offlineshop_ext.post("/api/v1/offlineshop/items")
@offlineshop_ext.put("/api/v1/offlineshop/items/{item_id}")
# @api_check_wallet_key("invoice")
async def api_add_or_update_item(data: CreateItemsData, item_id=None, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_add_or_update_item(
data: CreateItemsData, item_id=None, wallet: WalletTypeInfo = Depends(get_key_type)
):
shop = await get_or_create_shop_by_wallet(wallet.wallet.id)
if item_id == None:
await add_item(
shop.id,
data.name,
data.description,
data.image,
data.price,
data.unit,
shop.id, data.name, data.description, data.image, data.price, data.unit
)
return HTMLResponse(status_code=HTTPStatus.CREATED)
return HTMLResponse(status_code=HTTPStatus.CREATED)
else:
await update_item(
shop.id,
@ -102,7 +98,9 @@ class CreateMethodData(BaseModel):
@offlineshop_ext.put("/api/v1/offlineshop/method")
# @api_check_wallet_key("invoice")
async def api_set_method(data: CreateMethodData, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_set_method(
data: CreateMethodData, wallet: WalletTypeInfo = Depends(get_key_type)
):
method = data.method
wordlist = data.wordlist.split("\n") if data.wordlist else None

View File

@ -12,11 +12,7 @@ satsdice_ext: APIRouter = APIRouter(prefix="/satsdice", tags=["satsdice"])
def satsdice_renderer():
return template_renderer(
[
"lnbits/extensions/satsdice/templates",
]
)
return template_renderer(["lnbits/extensions/satsdice/templates"])
from .views_api import * # noqa

View File

@ -15,9 +15,7 @@ from .models import (
from lnbits.helpers import urlsafe_short_hash
async def create_satsdice_pay(
data: CreateSatsDiceLink,
) -> satsdiceLink:
async def create_satsdice_pay(data: CreateSatsDiceLink,) -> satsdiceLink:
satsdice_id = urlsafe_short_hash()
await db.execute(
"""
@ -124,13 +122,7 @@ async def create_satsdice_payment(data: CreateSatsDicePayment) -> satsdicePaymen
)
VALUES (?, ?, ?, ?, ?)
""",
(
data.payment_hash,
data.satsdice_pay,
data.value,
False,
False,
),
(data.payment_hash, data.satsdice_pay, data.value, False, False),
)
payment = await get_satsdice_payment(payment_hash)
assert payment, "Newly created withdraw couldn't be retrieved"
@ -211,8 +203,7 @@ async def get_satsdice_withdraw_by_hash(
unique_hash: str, num=0
) -> Optional[satsdiceWithdraw]:
row = await db.fetchone(
"SELECT * FROM satsdice.satsdice_withdraw WHERE unique_hash = ?",
(unique_hash,),
"SELECT * FROM satsdice.satsdice_withdraw WHERE unique_hash = ?", (unique_hash,)
)
if not row:
return None
@ -259,10 +250,7 @@ async def delete_satsdice_withdraw(withdraw_id: str) -> None:
)
async def create_withdraw_hash_check(
the_hash: str,
lnurl_id: str,
) -> HashCheck:
async def create_withdraw_hash_check(the_hash: str, lnurl_id: str) -> HashCheck:
await db.execute(
"""
INSERT INTO satsdice.hash_checkw (
@ -271,10 +259,7 @@ async def create_withdraw_hash_check(
)
VALUES (?, ?)
""",
(
the_hash,
lnurl_id,
),
(the_hash, lnurl_id),
)
hashCheck = await get_withdraw_hash_checkw(the_hash, lnurl_id)
return hashCheck

View File

@ -19,7 +19,11 @@ from .crud import (
get_satsdice_pay,
create_satsdice_payment,
)
from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore
from lnurl import (
LnurlPayResponse,
LnurlPayActionResponse,
LnurlErrorResponse,
) # type: ignore
##############LNURLP STUFF
@ -30,8 +34,7 @@ async def api_lnurlp_response(req: Request, link_id: str = Query(None)):
link = await get_satsdice_pay(link_id)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="LNURL-pay not found.",
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-pay not found."
)
resp = LnurlPayResponse(
callback=req.url_for(
@ -51,8 +54,7 @@ async def api_lnurlp_callback(link_id: str = Query(None), amount: str = Query(No
link = await get_satsdice_pay(link_id)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="LNURL-pay not found.",
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-pay not found."
)
min, max = link.min_bet, link.max_bet
@ -87,15 +89,10 @@ async def api_lnurlp_callback(link_id: str = Query(None), amount: str = Query(No
link = await create_satsdice_payment(data)
if success_action:
resp = LnurlPayActionResponse(
pr=payment_request,
success_action=success_action,
routes=[],
pr=payment_request, success_action=success_action, routes=[]
)
else:
resp = LnurlPayActionResponse(
pr=payment_request,
routes=[],
)
resp = LnurlPayActionResponse(pr=payment_request, routes=[])
return resp.dict()
@ -109,15 +106,11 @@ async def api_lnurlw_response(unique_hash: str = Query(None)):
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="LNURL-satsdice not found.",
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-satsdice not found."
)
if link.used:
raise HTTPException(
status_code=HTTPStatus.OK,
detail="satsdice is spent.",
)
raise HTTPException(status_code=HTTPStatus.OK, detail="satsdice is spent.")
return link.lnurl_response.dict()
@ -136,21 +129,14 @@ async def api_lnurlw_callback(
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="LNURL-satsdice not found.",
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-satsdice not found."
)
if link.used:
raise HTTPException(
status_code=HTTPStatus.OK,
detail="satsdice is spent.",
)
raise HTTPException(status_code=HTTPStatus.OK, detail="satsdice is spent.")
if link.k1 != k1:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Bad request..",
)
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Bad request..")
if now < link.open_time:
return {"status": "ERROR", "reason": f"Wait {link.open_time - now} seconds."}

View File

@ -51,11 +51,7 @@ class satsdiceLink(BaseModel):
# qs: Dict = parse_qs(url.query)
# qs["payment_hash"] = payment_hash
# url = url._replace(query=urlencode(qs, doseq=True))
return {
"tag": "url",
"description": "Check the attached link",
"url": url,
}
return {"tag": "url", "description": "Check the attached link", "url": url}
class satsdicePayment(BaseModel):
@ -78,9 +74,7 @@ class satsdiceWithdraw(BaseModel):
def lnurl(self, req: Request) -> Lnurl:
return lnurl_encode(
req.url_for(
"satsdice.lnurlw_response",
unique_hash=self.unique_hash,
_external=True,
"satsdice.lnurlw_response", unique_hash=self.unique_hash, _external=True
)
)
@ -91,9 +85,7 @@ class satsdiceWithdraw(BaseModel):
@property
def lnurl_response(self, req: Request) -> LnurlWithdrawResponse:
url = req.url_for(
"satsdice.api_lnurlw_callback",
unique_hash=self.unique_hash,
_external=True,
"satsdice.api_lnurlw_callback", unique_hash=self.unique_hash, _external=True
)
return LnurlWithdrawResponse(
callback=url,

View File

@ -15,9 +15,7 @@ from lnbits.core.crud import (
delete_expired_invoices,
get_balance_checks,
)
from lnbits.core.services import (
check_invoice_status,
)
from lnbits.core.services import check_invoice_status
from fastapi import FastAPI, Request
from fastapi.params import Depends
from fastapi.templating import Jinja2Templates

View File

@ -67,14 +67,12 @@ async def api_link_retrieve(
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Pay link does not exist.",
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
)
if link.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Not your pay link.",
status_code=HTTPStatus.FORBIDDEN, detail="Not your pay link."
)
return {**link._asdict(), **{"lnurl": link.lnurl}}
@ -88,17 +86,13 @@ async def api_link_create_or_update(
link_id: str = Query(None),
):
if data.min_bet > data.max_bet:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Bad request",
)
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Bad request")
if link_id:
link = await get_satsdice_pay(link_id)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Satsdice does not exist",
status_code=HTTPStatus.NOT_FOUND, detail="Satsdice does not exist"
)
if link.wallet != wallet.wallet.id:
@ -123,14 +117,12 @@ async def api_link_delete(
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Pay link does not exist.",
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
)
if link.wallet != g.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Not your pay link.",
status_code=HTTPStatus.FORBIDDEN, detail="Not your pay link."
)
await delete_satsdice_pay(link_id)
@ -153,10 +145,7 @@ async def api_withdraws(
return (
jsonify(
[
{
**withdraw._asdict(),
**{"lnurl": withdraw.lnurl},
}
{**withdraw._asdict(), **{"lnurl": withdraw.lnurl}}
for withdraw in await get_satsdice_withdraws(wallet_ids)
]
),
@ -177,14 +166,12 @@ async def api_withdraw_retrieve(
if not withdraw:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="satsdice withdraw does not exist.",
status_code=HTTPStatus.NOT_FOUND, detail="satsdice withdraw does not exist."
)
if withdraw.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Not your satsdice withdraw.",
status_code=HTTPStatus.FORBIDDEN, detail="Not your satsdice withdraw."
)
return {**withdraw._asdict(), **{"lnurl": withdraw.lnurl}}, HTTPStatus.OK
@ -220,8 +207,7 @@ async def api_withdraw_create_or_update(
)
if withdraw.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Not your satsdice withdraw.",
status_code=HTTPStatus.FORBIDDEN, detail="Not your satsdice withdraw."
)
withdraw = await update_satsdice_withdraw(
@ -245,14 +231,12 @@ async def api_withdraw_delete(
if not withdraw:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="satsdice withdraw does not exist.",
status_code=HTTPStatus.NOT_FOUND, detail="satsdice withdraw does not exist."
)
if withdraw.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Not your satsdice withdraw.",
status_code=HTTPStatus.FORBIDDEN, detail="Not your satsdice withdraw."
)
await delete_satsdice_withdraw(withdraw_id)

View File

@ -8,17 +8,11 @@ from lnbits.helpers import template_renderer
db = Database("ext_satspay")
satspay_ext: APIRouter = APIRouter(
prefix="/satspay",
tags=["satspay"]
)
satspay_ext: APIRouter = APIRouter(prefix="/satspay", tags=["satspay"])
def satspay_renderer():
return template_renderer(
[
"lnbits/extensions/satspay/templates",
]
)
return template_renderer(["lnbits/extensions/satspay/templates"])
from .views_api import * # noqa

View File

@ -14,10 +14,7 @@ from ..watchonly.crud import get_watch_wallet, get_fresh_address, get_mempool
###############CHARGES##########################
async def create_charge(
user: str,
data: CreateCharge
) -> Charges:
async def create_charge(user: str, data: CreateCharge) -> Charges:
charge_id = urlsafe_short_hash()
if data.onchainwallet:
wallet = await get_watch_wallet(data.onchainwallet)

View File

@ -4,6 +4,7 @@ from fastapi.param_functions import Query
from pydantic import BaseModel
import time
class CreateCharge(BaseModel):
onchainwallet: str = Query(None)
lnbitswallet: str = Query(None)
@ -14,6 +15,7 @@ class CreateCharge(BaseModel):
time: int = Query(..., ge=1)
amount: int = Query(..., ge=1)
class Charges(BaseModel):
id: str
user: str

View File

@ -14,9 +14,12 @@ from .crud import get_charge
templates = Jinja2Templates(directory="templates")
@satspay_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
return satspay_renderer().TemplateResponse("satspay/index.html", {"request": request,"user": user.dict()})
return satspay_renderer().TemplateResponse(
"satspay/index.html", {"request": request, "user": user.dict()}
)
@satspay_ext.get("/{charge_id}", response_class=HTMLResponse)
@ -24,7 +27,8 @@ async def display(request: Request, charge_id):
charge = await get_charge(charge_id)
if not charge:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Charge link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Charge link does not exist."
)
return satspay_renderer().TemplateResponse("satspay/display.html", {"request": request, "charge": charge})
return satspay_renderer().TemplateResponse(
"satspay/display.html", {"request": request, "charge": charge}
)

View File

@ -30,8 +30,9 @@ from .crud import (
@satspay_ext.post("/api/v1/charge")
@satspay_ext.put("/api/v1/charge/{charge_id}")
async def api_charge_create_or_update(data: CreateCharge, wallet: WalletTypeInfo = Depends(get_key_type), charge_id=None):
async def api_charge_create_or_update(
data: CreateCharge, wallet: WalletTypeInfo = Depends(get_key_type), charge_id=None
):
if not charge_id:
charge = await create_charge(user=wallet.wallet.user, data=data)
return charge.dict()
@ -44,32 +45,33 @@ async def api_charge_create_or_update(data: CreateCharge, wallet: WalletTypeInfo
async def api_charges_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
try:
return [
{
**charge.dict(),
**{"time_elapsed": charge.time_elapsed},
**{"paid": charge.paid},
}
for charge in await get_charges(wallet.wallet.user)
]
{
**charge.dict(),
**{"time_elapsed": charge.time_elapsed},
**{"paid": charge.paid},
}
for charge in await get_charges(wallet.wallet.user)
]
except:
return ""
@satspay_ext.get("/api/v1/charge/{charge_id}")
async def api_charge_retrieve(charge_id, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_charge_retrieve(
charge_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
charge = await get_charge(charge_id)
if not charge:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Charge does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Charge does not exist."
)
return {
**charge.dict(),
**{"time_elapsed": charge.time_elapsed},
**{"paid": charge.paid},
}
**charge.dict(),
**{"time_elapsed": charge.time_elapsed},
**{"paid": charge.paid},
}
@satspay_ext.delete("/api/v1/charge/{charge_id}")
@ -78,8 +80,7 @@ async def api_charge_delete(charge_id, wallet: WalletTypeInfo = Depends(get_key_
if not charge:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Charge does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Charge does not exist."
)
await delete_charge(charge_id)
@ -96,8 +97,7 @@ async def api_charges_balance(charge_id):
if not charge:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Charge does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Charge does not exist."
)
if charge.paid and charge.webhook:
@ -129,7 +129,9 @@ async def api_charges_balance(charge_id):
@satspay_ext.put("/api/v1/mempool")
async def api_update_mempool(endpoint: str = Query(...), wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_update_mempool(
endpoint: str = Query(...), wallet: WalletTypeInfo = Depends(get_key_type)
):
mempool = await update_mempool(endpoint, user=wallet.wallet.user)
return mempool.dict()

View File

@ -14,12 +14,9 @@ tpos_ext: APIRouter = APIRouter(
# "tpos", __name__, static_folder="static", template_folder="templates"
)
def tpos_renderer():
return template_renderer(
[
"lnbits/extensions/tpos/templates",
]
)
return template_renderer(["lnbits/extensions/tpos/templates"])
from .views_api import * # noqa

View File

@ -7,6 +7,7 @@ class CreateTposData(BaseModel):
name: str
currency: str
class TPoS(BaseModel):
id: str
wallet: str

View File

@ -13,11 +13,14 @@ from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates")
@tpos_ext.get("/", response_class=HTMLResponse)
# @validate_uuids(["usr"], required=True)
# @check_user_exists()
async def index(request: Request, user: User = Depends(check_user_exists)):
return tpos_renderer().TemplateResponse("tpos/index.html", {"request": request,"user": user.dict()})
return tpos_renderer().TemplateResponse(
"tpos/index.html", {"request": request, "user": user.dict()}
)
@tpos_ext.get("/{tpos_id}")
@ -25,9 +28,10 @@ async def tpos(request: Request, tpos_id):
tpos = await get_tpos(tpos_id)
if not tpos:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="TPoS does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
)
# abort(HTTPStatus.NOT_FOUND, "TPoS does not exist.")
return tpos_renderer().TemplateResponse("tpos/tpos.html", {"request": request, "tpos": tpos})
return tpos_renderer().TemplateResponse(
"tpos/tpos.html", {"request": request, "tpos": tpos}
)

View File

@ -19,18 +19,19 @@ from .models import TPoS, CreateTposData
@tpos_ext.get("/api/v1/tposs", status_code=HTTPStatus.OK)
async def api_tposs(
all_wallets: bool = Query(None),
wallet: WalletTypeInfo = Depends(get_key_type)
):
all_wallets: bool = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)
):
wallet_ids = [wallet.wallet.id]
if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
return [tpos.dict() for tpos in await get_tposs(wallet_ids)]
@tpos_ext.post("/api/v1/tposs", status_code=HTTPStatus.CREATED)
async def api_tpos_create(data: CreateTposData, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_tpos_create(
data: CreateTposData, wallet: WalletTypeInfo = Depends(get_key_type)
):
tpos = await create_tpos(wallet_id=wallet.wallet.id, data=data)
return tpos.dict()
@ -41,30 +42,26 @@ async def api_tpos_delete(tpos_id: str, wallet: WalletTypeInfo = Depends(get_key
if not tpos:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="TPoS does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
)
# return {"message": "TPoS does not exist."}, HTTPStatus.NOT_FOUND
if tpos.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Not your TPoS."
)
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your TPoS.")
# return {"message": "Not your TPoS."}, HTTPStatus.FORBIDDEN
await delete_tpos(tpos_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
# return "", HTTPStatus.NO_CONTENT
@tpos_ext.post("/api/v1/tposs/{tpos_id}/invoices", status_code=HTTPStatus.CREATED)
async def api_tpos_create_invoice(amount: int = Query(..., ge=1), tpos_id: str = None):
tpos = await get_tpos(tpos_id)
if not tpos:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="TPoS does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
)
# return {"message": "TPoS does not exist."}, HTTPStatus.NOT_FOUND
@ -76,22 +73,20 @@ async def api_tpos_create_invoice(amount: int = Query(..., ge=1), tpos_id: str =
extra={"tag": "tpos"},
)
except Exception as e:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail=str(e)
)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
# return {"message": str(e)}, HTTPStatus.INTERNAL_SERVER_ERROR
return {"payment_hash": payment_hash, "payment_request": payment_request}
@tpos_ext.get("/api/v1/tposs/{tpos_id}/invoices/{payment_hash}", status_code=HTTPStatus.OK)
@tpos_ext.get(
"/api/v1/tposs/{tpos_id}/invoices/{payment_hash}", status_code=HTTPStatus.OK
)
async def api_tpos_check_invoice(tpos_id: str, payment_hash: str):
tpos = await get_tpos(tpos_id)
if not tpos:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="TPoS does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist."
)
# return {"message": "TPoS does not exist."}, HTTPStatus.NOT_FOUND

View File

@ -10,15 +10,12 @@ db = Database("ext_usermanager")
usermanager_ext: APIRouter = APIRouter(
prefix="/usermanager",
tags=["usermanager"]
#"usermanager", __name__, static_folder="static", template_folder="templates"
# "usermanager", __name__, static_folder="static", template_folder="templates"
)
def usermanager_renderer():
return template_renderer(
[
"lnbits/extensions/usermanager/templates",
]
)
return template_renderer(["lnbits/extensions/usermanager/templates"])
from .views_api import * # noqa

View File

@ -16,9 +16,7 @@ from .models import Users, Wallets, CreateUserData
### Users
async def create_usermanager_user(
data: CreateUserData
) -> Users:
async def create_usermanager_user(data: CreateUserData) -> Users:
account = await create_account()
user = await get_user(account.id)
assert user, "Newly created user couldn't be retrieved"
@ -38,7 +36,14 @@ async def create_usermanager_user(
INSERT INTO usermanager.wallets (id, admin, name, "user", adminkey, inkey)
VALUES (?, ?, ?, ?, ?, ?)
""",
(wallet.id, data.admin_id, data.wallet_name, user.id, wallet.adminkey, wallet.inkey),
(
wallet.id,
data.admin_id,
data.wallet_name,
user.id,
wallet.adminkey,
wallet.inkey,
),
)
user_created = await get_usermanager_user(user.id)
@ -55,7 +60,7 @@ async def get_usermanager_users(user_id: str) -> List[Users]:
rows = await db.fetchall(
"SELECT * FROM usermanager.users WHERE admin = ?", (user_id,)
)
return [Users(**row) for row in rows]

View File

@ -2,6 +2,7 @@ from pydantic import BaseModel
from fastapi.param_functions import Query
from sqlite3 import Row
class CreateUserData(BaseModel):
user_name: str = Query(...)
wallet_name: str = Query(...)

View File

@ -8,6 +8,9 @@ from lnbits.decorators import check_user_exists
from . import usermanager_ext, usermanager_renderer
@usermanager_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
return usermanager_renderer().TemplateResponse("usermanager/index.html", {"request": request,"user": user.dict()})
return usermanager_renderer().TemplateResponse(
"usermanager/index.html", {"request": request, "user": user.dict()}
)

View File

@ -49,20 +49,25 @@ async def api_usermanager_user(user_id, wallet: WalletTypeInfo = Depends(get_key
# "password": {"type": "string", "required": False},
# }
# )
async def api_usermanager_users_create(data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_usermanager_users_create(
data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type)
):
user = await create_usermanager_user(data)
full = user.dict()
full["wallets"] = [wallet.dict() for wallet in await get_usermanager_users_wallets(user.id)]
full["wallets"] = [
wallet.dict() for wallet in await get_usermanager_users_wallets(user.id)
]
return full
@usermanager_ext.delete("/api/v1/users/{user_id}")
async def api_usermanager_users_delete(user_id, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_usermanager_users_delete(
user_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
user = await get_usermanager_user(user_id)
if not user:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="User does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
)
await delete_usermanager_user(user_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
@ -72,16 +77,15 @@ async def api_usermanager_users_delete(user_id, wallet: WalletTypeInfo = Depends
@usermanager_ext.post("/api/v1/extensions")
async def api_usermanager_activate_extension(extension: str = Query(...), userid: str = Query(...), active: bool = Query(...)):
async def api_usermanager_activate_extension(
extension: str = Query(...), userid: str = Query(...), active: bool = Query(...)
):
user = await get_user(userid)
if not user:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="User does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
)
update_user_extension(
user_id=userid, extension=extension, active=active
)
update_user_extension(user_id=userid, extension=extension, active=active)
return {"extension": "updated"}
@ -93,11 +97,9 @@ async def api_usermanager_wallets_create(
wallet: WalletTypeInfo = Depends(get_key_type),
user_id: str = Query(...),
wallet_name: str = Query(...),
admin_id: str = Query(...)
admin_id: str = Query(...),
):
user = await create_usermanager_wallet(
user_id, wallet_name, admin_id
)
user = await create_usermanager_wallet(user_id, wallet_name, admin_id)
return user.dict()
@ -108,23 +110,30 @@ async def api_usermanager_wallets(wallet: WalletTypeInfo = Depends(get_key_type)
@usermanager_ext.get("/api/v1/wallets/{wallet_id}")
async def api_usermanager_wallet_transactions(wallet_id, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_usermanager_wallet_transactions(
wallet_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
return await get_usermanager_wallet_transactions(wallet_id)
@usermanager_ext.get("/api/v1/wallets/{user_id}")
async def api_usermanager_users_wallets(user_id, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_usermanager_users_wallets(
user_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
# wallet = await get_usermanager_users_wallets(user_id)
return [s_wallet.dict() for s_wallet in await get_usermanager_users_wallets(user_id)]
return [
s_wallet.dict() for s_wallet in await get_usermanager_users_wallets(user_id)
]
@usermanager_ext.delete("/api/v1/wallets/{wallet_id}")
async def api_usermanager_wallets_delete(wallet_id, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_usermanager_wallets_delete(
wallet_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
get_wallet = await get_usermanager_wallet(wallet_id)
if not get_wallet:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Wallet does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
)
await delete_usermanager_wallet(wallet_id, get_wallet.user)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)

View File

@ -8,17 +8,11 @@ from lnbits.helpers import template_renderer
db = Database("ext_watchonly")
watchonly_ext: APIRouter = APIRouter(
prefix="/watchonly",
tags=["watchonly"]
)
watchonly_ext: APIRouter = APIRouter(prefix="/watchonly", tags=["watchonly"])
def watchonly_renderer():
return template_renderer(
[
"lnbits/extensions/watchonly/templates",
]
)
return template_renderer(["lnbits/extensions/watchonly/templates"])
from .views_api import * # noqa

View File

@ -2,10 +2,12 @@ from sqlite3 import Row
from fastapi.param_functions import Query
from pydantic import BaseModel
class CreateWallet(BaseModel):
masterpub: str = Query("")
title: str = Query("")
class Wallets(BaseModel):
id: str
user: str

View File

@ -8,6 +8,7 @@ from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from . import watchonly_ext, watchonly_renderer
# from .crud import get_payment
from fastapi.templating import Jinja2Templates
@ -17,7 +18,9 @@ templates = Jinja2Templates(directory="templates")
@watchonly_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
return watchonly_renderer().TemplateResponse("watchonly/index.html", {"request": request,"user": user.dict()})
return watchonly_renderer().TemplateResponse(
"watchonly/index.html", {"request": request, "user": user.dict()}
)
# @watchonly_ext.get("/{charge_id}", response_class=HTMLResponse)

View File

@ -38,29 +38,29 @@ async def api_wallets_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
@watchonly_ext.get("/api/v1/wallet/{wallet_id}")
async def api_wallet_retrieve(wallet_id, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_wallet_retrieve(
wallet_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
w_wallet = await get_watch_wallet(wallet_id)
if not w_wallet:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Wallet does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
)
return w_wallet.dict()
@watchonly_ext.post("/api/v1/wallet")
async def api_wallet_create_or_update(data: CreateWallet, wallet_id=None, w: WalletTypeInfo = Depends(get_key_type)):
async def api_wallet_create_or_update(
data: CreateWallet, wallet_id=None, w: WalletTypeInfo = Depends(get_key_type)
):
try:
wallet = await create_watch_wallet(
user=w.wallet.user, masterpub=data.masterpub, title=data.title
)
except Exception as e:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=str(e)
)
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
mempool = await get_mempool(w.wallet.user)
if not mempool:
@ -74,8 +74,7 @@ async def api_wallet_delete(wallet_id, w: WalletTypeInfo = Depends(get_key_type)
if not wallet:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Wallet does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
)
await delete_watch_wallet(wallet_id)
@ -96,14 +95,12 @@ async def api_fresh_address(wallet_id, w: WalletTypeInfo = Depends(get_key_type)
@watchonly_ext.get("/api/v1/addresses/{wallet_id}")
async def api_get_addresses(wallet_id, w: WalletTypeInfo = Depends(get_key_type)):
wallet = await get_watch_wallet(wallet_id)
if not wallet:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Wallet does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Wallet does not exist."
)
addresses = await get_addresses(wallet_id)
@ -119,7 +116,9 @@ async def api_get_addresses(wallet_id, w: WalletTypeInfo = Depends(get_key_type)
@watchonly_ext.put("/api/v1/mempool")
async def api_update_mempool(endpoint: str = Query(...), w: WalletTypeInfo = Depends(get_key_type)):
async def api_update_mempool(
endpoint: str = Query(...), w: WalletTypeInfo = Depends(get_key_type)
):
mempool = await update_mempool(endpoint, user=w.wallet.user)
return mempool.dict()

View File

@ -21,12 +21,9 @@ withdraw_ext: APIRouter = APIRouter(
# "withdraw", __name__, static_folder="static", template_folder="templates"
)
def withdraw_renderer():
return template_renderer(
[
"lnbits/extensions/withdraw/templates",
]
)
return template_renderer(["lnbits/extensions/withdraw/templates"])
from .views_api import * # noqa

View File

@ -7,9 +7,7 @@ from .models import WithdrawLink, HashCheck, CreateWithdrawData
async def create_withdraw_link(
data: CreateWithdrawData,
wallet_id: str,
usescsv: str,
data: CreateWithdrawData, wallet_id: str, usescsv: str
) -> WithdrawLink:
link_id = urlsafe_short_hash()
await db.execute(
@ -115,10 +113,7 @@ def chunks(lst, n):
yield lst[i : i + n]
async def create_hash_check(
the_hash: str,
lnurl_id: str,
) -> HashCheck:
async def create_hash_check(the_hash: str, lnurl_id: str) -> HashCheck:
await db.execute(
"""
INSERT INTO withdraw.hash_check (
@ -127,10 +122,7 @@ async def create_hash_check(
)
VALUES (?, ?)
""",
(
the_hash,
lnurl_id,
),
(the_hash, lnurl_id),
)
hashCheck = await get_hash_check(the_hash, lnurl_id)
return hashCheck

View File

@ -14,14 +14,17 @@ from .crud import get_withdraw_link_by_hash, update_withdraw_link
# FOR LNURLs WHICH ARE NOT UNIQUE
@withdraw_ext.get("/api/v1/lnurl/{unique_hash}", status_code=HTTPStatus.OK, name="withdraw.api_lnurl_response")
@withdraw_ext.get(
"/api/v1/lnurl/{unique_hash}",
status_code=HTTPStatus.OK,
name="withdraw.api_lnurl_response",
)
async def api_lnurl_response(request: Request, unique_hash):
link = await get_withdraw_link_by_hash(unique_hash)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Withdraw link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
)
# return ({"status": "ERROR", "reason": "LNURL-withdraw not found."},
# HTTPStatus.OK,
@ -39,23 +42,22 @@ async def api_lnurl_response(request: Request, unique_hash):
return link.lnurl_response(request).dict()
# CALLBACK
@withdraw_ext.get("/api/v1/lnurl/cb/{unique_hash}", name="withdraw.api_lnurl_callback")
async def api_lnurl_callback(request: Request,
unique_hash: str=Query(...),
k1: str = Query(...),
payment_request: str = Query(..., alias="pr")
):
async def api_lnurl_callback(
request: Request,
unique_hash: str = Query(...),
k1: str = Query(...),
payment_request: str = Query(..., alias="pr"),
):
link = await get_withdraw_link_by_hash(unique_hash)
now = int(datetime.now().timestamp())
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="LNURL-withdraw not found."
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found."
)
# return (
# {"status": "ERROR", "reason": "LNURL-withdraw not found."},
@ -73,10 +75,7 @@ async def api_lnurl_callback(request: Request,
# )
if link.k1 != k1:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Bad request."
)
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Bad request.")
# return {"status": "ERROR", "reason": "Bad request."}, HTTPStatus.OK
if now < link.open_time:
@ -123,17 +122,21 @@ async def api_lnurl_callback(request: Request,
return {"status": "OK"}
# FOR LNURLs WHICH ARE UNIQUE
@withdraw_ext.get("/api/v1/lnurl/{unique_hash}/{id_unique_hash}", status_code=HTTPStatus.OK, name="withdraw.api_lnurl_multi_response")
@withdraw_ext.get(
"/api/v1/lnurl/{unique_hash}/{id_unique_hash}",
status_code=HTTPStatus.OK,
name="withdraw.api_lnurl_multi_response",
)
async def api_lnurl_multi_response(request: Request, unique_hash, id_unique_hash):
link = await get_withdraw_link_by_hash(unique_hash)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="LNURL-withdraw not found."
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found."
)
# return (
# {"status": "ERROR", "reason": "LNURL-withdraw not found."},
@ -158,8 +161,7 @@ async def api_lnurl_multi_response(request: Request, unique_hash, id_unique_hash
found = True
if not found:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="LNURL-withdraw not found."
status_code=HTTPStatus.NOT_FOUND, detail="LNURL-withdraw not found."
)
# return (
# {"status": "ERROR", "reason": "LNURL-withdraw not found."},

View File

@ -5,13 +5,14 @@ from sqlite3 import Row
from pydantic import BaseModel
import shortuuid # type: ignore
class CreateWithdrawData(BaseModel):
title: str = Query(...)
min_withdrawable: int = Query(..., ge=1)
max_withdrawable: int = Query(..., ge=1)
uses: int = Query(..., ge=1)
wait_time: int = Query(..., ge=1)
is_unique: bool
title: str = Query(...)
min_withdrawable: int = Query(..., ge=1)
max_withdrawable: int = Query(..., ge=1)
uses: int = Query(..., ge=1)
wait_time: int = Query(..., ge=1)
is_unique: bool
class WithdrawLink(BaseModel):
@ -49,17 +50,15 @@ class WithdrawLink(BaseModel):
url = req.url_for(
"withdraw.api_lnurl_multi_response",
unique_hash=self.unique_hash,
id_unique_hash=multihash
id_unique_hash=multihash,
)
else:
url = req.url_for(
"withdraw.api_lnurl_response",
unique_hash=self.unique_hash
"withdraw.api_lnurl_response", unique_hash=self.unique_hash
)
return lnurl_encode(url)
def lnurl_response(self, req: Request) -> LnurlWithdrawResponse:
url = req.url_for(
name="withdraw.api_lnurl_callback", unique_hash=self.unique_hash

View File

@ -15,11 +15,14 @@ from lnbits.core.models import User
templates = Jinja2Templates(directory="templates")
@withdraw_ext.get("/", response_class=HTMLResponse)
# @validate_uuids(["usr"], required=True)
# @check_user_exists()
async def index(request: Request, user: User = Depends(check_user_exists)):
return withdraw_renderer().TemplateResponse("withdraw/index.html", {"request":request,"user": user.dict()})
return withdraw_renderer().TemplateResponse(
"withdraw/index.html", {"request": request, "user": user.dict()}
)
@withdraw_ext.get("/{link_id}", response_class=HTMLResponse)
@ -28,13 +31,20 @@ async def display(request: Request, link_id):
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Withdraw link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
)
# response.status_code = HTTPStatus.NOT_FOUND
# return "Withdraw link does not exist." #probably here is where we should return the 404??
print("LINK", link)
return withdraw_renderer().TemplateResponse("withdraw/display.html", {"request":request,"link":link.dict(), "lnurl": link.lnurl(req=request), "unique":True})
return withdraw_renderer().TemplateResponse(
"withdraw/display.html",
{
"request": request,
"link": link.dict(),
"lnurl": link.lnurl(req=request),
"unique": True,
},
)
@withdraw_ext.get("/img/{link_id}", response_class=StreamingResponse)
@ -42,8 +52,7 @@ async def img(request: Request, link_id):
link = await get_withdraw_link(link_id, 0)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Withdraw link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
)
# response.status_code = HTTPStatus.NOT_FOUND
# return "Withdraw link does not exist."
@ -63,7 +72,8 @@ async def img(request: Request, link_id):
"Cache-Control": "no-cache, no-store, must-revalidate",
"Pragma": "no-cache",
"Expires": "0",
})
},
)
@withdraw_ext.get("/print/{link_id}", response_class=HTMLResponse)
@ -71,15 +81,17 @@ async def print_qr(request: Request, link_id):
link = await get_withdraw_link(link_id)
if not link:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Withdraw link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
)
# response.status_code = HTTPStatus.NOT_FOUND
# return "Withdraw link does not exist."
if link.uses == 0:
return withdraw_renderer().TemplateResponse("withdraw/print_qr.html", {"request":request,"link":link.dict(), unique:False})
return withdraw_renderer().TemplateResponse(
"withdraw/print_qr.html",
{"request": request, "link": link.dict(), unique: False},
)
links = []
count = 0
@ -87,8 +99,7 @@ async def print_qr(request: Request, link_id):
linkk = await get_withdraw_link(link_id, count)
if not linkk:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail="Withdraw link does not exist."
status_code=HTTPStatus.NOT_FOUND, detail="Withdraw link does not exist."
)
# response.status_code = HTTPStatus.NOT_FOUND
# return "Withdraw link does not exist."
@ -97,4 +108,6 @@ async def print_qr(request: Request, link_id):
page_link = list(chunks(links, 2))
linked = list(chunks(page_link, 5))
print("LINKED", linked)
return withdraw_renderer().TemplateResponse("withdraw/print_qr.html", {"request":request,"link":linked, "unique":True})
return withdraw_renderer().TemplateResponse(
"withdraw/print_qr.html", {"request": request, "link": linked, "unique": True}
)

View File

@ -28,7 +28,11 @@ from .crud import (
@withdraw_ext.get("/api/v1/links", status_code=HTTPStatus.OK)
# @api_check_wallet_key("invoice")
async def api_links(req: Request, wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)):
async def api_links(
req: Request,
wallet: WalletTypeInfo = Depends(get_key_type),
all_wallets: bool = Query(False),
):
wallet_ids = [wallet.wallet.id]
if all_wallets:
@ -36,12 +40,9 @@ async def api_links(req: Request, wallet: WalletTypeInfo = Depends(get_key_type)
try:
return [
{
**link.dict(),
**{"lnurl": link.lnurl(req)},
}
for link in await get_withdraw_links(wallet_ids)
]
{**link.dict(), **{"lnurl": link.lnurl(req)}}
for link in await get_withdraw_links(wallet_ids)
]
except LnurlInvalidUrl:
raise HTTPException(
@ -59,21 +60,20 @@ async def api_link_retrieve(link_id, wallet: WalletTypeInfo = Depends(get_key_ty
if not link:
raise HTTPException(
detail="Withdraw link does not exist.",
status_code=HTTPStatus.NOT_FOUND
detail="Withdraw link does not exist.", status_code=HTTPStatus.NOT_FOUND
)
# response.status_code = HTTPStatus.NOT_FOUND
# return {"message": "Withdraw link does not exist."}
if link.wallet != wallet.wallet.id:
raise HTTPException(
detail="Not your withdraw link.",
status_code=HTTPStatus.FORBIDDEN
detail="Not your withdraw link.", status_code=HTTPStatus.FORBIDDEN
)
# response.status_code = HTTPStatus.FORBIDDEN
# return {"message": "Not your withdraw link."}
return {**link, **{"lnurl": link.lnurl(request)}}
# class CreateData(BaseModel):
# title: str = Query(...)
# min_withdrawable: int = Query(..., ge=1)
@ -82,14 +82,20 @@ async def api_link_retrieve(link_id, wallet: WalletTypeInfo = Depends(get_key_ty
# wait_time: int = Query(..., ge=1)
# is_unique: bool
@withdraw_ext.post("/api/v1/links", status_code=HTTPStatus.CREATED)
@withdraw_ext.put("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
# @api_check_wallet_key("admin")
async def api_link_create_or_update(req: Request, data: CreateWithdrawData, link_id: str = None, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_link_create_or_update(
req: Request,
data: CreateWithdrawData,
link_id: str = None,
wallet: WalletTypeInfo = Depends(get_key_type),
):
if data.max_withdrawable < data.min_withdrawable:
raise HTTPException(
detail="`max_withdrawable` needs to be at least `min_withdrawable`.",
status_code=HTTPStatus.BAD_REQUEST
status_code=HTTPStatus.BAD_REQUEST,
)
# response.status_code = HTTPStatus.BAD_REQUEST
# return {
@ -108,15 +114,13 @@ async def api_link_create_or_update(req: Request, data: CreateWithdrawData, link
link = await get_withdraw_link(link_id, 0)
if not link:
raise HTTPException(
detail="Withdraw link does not exist.",
status_code=HTTPStatus.NOT_FOUND
detail="Withdraw link does not exist.", status_code=HTTPStatus.NOT_FOUND
)
# response.status_code = HTTPStatus.NOT_FOUND
# return {"message": "Withdraw link does not exist."}
if link.wallet != wallet.wallet.id:
raise HTTPException(
detail="Not your withdraw link.",
status_code=HTTPStatus.FORBIDDEN
detail="Not your withdraw link.", status_code=HTTPStatus.FORBIDDEN
)
# response.status_code = HTTPStatus.FORBIDDEN
# return {"message": "Not your withdraw link."}
@ -137,16 +141,14 @@ async def api_link_delete(link_id, wallet: WalletTypeInfo = Depends(get_key_type
if not link:
raise HTTPException(
detail="Withdraw link does not exist.",
status_code=HTTPStatus.NOT_FOUND
detail="Withdraw link does not exist.", status_code=HTTPStatus.NOT_FOUND
)
# response.status_code = HTTPStatus.NOT_FOUND
# return {"message": "Withdraw link does not exist."}
if link.wallet != wallet.wallet.id:
raise HTTPException(
detail="Not your withdraw link.",
status_code=HTTPStatus.FORBIDDEN
detail="Not your withdraw link.", status_code=HTTPStatus.FORBIDDEN
)
# response.status_code = HTTPStatus.FORBIDDEN
# return {"message": "Not your withdraw link."}
@ -158,6 +160,8 @@ async def api_link_delete(link_id, wallet: WalletTypeInfo = Depends(get_key_type
@withdraw_ext.get("/api/v1/links/{the_hash}/{lnurl_id}", status_code=HTTPStatus.OK)
# @api_check_wallet_key("invoice")
async def api_hash_retrieve(the_hash, lnurl_id, wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_hash_retrieve(
the_hash, lnurl_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
hashCheck = await get_hash_check(the_hash, lnurl_id)
return hashCheck

View File

@ -41,7 +41,9 @@ class ExtensionManager:
]:
try:
with open(
os.path.join(settings.LNBITS_PATH, "extensions", extension, "config.json")
os.path.join(
settings.LNBITS_PATH, "extensions", extension, "config.json"
)
) as json_file:
config = json.load(json_file)
is_valid = True
@ -137,11 +139,8 @@ def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]:
def url_for_vendored(abspath: str) -> str:
return "/" + os.path.relpath(abspath, settings.LNBITS_PATH)
def url_for(
endpoint: str,
external: Optional[bool] = False,
**params: Any,
) -> str:
def url_for(endpoint: str, external: Optional[bool] = False, **params: Any) -> str:
base = g().base_url if external else ""
url_params = "?"
for key in params:
@ -149,9 +148,12 @@ def url_for(
url = f"{base}{endpoint}{url_params}"
return url
def template_renderer(additional_folders: List = []) -> Jinja2Templates:
t = Jinja2Templates(
loader=jinja2.FileSystemLoader(["lnbits/templates", "lnbits/core/templates", *additional_folders]),
loader=jinja2.FileSystemLoader(
["lnbits/templates", "lnbits/core/templates", *additional_folders]
)
)
t.env.globals["SITE_TITLE"] = settings.LNBITS_SITE_TITLE
t.env.globals["SITE_TAGLINE"] = settings.LNBITS_SITE_TAGLINE
@ -159,7 +161,7 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates:
t.env.globals["LNBITS_THEME_OPTIONS"] = settings.LNBITS_THEME_OPTIONS
t.env.globals["LNBITS_VERSION"] = settings.LNBITS_COMMIT
t.env.globals["EXTENSIONS"] = get_valid_extensions()
if settings.DEBUG:
t.env.globals["VENDORED_JS"] = map(url_for_vendored, get_js_vendored())
t.env.globals["VENDORED_CSS"] = map(url_for_vendored, get_css_vendored())

View File

@ -1,4 +1,4 @@
# Borrowed from the excellent accent-starlette
# Borrowed from the excellent accent-starlette
# https://github.com/accent-starlette/starlette-core/blob/master/starlette_core/templating.py
import typing
@ -23,7 +23,7 @@ class Jinja2Templates(templating.Jinja2Templates):
def get_environment(self, loader: "jinja2.BaseLoader") -> "jinja2.Environment":
@jinja2.contextfunction
def url_for(context: dict, name: str, **path_params: typing.Any) -> str:
request: Request = context["request"] # type: starlette.requests.Request
request: Request = context["request"] # type: starlette.requests.Request
return request.app.url_path_for(name, **path_params)
def url_params_update(init: QueryParams, **new: typing.Any) -> QueryParams:

View File

@ -1,8 +1,9 @@
import contextvars
import types
request_global = contextvars.ContextVar("request_global",
default=types.SimpleNamespace())
request_global = contextvars.ContextVar(
"request_global", default=types.SimpleNamespace()
)
def g() -> types.SimpleNamespace:

View File

@ -52,8 +52,7 @@ SERVICE_FEE = env.float("LNBITS_SERVICE_FEE", default=0.0)
try:
LNBITS_COMMIT = (
subprocess.check_output(
["git", "-C", LNBITS_PATH, "rev-parse", "HEAD"],
stderr=subprocess.DEVNULL,
["git", "-C", LNBITS_PATH, "rev-parse", "HEAD"], stderr=subprocess.DEVNULL
)
.strip()
.decode("ascii")

View File

@ -35,7 +35,7 @@ class CLightningWallet(Wallet):
try:
answer = self.ln.help("invoicewithdescriptionhash")
if answer["help"][0]["command"].startswith(
"invoicewithdescriptionhash msatoshi label description_hash",
"invoicewithdescriptionhash msatoshi label description_hash"
):
self.supports_description_hash = True
except:
@ -53,8 +53,7 @@ class CLightningWallet(Wallet):
try:
funds = self.ln.listfunds()
return StatusResponse(
None,
sum([ch["channel_sat"] * 1000 for ch in funds["channels"]]),
None, sum([ch["channel_sat"] * 1000 for ch in funds["channels"]])
)
except RpcError as exc:
error_message = f"lightningd '{exc.method}' failed with '{exc.error}'."
@ -121,11 +120,7 @@ class CLightningWallet(Wallet):
i = 0
while True:
call = json.dumps(
{
"method": "waitanyinvoice",
"id": 0,
"params": [self.last_pay_index],
}
{"method": "waitanyinvoice", "id": 0, "params": [self.last_pay_index]}
)
await stream.send_all(call.encode("utf-8"))

View File

@ -30,9 +30,7 @@ class LNbitsWallet(Wallet):
async with httpx.AsyncClient() as client:
try:
r = await client.get(
url=f"{self.endpoint}/api/v1/wallet",
headers=self.key,
timeout=15,
url=f"{self.endpoint}/api/v1/wallet", headers=self.key, timeout=15
)
except Exception as exc:
return StatusResponse(
@ -65,9 +63,7 @@ class LNbitsWallet(Wallet):
async with httpx.AsyncClient() as client:
r = await client.post(
url=f"{self.endpoint}/api/v1/payments",
headers=self.key,
json=data,
url=f"{self.endpoint}/api/v1/payments", headers=self.key, json=data
)
ok, checking_id, payment_request, error_message = (
not r.is_error,

View File

@ -64,19 +64,11 @@ def load_macaroon(macaroon_path: str):
def parse_checking_id(checking_id: str) -> bytes:
return base64.b64decode(
checking_id.replace("_", "/"),
)
return base64.b64decode(checking_id.replace("_", "/"))
def stringify_checking_id(r_hash: bytes) -> str:
return (
base64.b64encode(
r_hash,
)
.decode("utf-8")
.replace("/", "_")
)
return base64.b64encode(r_hash).decode("utf-8").replace("/", "_")
class LndWallet(Wallet):
@ -177,28 +169,23 @@ class LndWallet(Wallet):
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
async with purerpc.secure_channel(
self.endpoint,
self.port,
get_ssl_context(self.cert_path),
self.endpoint, self.port, get_ssl_context(self.cert_path)
) as channel:
client = purerpc.Client("lnrpc.Lightning", channel)
subscribe_invoices = client.get_method_stub(
"SubscribeInvoices",
purerpc.RPCSignature(
purerpc.Cardinality.UNARY_STREAM,
ln.InvoiceSubscription,
ln.Invoice,
purerpc.Cardinality.UNARY_STREAM, ln.InvoiceSubscription, ln.Invoice
),
)
if self.macaroon_path.split('.')[-1] == 'macaroon':
if self.macaroon_path.split(".")[-1] == "macaroon":
macaroon = load_macaroon(self.macaroon_path)
else:
macaroon = self.macaroon_path
async for inv in subscribe_invoices(
ln.InvoiceSubscription(),
metadata=[("macaroon", macaroon)],
ln.InvoiceSubscription(), metadata=[("macaroon", macaroon)]
):
if not inv.settled:
continue

View File

@ -39,8 +39,7 @@ class LndRestWallet(Wallet):
try:
async with httpx.AsyncClient(verify=self.cert) as client:
r = await client.get(
f"{self.endpoint}/v1/balance/channels",
headers=self.auth,
f"{self.endpoint}/v1/balance/channels", headers=self.auth
)
except (httpx.ConnectError, httpx.RequestError):
return StatusResponse(f"Unable to connect to {self.endpoint}.", 0)
@ -60,10 +59,7 @@ class LndRestWallet(Wallet):
memo: Optional[str] = None,
description_hash: Optional[bytes] = None,
) -> InvoiceResponse:
data: Dict = {
"value": amount,
"private": True,
}
data: Dict = {"value": amount, "private": True}
if description_hash:
data["description_hash"] = base64.b64encode(description_hash).decode(
"ascii"
@ -73,9 +69,7 @@ class LndRestWallet(Wallet):
async with httpx.AsyncClient(verify=self.cert) as client:
r = await client.post(
url=f"{self.endpoint}/v1/invoices",
headers=self.auth,
json=data,
url=f"{self.endpoint}/v1/invoices", headers=self.auth, json=data
)
if r.is_error:
@ -117,8 +111,7 @@ class LndRestWallet(Wallet):
async with httpx.AsyncClient(verify=self.cert) as client:
r = await client.get(
url=f"{self.endpoint}/v1/invoice/{checking_id}",
headers=self.auth,
url=f"{self.endpoint}/v1/invoice/{checking_id}", headers=self.auth
)
if r.is_error or not r.json().get("settled"):
@ -164,9 +157,7 @@ class LndRestWallet(Wallet):
while True:
try:
async with httpx.AsyncClient(
timeout=None,
headers=self.auth,
verify=self.cert,
timeout=None, headers=self.auth, verify=self.cert
) as client:
async with client.stream("GET", url) as r:
async for line in r.aiter_lines():

View File

@ -139,12 +139,10 @@ class LNPayWallet(Wallet):
lntx_id = data["data"]["wtx"]["lnTx"]["id"]
async with httpx.AsyncClient() as client:
r = await client.get(
f"{self.endpoint}/lntx/{lntx_id}?fields=settled",
headers=self.auth,
f"{self.endpoint}/lntx/{lntx_id}?fields=settled", headers=self.auth
)
data = r.json()
if data["settled"]:
await self.queue.put(lntx_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)

View File

@ -30,9 +30,7 @@ class LntxbotWallet(Wallet):
async def status(self) -> StatusResponse:
async with httpx.AsyncClient() as client:
r = await client.get(
f"{self.endpoint}/balance",
headers=self.auth,
timeout=40,
f"{self.endpoint}/balance", headers=self.auth, timeout=40
)
try:
data = r.json()
@ -60,10 +58,7 @@ class LntxbotWallet(Wallet):
async with httpx.AsyncClient() as client:
r = await client.post(
f"{self.endpoint}/addinvoice",
headers=self.auth,
json=data,
timeout=40,
f"{self.endpoint}/addinvoice", headers=self.auth, json=data, timeout=40
)
if r.is_error:
@ -123,8 +118,7 @@ class LntxbotWallet(Wallet):
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
async with httpx.AsyncClient() as client:
r = await client.post(
url=f"{self.endpoint}/paymentstatus/{checking_id}",
headers=self.auth,
url=f"{self.endpoint}/paymentstatus/{checking_id}", headers=self.auth
)
data = r.json()

View File

@ -36,9 +36,7 @@ class OpenNodeWallet(Wallet):
try:
async with httpx.AsyncClient() as client:
r = await client.get(
f"{self.endpoint}/v1/account/balance",
headers=self.auth,
timeout=40,
f"{self.endpoint}/v1/account/balance", headers=self.auth, timeout=40
)
except (httpx.ConnectError, httpx.RequestError):
return StatusResponse(f"Unable to connect to '{self.endpoint}'", 0)
@ -137,7 +135,6 @@ class OpenNodeWallet(Wallet):
if "status" not in data or data["status"] != "paid":
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
charge_id = data["id"]
x = hmac.new(self.auth["Authorization"].encode("ascii"), digestmod="sha256")
x.update(charge_id.encode("ascii"))
@ -147,4 +144,3 @@ class OpenNodeWallet(Wallet):
await self.queue.put(charge_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)

View File

@ -75,8 +75,7 @@ class SparkWallet(Wallet):
return StatusResponse(str(e), 0)
return StatusResponse(
None,
sum([ch["channel_sat"] * 1000 for ch in funds["channels"]]),
None, sum([ch["channel_sat"] * 1000 for ch in funds["channels"]])
)
async def create_invoice(