mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2024-11-19 01:43:42 +01:00
I want them to turn black
This commit is contained in:
parent
70facdaa93
commit
1d3bb016a2
@ -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()
|
||||
|
||||
|
@ -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}
|
||||
)
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
@ -9,5 +9,3 @@ core_app: APIRouter = APIRouter()
|
||||
from .views.api import * # noqa
|
||||
from .views.generic import * # noqa
|
||||
from .views.public_api import * # noqa
|
||||
|
||||
|
||||
|
@ -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(
|
||||
"""
|
||||
|
@ -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"]:
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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 ""
|
||||
|
||||
|
||||
|
@ -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
|
||||
],
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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 ""
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}}
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
"""
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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=[],
|
||||
)
|
||||
|
||||
|
@ -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.",
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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."}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -7,6 +7,7 @@ class CreateTposData(BaseModel):
|
||||
name: str
|
||||
currency: str
|
||||
|
||||
|
||||
class TPoS(BaseModel):
|
||||
id: str
|
||||
wallet: str
|
||||
|
@ -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}
|
||||
)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
||||
|
||||
|
@ -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(...)
|
||||
|
@ -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()}
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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."},
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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")
|
||||
|
@ -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"))
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user