feat: adhere to ruff's B rules (#2423)
* feat: adhere to ruff's `B` rules last of the ruff checks. closes #2308 * B904 * B008 * B005 * B025 * cleanup on fake
This commit is contained in:
parent
e13a37c193
commit
98ec59df96
28 changed files with 226 additions and 169 deletions
|
|
@ -137,8 +137,8 @@ def create_app() -> FastAPI:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Allow registering new extensions routes without direct access to the `app` object
|
# Allow registering new extensions routes without direct access to the `app` object
|
||||||
setattr(core_app_extra, "register_new_ext_routes", register_new_ext_routes(app))
|
core_app_extra.register_new_ext_routes = register_new_ext_routes(app)
|
||||||
setattr(core_app_extra, "register_new_ratelimiter", register_new_ratelimiter(app))
|
core_app_extra.register_new_ratelimiter = register_new_ratelimiter(app)
|
||||||
|
|
||||||
# register static files
|
# register static files
|
||||||
static_path = Path("lnbits", "static")
|
static_path = Path("lnbits", "static")
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@ async def migrate_extension_database(ext: Extension, current_version):
|
||||||
try:
|
try:
|
||||||
ext_migrations = importlib.import_module(f"{ext.module_name}.migrations")
|
ext_migrations = importlib.import_module(f"{ext.module_name}.migrations")
|
||||||
ext_db = importlib.import_module(ext.module_name).db
|
ext_db = importlib.import_module(ext.module_name).db
|
||||||
except ImportError as e:
|
except ImportError as exc:
|
||||||
logger.error(e)
|
logger.error(exc)
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
f"Please make sure that the extension `{ext.code}` has a migrations file."
|
f"Please make sure that the extension `{ext.code}` has a migrations file."
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
async with ext_db.connect() as ext_conn:
|
async with ext_db.connect() as ext_conn:
|
||||||
await run_migration(ext_conn, ext_migrations, ext.code, current_version)
|
await run_migration(ext_conn, ext_migrations, ext.code, current_version)
|
||||||
|
|
@ -113,7 +113,7 @@ def to_valid_user_id(user_id: str) -> UUID:
|
||||||
raise ValueError("User ID must have at least 128 bits")
|
raise ValueError("User ID must have at least 128 bits")
|
||||||
try:
|
try:
|
||||||
int(user_id, 16)
|
int(user_id, 16)
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
raise ValueError("Invalid hex string for User ID.")
|
raise ValueError("Invalid hex string for User ID.") from exc
|
||||||
|
|
||||||
return UUID(hex=user_id[:32], version=4)
|
return UUID(hex=user_id[:32], version=4)
|
||||||
|
|
|
||||||
|
|
@ -201,8 +201,8 @@ async def pay_invoice(
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
invoice = bolt11_decode(payment_request)
|
invoice = bolt11_decode(payment_request)
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
raise InvoiceError("Bolt11 decoding failed.")
|
raise InvoiceError("Bolt11 decoding failed.") from exc
|
||||||
|
|
||||||
if not invoice.amount_msat or not invoice.amount_msat > 0:
|
if not invoice.amount_msat or not invoice.amount_msat > 0:
|
||||||
raise InvoiceError("Amountless invoices not supported.")
|
raise InvoiceError("Amountless invoices not supported.")
|
||||||
|
|
@ -286,10 +286,10 @@ async def pay_invoice(
|
||||||
conn=conn,
|
conn=conn,
|
||||||
**payment_kwargs,
|
**payment_kwargs,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
logger.error(f"could not create temporary payment: {e}")
|
logger.error(f"could not create temporary payment: {exc}")
|
||||||
# happens if the same wallet tries to pay an invoice twice
|
# happens if the same wallet tries to pay an invoice twice
|
||||||
raise PaymentError("Could not make payment.")
|
raise PaymentError("Could not make payment.") from exc
|
||||||
|
|
||||||
# do the balance check
|
# do the balance check
|
||||||
wallet = await get_wallet(wallet_id, conn=conn)
|
wallet = await get_wallet(wallet_id, conn=conn)
|
||||||
|
|
@ -727,7 +727,7 @@ def update_cached_settings(sets_dict: dict):
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.warning(f"Failed overriding setting: {key}, value: {value}")
|
logger.warning(f"Failed overriding setting: {key}, value: {value}")
|
||||||
if "super_user" in sets_dict:
|
if "super_user" in sets_dict:
|
||||||
setattr(settings, "super_user", sets_dict["super_user"])
|
settings.super_user = sets_dict["super_user"]
|
||||||
|
|
||||||
|
|
||||||
async def init_admin_settings(super_user: Optional[str] = None) -> SuperSettings:
|
async def init_admin_settings(super_user: Optional[str] = None) -> SuperSettings:
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,11 @@ async def api_auditor():
|
||||||
"node_balance_msats": int(node_balance),
|
"node_balance_msats": int(node_balance),
|
||||||
"lnbits_balance_msats": int(total_balance),
|
"lnbits_balance_msats": int(total_balance),
|
||||||
}
|
}
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
detail="Could not audit balance.",
|
detail="Could not audit balance.",
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@admin_router.get(
|
@admin_router.get(
|
||||||
|
|
@ -113,10 +113,10 @@ async def api_restart_server() -> dict[str, str]:
|
||||||
async def api_topup_balance(data: CreateTopup) -> dict[str, str]:
|
async def api_topup_balance(data: CreateTopup) -> dict[str, str]:
|
||||||
try:
|
try:
|
||||||
await get_wallet(data.id)
|
await get_wallet(data.id)
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="wallet does not exist."
|
status_code=HTTPStatus.FORBIDDEN, detail="wallet does not exist."
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
if settings.lnbits_backend_wallet_class == "VoidWallet":
|
if settings.lnbits_backend_wallet_class == "VoidWallet":
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
|
||||||
try:
|
try:
|
||||||
url = str(lnurl_decode(code))
|
url = str(lnurl_decode(code))
|
||||||
domain = urlparse(url).netloc
|
domain = urlparse(url).netloc
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
# parse internet identifier (user@domain.com)
|
# parse internet identifier (user@domain.com)
|
||||||
name_domain = code.split("@")
|
name_domain = code.split("@")
|
||||||
if len(name_domain) == 2 and len(name_domain[1].split(".")) >= 2:
|
if len(name_domain) == 2 and len(name_domain[1].split(".")) >= 2:
|
||||||
|
|
@ -94,7 +94,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl"
|
status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl"
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
# params is what will be returned to the client
|
# params is what will be returned to the client
|
||||||
params: Dict = {"domain": domain}
|
params: Dict = {"domain": domain}
|
||||||
|
|
@ -119,14 +119,14 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(r.text)
|
data = json.loads(r.text)
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
|
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
|
||||||
detail={
|
detail={
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"message": f"got invalid response '{r.text[:200]}'",
|
"message": f"got invalid response '{r.text[:200]}'",
|
||||||
},
|
},
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tag: str = data.get("tag")
|
tag: str = data.get("tag")
|
||||||
|
|
@ -185,7 +185,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"message": f"lnurl JSON response invalid: {exc}",
|
"message": f"lnurl JSON response invalid: {exc}",
|
||||||
},
|
},
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,11 @@ async def login(data: LoginUsernamePassword) -> JSONResponse:
|
||||||
raise HTTPException(HTTP_401_UNAUTHORIZED, "Invalid credentials.")
|
raise HTTPException(HTTP_401_UNAUTHORIZED, "Invalid credentials.")
|
||||||
|
|
||||||
return _auth_success_response(user.username, user.id)
|
return _auth_success_response(user.username, user.id)
|
||||||
except HTTPException as e:
|
except HTTPException as exc:
|
||||||
raise e
|
raise exc
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
logger.debug(e)
|
logger.debug(exc)
|
||||||
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot login.")
|
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot login.") from exc
|
||||||
|
|
||||||
|
|
||||||
@auth_router.post("/usr", description="Login via the User ID")
|
@auth_router.post("/usr", description="Login via the User ID")
|
||||||
|
|
@ -86,11 +86,11 @@ async def login_usr(data: LoginUsr) -> JSONResponse:
|
||||||
raise HTTPException(HTTP_401_UNAUTHORIZED, "User ID does not exist.")
|
raise HTTPException(HTTP_401_UNAUTHORIZED, "User ID does not exist.")
|
||||||
|
|
||||||
return _auth_success_response(user.username or "", user.id)
|
return _auth_success_response(user.username or "", user.id)
|
||||||
except HTTPException as e:
|
except HTTPException as exc:
|
||||||
raise e
|
raise exc
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
logger.debug(e)
|
logger.debug(exc)
|
||||||
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot login.")
|
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot login.") from exc
|
||||||
|
|
||||||
|
|
||||||
@auth_router.get("/{provider}", description="SSO Provider")
|
@auth_router.get("/{provider}", description="SSO Provider")
|
||||||
|
|
@ -124,16 +124,16 @@ async def handle_oauth_token(request: Request, provider: str) -> RedirectRespons
|
||||||
user_id = decrypt_internal_message(provider_sso.state)
|
user_id = decrypt_internal_message(provider_sso.state)
|
||||||
request.session.pop("user", None)
|
request.session.pop("user", None)
|
||||||
return await _handle_sso_login(userinfo, user_id)
|
return await _handle_sso_login(userinfo, user_id)
|
||||||
except HTTPException as e:
|
except HTTPException as exc:
|
||||||
raise e
|
raise exc
|
||||||
except ValueError as e:
|
except ValueError as exc:
|
||||||
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
|
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
logger.debug(e)
|
logger.debug(exc)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
HTTP_500_INTERNAL_SERVER_ERROR,
|
HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
f"Cannot authenticate user with {provider} Auth.",
|
f"Cannot authenticate user with {provider} Auth.",
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@auth_router.post("/logout")
|
@auth_router.post("/logout")
|
||||||
|
|
@ -169,11 +169,13 @@ async def register(data: CreateUser) -> JSONResponse:
|
||||||
user = await create_user(data)
|
user = await create_user(data)
|
||||||
return _auth_success_response(user.username)
|
return _auth_success_response(user.username)
|
||||||
|
|
||||||
except ValueError as e:
|
except ValueError as exc:
|
||||||
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
|
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
logger.debug(e)
|
logger.debug(exc)
|
||||||
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot create user.")
|
raise HTTPException(
|
||||||
|
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot create user."
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@auth_router.put("/password")
|
@auth_router.put("/password")
|
||||||
|
|
@ -189,13 +191,13 @@ async def update_password(
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return await update_user_password(data)
|
return await update_user_password(data)
|
||||||
except AssertionError as e:
|
except AssertionError as exc:
|
||||||
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
|
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
logger.debug(e)
|
logger.debug(exc)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user password."
|
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user password."
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@auth_router.put("/update")
|
@auth_router.put("/update")
|
||||||
|
|
@ -211,11 +213,13 @@ async def update(
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return await update_account(user.id, data.username, None, data.config)
|
return await update_account(user.id, data.username, None, data.config)
|
||||||
except AssertionError as e:
|
except AssertionError as exc:
|
||||||
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
|
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
logger.debug(e)
|
logger.debug(exc)
|
||||||
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user.")
|
raise HTTPException(
|
||||||
|
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user."
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@auth_router.put("/first_install")
|
@auth_router.put("/first_install")
|
||||||
|
|
@ -237,13 +241,13 @@ async def first_install(data: UpdateSuperuserPassword) -> JSONResponse:
|
||||||
await update_user_password(super_user)
|
await update_user_password(super_user)
|
||||||
settings.first_install = False
|
settings.first_install = False
|
||||||
return _auth_success_response(username=super_user.username)
|
return _auth_success_response(username=super_user.username)
|
||||||
except AssertionError as e:
|
except AssertionError as exc:
|
||||||
raise HTTPException(HTTP_403_FORBIDDEN, str(e))
|
raise HTTPException(HTTP_403_FORBIDDEN, str(exc)) from exc
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
logger.debug(e)
|
logger.debug(exc)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user password."
|
HTTP_500_INTERNAL_SERVER_ERROR, "Cannot update user password."
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
async def _handle_sso_login(userinfo: OpenID, verified_user_id: Optional[str] = None):
|
async def _handle_sso_login(userinfo: OpenID, verified_user_id: Optional[str] = None):
|
||||||
|
|
|
||||||
|
|
@ -104,10 +104,10 @@ async def api_install_extension(
|
||||||
ext_info.notify_upgrade()
|
ext_info.notify_upgrade()
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
except AssertionError as e:
|
except AssertionError as exc:
|
||||||
raise HTTPException(HTTPStatus.BAD_REQUEST, str(e))
|
raise HTTPException(HTTPStatus.BAD_REQUEST, str(exc)) from exc
|
||||||
except Exception as ex:
|
except Exception as exc:
|
||||||
logger.warning(ex)
|
logger.warning(exc)
|
||||||
ext_info.clean_extension_files()
|
ext_info.clean_extension_files()
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
|
@ -115,7 +115,7 @@ async def api_install_extension(
|
||||||
f"Failed to install extension {ext_info.id} "
|
f"Failed to install extension {ext_info.id} "
|
||||||
f"({ext_info.installed_version})."
|
f"({ext_info.installed_version})."
|
||||||
),
|
),
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@extension_router.delete("/{ext_id}")
|
@extension_router.delete("/{ext_id}")
|
||||||
|
|
@ -159,10 +159,10 @@ async def api_uninstall_extension(
|
||||||
await delete_installed_extension(ext_id=ext_info.id)
|
await delete_installed_extension(ext_id=ext_info.id)
|
||||||
|
|
||||||
logger.success(f"Extension '{ext_id}' uninstalled.")
|
logger.success(f"Extension '{ext_id}' uninstalled.")
|
||||||
except Exception as ex:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(ex)
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@extension_router.get("/{ext_id}/releases", dependencies=[Depends(check_admin)])
|
@extension_router.get("/{ext_id}/releases", dependencies=[Depends(check_admin)])
|
||||||
|
|
@ -183,10 +183,10 @@ async def get_extension_releases(ext_id: str):
|
||||||
|
|
||||||
return extension_releases
|
return extension_releases
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(ex)
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@extension_router.put("/invoice", dependencies=[Depends(check_admin)])
|
@extension_router.put("/invoice", dependencies=[Depends(check_admin)])
|
||||||
|
|
@ -216,11 +216,13 @@ async def get_extension_invoice(data: CreateExtension):
|
||||||
|
|
||||||
return payment_info
|
return payment_info
|
||||||
|
|
||||||
except AssertionError as e:
|
except AssertionError as exc:
|
||||||
raise HTTPException(HTTPStatus.BAD_REQUEST, str(e))
|
raise HTTPException(HTTPStatus.BAD_REQUEST, str(exc)) from exc
|
||||||
except Exception as ex:
|
except Exception as exc:
|
||||||
logger.warning(ex)
|
logger.warning(exc)
|
||||||
raise HTTPException(HTTPStatus.INTERNAL_SERVER_ERROR, "Cannot request invoice")
|
raise HTTPException(
|
||||||
|
HTTPStatus.INTERNAL_SERVER_ERROR, "Cannot request invoice"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@extension_router.get(
|
@extension_router.get(
|
||||||
|
|
@ -238,10 +240,10 @@ async def get_extension_release(org: str, repo: str, tag_name: str):
|
||||||
"is_version_compatible": config.is_version_compatible(),
|
"is_version_compatible": config.is_version_compatible(),
|
||||||
"warning": config.warning,
|
"warning": config.warning,
|
||||||
}
|
}
|
||||||
except Exception as ex:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(ex)
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@extension_router.delete(
|
@extension_router.delete(
|
||||||
|
|
@ -262,9 +264,9 @@ async def delete_extension_db(ext_id: str):
|
||||||
except HTTPException as ex:
|
except HTTPException as ex:
|
||||||
logger.error(ex)
|
logger.error(ex)
|
||||||
raise ex
|
raise ex
|
||||||
except Exception as ex:
|
except Exception as exc:
|
||||||
logger.error(ex)
|
logger.error(exc)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
detail=f"Cannot delete data for extension '{ext_id}'",
|
detail=f"Cannot delete data for extension '{ext_id}'",
|
||||||
)
|
) from exc
|
||||||
|
|
|
||||||
|
|
@ -171,9 +171,11 @@ async def extensions_install(
|
||||||
"extensions": extensions,
|
"extensions": extensions,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
logger.warning(e)
|
logger.warning(exc)
|
||||||
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get(
|
@generic_router.get(
|
||||||
|
|
@ -396,8 +398,10 @@ async def hex_to_uuid4(hex_value: str):
|
||||||
try:
|
try:
|
||||||
user_id = to_valid_user_id(hex_value).hex
|
user_id = to_valid_user_id(hex_value).hex
|
||||||
return RedirectResponse(url=f"/wallet?usr={user_id}")
|
return RedirectResponse(url=f"/wallet?usr={user_id}")
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST, detail=str(exc)
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
async def toggle_extension(extension_to_enable, extension_to_disable, user_id):
|
async def toggle_extension(extension_to_enable, extension_to_disable, user_id):
|
||||||
|
|
|
||||||
|
|
@ -195,5 +195,7 @@ async def api_get_1ml_stats(node: Node = Depends(require_node)) -> Optional[Node
|
||||||
try:
|
try:
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
return r.json()["noderank"]
|
return r.json()["noderank"]
|
||||||
except httpx.HTTPStatusError:
|
except httpx.HTTPStatusError as exc:
|
||||||
raise HTTPException(status_code=404, detail="Node not found on 1ml.com")
|
raise HTTPException(
|
||||||
|
status_code=404, detail="Node not found on 1ml.com"
|
||||||
|
) from exc
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from fastapi import (
|
||||||
Depends,
|
Depends,
|
||||||
Header,
|
Header,
|
||||||
HTTPException,
|
HTTPException,
|
||||||
|
Query,
|
||||||
Request,
|
Request,
|
||||||
)
|
)
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
@ -28,7 +29,6 @@ from lnbits.core.models import (
|
||||||
Payment,
|
Payment,
|
||||||
PaymentFilters,
|
PaymentFilters,
|
||||||
PaymentHistoryPoint,
|
PaymentHistoryPoint,
|
||||||
Query,
|
|
||||||
Wallet,
|
Wallet,
|
||||||
WalletType,
|
WalletType,
|
||||||
)
|
)
|
||||||
|
|
@ -133,19 +133,19 @@ async def api_payments_create_invoice(data: CreateInvoice, wallet: Wallet):
|
||||||
if data.description_hash:
|
if data.description_hash:
|
||||||
try:
|
try:
|
||||||
description_hash = bytes.fromhex(data.description_hash)
|
description_hash = bytes.fromhex(data.description_hash)
|
||||||
except ValueError:
|
except ValueError as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
detail="'description_hash' must be a valid hex string",
|
detail="'description_hash' must be a valid hex string",
|
||||||
)
|
) from exc
|
||||||
if data.unhashed_description:
|
if data.unhashed_description:
|
||||||
try:
|
try:
|
||||||
unhashed_description = bytes.fromhex(data.unhashed_description)
|
unhashed_description = bytes.fromhex(data.unhashed_description)
|
||||||
except ValueError:
|
except ValueError as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
detail="'unhashed_description' must be a valid hex string",
|
detail="'unhashed_description' must be a valid hex string",
|
||||||
)
|
) from exc
|
||||||
# do not save memo if description_hash or unhashed_description is set
|
# do not save memo if description_hash or unhashed_description is set
|
||||||
memo = ""
|
memo = ""
|
||||||
|
|
||||||
|
|
@ -170,8 +170,8 @@ async def api_payments_create_invoice(data: CreateInvoice, wallet: Wallet):
|
||||||
payment_db = await get_standalone_payment(payment_hash, conn=conn)
|
payment_db = await get_standalone_payment(payment_hash, conn=conn)
|
||||||
assert payment_db is not None, "payment not found"
|
assert payment_db is not None, "payment not found"
|
||||||
checking_id = payment_db.checking_id
|
checking_id = payment_db.checking_id
|
||||||
except InvoiceError as e:
|
except InvoiceError as exc:
|
||||||
raise HTTPException(status_code=520, detail=str(e))
|
raise HTTPException(status_code=520, detail=str(exc)) from exc
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
|
|
@ -192,12 +192,14 @@ async def api_payments_pay_invoice(
|
||||||
payment_hash = await pay_invoice(
|
payment_hash = await pay_invoice(
|
||||||
wallet_id=wallet.id, payment_request=bolt11, extra=extra
|
wallet_id=wallet.id, payment_request=bolt11, extra=extra
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as exc:
|
||||||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
|
raise HTTPException(
|
||||||
except PermissionError as e:
|
status_code=HTTPStatus.BAD_REQUEST, detail=str(exc)
|
||||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail=str(e))
|
) from exc
|
||||||
except PaymentError as e:
|
except PermissionError as exc:
|
||||||
raise HTTPException(status_code=520, detail=str(e))
|
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail=str(exc)) from exc
|
||||||
|
except PaymentError as exc:
|
||||||
|
raise HTTPException(status_code=520, detail=str(exc)) from exc
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
|
|
@ -282,11 +284,11 @@ async def api_payments_pay_lnurl(
|
||||||
if r.is_error:
|
if r.is_error:
|
||||||
raise httpx.ConnectError("LNURL callback connection error")
|
raise httpx.ConnectError("LNURL callback connection error")
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
except (httpx.ConnectError, httpx.RequestError):
|
except (httpx.ConnectError, httpx.RequestError) as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
detail=f"Failed to connect to {domain}.",
|
detail=f"Failed to connect to {domain}.",
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
params = json.loads(r.text)
|
params = json.loads(r.text)
|
||||||
if params.get("status") == "ERROR":
|
if params.get("status") == "ERROR":
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,10 @@ async def api_public_payment_longpolling(payment_hash):
|
||||||
invoice = bolt11.decode(payment.bolt11)
|
invoice = bolt11.decode(payment.bolt11)
|
||||||
if invoice.has_expired():
|
if invoice.has_expired():
|
||||||
return {"status": "expired"}
|
return {"status": "expired"}
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST, detail="Invalid bolt11 invoice."
|
status_code=HTTPStatus.BAD_REQUEST, detail="Invalid bolt11 invoice."
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
payment_queue = asyncio.Queue(0)
|
payment_queue = asyncio.Queue(0)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,10 @@ async def api_create_tinyurl(
|
||||||
if tinyurl.wallet == wallet.wallet.id:
|
if tinyurl.wallet == wallet.wallet.id:
|
||||||
return tinyurl
|
return tinyurl
|
||||||
return await create_tinyurl(url, endless, wallet.wallet.id)
|
return await create_tinyurl(url, endless, wallet.wallet.id)
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST, detail="Unable to create tinyurl"
|
status_code=HTTPStatus.BAD_REQUEST, detail="Unable to create tinyurl"
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@tinyurl_router.get(
|
@tinyurl_router.get(
|
||||||
|
|
@ -60,10 +60,10 @@ async def api_get_tinyurl(
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="Wrong key provided."
|
status_code=HTTPStatus.FORBIDDEN, detail="Wrong key provided."
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Unable to fetch tinyurl"
|
status_code=HTTPStatus.NOT_FOUND, detail="Unable to fetch tinyurl"
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@tinyurl_router.delete(
|
@tinyurl_router.delete(
|
||||||
|
|
@ -83,10 +83,10 @@ async def api_delete_tinyurl(
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="Wrong key provided."
|
status_code=HTTPStatus.FORBIDDEN, detail="Wrong key provided."
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST, detail="Unable to delete"
|
status_code=HTTPStatus.BAD_REQUEST, detail="Unable to delete"
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
@tinyurl_router.get(
|
@tinyurl_router.get(
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,10 @@ class KeyChecker(SecurityBase):
|
||||||
detail="Invalid key or wallet.",
|
detail="Invalid key or wallet.",
|
||||||
)
|
)
|
||||||
self.wallet = wallet
|
self.wallet = wallet
|
||||||
except KeyError:
|
except KeyError as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST, detail="`X-API-KEY` header missing."
|
status_code=HTTPStatus.BAD_REQUEST, detail="`X-API-KEY` header missing."
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
class WalletInvoiceKeyChecker(KeyChecker):
|
class WalletInvoiceKeyChecker(KeyChecker):
|
||||||
|
|
@ -324,10 +324,10 @@ async def _get_account_from_token(access_token):
|
||||||
return await get_account_by_email(str(payload.get("email")))
|
return await get_account_by_email(str(payload.get("email")))
|
||||||
|
|
||||||
raise HTTPException(HTTPStatus.UNAUTHORIZED, "Data missing for access token.")
|
raise HTTPException(HTTPStatus.UNAUTHORIZED, "Data missing for access token.")
|
||||||
except ExpiredSignatureError:
|
except ExpiredSignatureError as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
HTTPStatus.UNAUTHORIZED, "Session expired.", {"token-expired": "true"}
|
HTTPStatus.UNAUTHORIZED, "Session expired.", {"token-expired": "true"}
|
||||||
)
|
) from exc
|
||||||
except JWTError as e:
|
except JWTError as exc:
|
||||||
logger.debug(e)
|
logger.debug(exc)
|
||||||
raise HTTPException(HTTPStatus.UNAUTHORIZED, "Invalid access token.")
|
raise HTTPException(HTTPStatus.UNAUTHORIZED, "Invalid access token.") from exc
|
||||||
|
|
|
||||||
|
|
@ -428,9 +428,9 @@ class InstallableExtension(BaseModel):
|
||||||
|
|
||||||
self._remember_payment_info()
|
self._remember_payment_info()
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as exc:
|
||||||
logger.warning(ex)
|
logger.warning(exc)
|
||||||
raise AssertionError("Cannot fetch extension archive file")
|
raise AssertionError("Cannot fetch extension archive file") from exc
|
||||||
|
|
||||||
archive_hash = file_hash(ext_zip_file)
|
archive_hash = file_hash(ext_zip_file)
|
||||||
if self.installed_release.hash and self.installed_release.hash != archive_hash:
|
if self.installed_release.hash and self.installed_release.hash != archive_hash:
|
||||||
|
|
@ -560,7 +560,11 @@ class InstallableExtension(BaseModel):
|
||||||
return ext
|
return ext
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_rows(cls, rows: List[Any] = []) -> List["InstallableExtension"]:
|
def from_rows(
|
||||||
|
cls, rows: Optional[List[Any]] = None
|
||||||
|
) -> List["InstallableExtension"]:
|
||||||
|
if rows is None:
|
||||||
|
rows = []
|
||||||
return [InstallableExtension.from_row(row) for row in rows]
|
return [InstallableExtension.from_row(row) for row in rows]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,12 @@ def catch_rpc_errors(f):
|
||||||
async def wrapper(*args, **kwargs):
|
async def wrapper(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
return await f(*args, **kwargs)
|
return await f(*args, **kwargs)
|
||||||
except RpcError as e:
|
except RpcError as exc:
|
||||||
if e.error["code"] == -32602:
|
msg = exc.error["message"]
|
||||||
raise HTTPException(status_code=400, detail=e.error["message"])
|
if exc.error["code"] == -32602:
|
||||||
|
raise HTTPException(status_code=400, detail=msg) from exc
|
||||||
else:
|
else:
|
||||||
raise HTTPException(status_code=500, detail=e.error["message"])
|
raise HTTPException(status_code=500, detail=msg) from exc
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
@ -66,9 +67,11 @@ class CoreLightningNode(Node):
|
||||||
# https://docs.corelightning.org/reference/lightning-connect
|
# https://docs.corelightning.org/reference/lightning-connect
|
||||||
try:
|
try:
|
||||||
await self.ln_rpc("connect", uri)
|
await self.ln_rpc("connect", uri)
|
||||||
except RpcError as e:
|
except RpcError as exc:
|
||||||
if e.error["code"] == 400:
|
if exc.error["code"] == 400:
|
||||||
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=e.error["message"])
|
raise HTTPException(
|
||||||
|
HTTPStatus.BAD_REQUEST, detail=exc.error["message"]
|
||||||
|
) from exc
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
@ -76,12 +79,12 @@ class CoreLightningNode(Node):
|
||||||
async def disconnect_peer(self, peer_id: str):
|
async def disconnect_peer(self, peer_id: str):
|
||||||
try:
|
try:
|
||||||
await self.ln_rpc("disconnect", peer_id)
|
await self.ln_rpc("disconnect", peer_id)
|
||||||
except RpcError as e:
|
except RpcError as exc:
|
||||||
if e.error["code"] == -1:
|
if exc.error["code"] == -1:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
HTTPStatus.BAD_REQUEST,
|
HTTPStatus.BAD_REQUEST,
|
||||||
detail=e.error["message"],
|
detail=exc.error["message"],
|
||||||
)
|
) from exc
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
@ -105,14 +108,14 @@ class CoreLightningNode(Node):
|
||||||
funding_txid=result["txid"],
|
funding_txid=result["txid"],
|
||||||
output_index=result["outnum"],
|
output_index=result["outnum"],
|
||||||
)
|
)
|
||||||
except RpcError as e:
|
except RpcError as exc:
|
||||||
message = e.error["message"]
|
message = exc.error["message"]
|
||||||
|
|
||||||
if "amount: should be a satoshi amount" in message:
|
if "amount: should be a satoshi amount" in message:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
HTTPStatus.BAD_REQUEST,
|
HTTPStatus.BAD_REQUEST,
|
||||||
detail="The amount is not a valid satoshi amount.",
|
detail="The amount is not a valid satoshi amount.",
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
if "Unknown peer" in message:
|
if "Unknown peer" in message:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -121,7 +124,7 @@ class CoreLightningNode(Node):
|
||||||
"We where able to connect to the peer but CLN "
|
"We where able to connect to the peer but CLN "
|
||||||
"can't find it when opening a channel."
|
"can't find it when opening a channel."
|
||||||
),
|
),
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
if "Owning subdaemon openingd died" in message:
|
if "Owning subdaemon openingd died" in message:
|
||||||
# https://github.com/ElementsProject/lightning/issues/2798#issuecomment-511205719
|
# https://github.com/ElementsProject/lightning/issues/2798#issuecomment-511205719
|
||||||
|
|
@ -131,14 +134,14 @@ class CoreLightningNode(Node):
|
||||||
"Likely the peer didn't like our channel opening "
|
"Likely the peer didn't like our channel opening "
|
||||||
"proposal and disconnected from us."
|
"proposal and disconnected from us."
|
||||||
),
|
),
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
if (
|
if (
|
||||||
"Number of pending channels exceed maximum" in message
|
"Number of pending channels exceed maximum" in message
|
||||||
or "exceeds maximum chan size of 10 BTC" in message
|
or "exceeds maximum chan size of 10 BTC" in message
|
||||||
or "Could not afford" in message
|
or "Could not afford" in message
|
||||||
):
|
):
|
||||||
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=message)
|
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=message) from exc
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@catch_rpc_errors
|
@catch_rpc_errors
|
||||||
|
|
@ -152,13 +155,13 @@ class CoreLightningNode(Node):
|
||||||
raise HTTPException(status_code=400, detail="Short id required")
|
raise HTTPException(status_code=400, detail="Short id required")
|
||||||
try:
|
try:
|
||||||
await self.ln_rpc("close", short_id)
|
await self.ln_rpc("close", short_id)
|
||||||
except RpcError as e:
|
except RpcError as exc:
|
||||||
message = e.error["message"]
|
message = exc.error["message"]
|
||||||
if (
|
if (
|
||||||
"Short channel ID not active:" in message
|
"Short channel ID not active:" in message
|
||||||
or "Short channel ID not found" in message
|
or "Short channel ID not found" in message
|
||||||
):
|
):
|
||||||
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=message)
|
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=message) from exc
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,11 +60,13 @@ class LndRestNode(Node):
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
except HTTPStatusError as e:
|
except HTTPStatusError as exc:
|
||||||
json = e.response.json()
|
json = exc.response.json()
|
||||||
if json:
|
if json:
|
||||||
error = json.get("error") or json
|
error = json.get("error") or json
|
||||||
raise HTTPException(e.response.status_code, detail=error.get("message"))
|
raise HTTPException(
|
||||||
|
exc.response.status_code, detail=error.get("message")
|
||||||
|
) from exc
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
def get(self, path: str, **kwargs):
|
def get(self, path: str, **kwargs):
|
||||||
|
|
@ -81,8 +83,8 @@ class LndRestNode(Node):
|
||||||
async def connect_peer(self, uri: str):
|
async def connect_peer(self, uri: str):
|
||||||
try:
|
try:
|
||||||
pubkey, host = uri.split("@")
|
pubkey, host = uri.split("@")
|
||||||
except ValueError:
|
except ValueError as exc:
|
||||||
raise HTTPException(400, detail="Invalid peer URI")
|
raise HTTPException(400, detail="Invalid peer URI") from exc
|
||||||
await self.request(
|
await self.request(
|
||||||
"POST",
|
"POST",
|
||||||
"/v1/peers",
|
"/v1/peers",
|
||||||
|
|
@ -96,11 +98,11 @@ class LndRestNode(Node):
|
||||||
async def disconnect_peer(self, peer_id: str):
|
async def disconnect_peer(self, peer_id: str):
|
||||||
try:
|
try:
|
||||||
await self.request("DELETE", "/v1/peers/" + peer_id)
|
await self.request("DELETE", "/v1/peers/" + peer_id)
|
||||||
except HTTPException as e:
|
except HTTPException as exc:
|
||||||
if "unable to disconnect" in e.detail:
|
if "unable to disconnect" in exc.detail:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
HTTPStatus.BAD_REQUEST, detail="Peer is not connected"
|
HTTPStatus.BAD_REQUEST, detail="Peer is not connected"
|
||||||
)
|
) from exc
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def _get_peer_info(self, peer_id: str) -> NodePeerInfo:
|
async def _get_peer_info(self, peer_id: str) -> NodePeerInfo:
|
||||||
|
|
|
||||||
|
|
@ -48,18 +48,20 @@ def main(
|
||||||
|
|
||||||
# this beautiful beast parses all command line arguments and
|
# this beautiful beast parses all command line arguments and
|
||||||
# passes them to the uvicorn server
|
# passes them to the uvicorn server
|
||||||
|
# TODO: why is this needed? it would be better only to rely on the commands options
|
||||||
d = {}
|
d = {}
|
||||||
for a in ctx.args:
|
for a in ctx.args:
|
||||||
item = a.split("=")
|
item = a.split("=")
|
||||||
if len(item) > 1: # argument like --key=value
|
if len(item) > 1: # argument like --key=value
|
||||||
print(a, item)
|
print(a, item)
|
||||||
d[item[0].strip("--").replace("-", "_")] = (
|
d[item[0].strip("--").replace("-", "_")] = ( # noqa: B005
|
||||||
int(item[1]) # need to convert to int if it's a number
|
int(item[1]) # need to convert to int if it's a number
|
||||||
if item[1].isdigit()
|
if item[1].isdigit()
|
||||||
else item[1]
|
else item[1]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
d[a.strip("--")] = True # argument like --key
|
# argument like --key
|
||||||
|
d[a.strip("--")] = True # noqa: B005
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
config = uvicorn.Config(
|
config = uvicorn.Config(
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ class AESCipher:
|
||||||
aes = AES.new(key, AES.MODE_CBC, iv)
|
aes = AES.new(key, AES.MODE_CBC, iv)
|
||||||
try:
|
try:
|
||||||
return self.unpad(aes.decrypt(encrypted[16:])).decode() # type: ignore
|
return self.unpad(aes.decrypt(encrypted[16:])).decode() # type: ignore
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError as exc:
|
||||||
raise ValueError("Wrong passphrase")
|
raise ValueError("Wrong passphrase") from exc
|
||||||
|
|
||||||
def encrypt(self, message: bytes) -> str:
|
def encrypt(self, message: bytes) -> str:
|
||||||
passphrase = self.passphrase
|
passphrase = self.passphrase
|
||||||
|
|
|
||||||
|
|
@ -93,11 +93,13 @@ class PaymentPendingStatus(PaymentStatus):
|
||||||
|
|
||||||
|
|
||||||
class Wallet(ABC):
|
class Wallet(ABC):
|
||||||
async def cleanup(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
__node_cls__: Optional[type[Node]] = None
|
__node_cls__: Optional[type[Node]] = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def cleanup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def status(self) -> Coroutine[None, None, StatusResponse]:
|
def status(self) -> Coroutine[None, None, StatusResponse]:
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ class ClicheWallet(Wallet):
|
||||||
|
|
||||||
self.endpoint = self.normalize_endpoint(settings.cliche_endpoint)
|
self.endpoint = self.normalize_endpoint(settings.cliche_endpoint)
|
||||||
|
|
||||||
|
async def cleanup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
async def status(self) -> StatusResponse:
|
async def status(self) -> StatusResponse:
|
||||||
try:
|
try:
|
||||||
ws = create_connection(self.endpoint)
|
ws = create_connection(self.endpoint)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ async def run_sync(func) -> Any:
|
||||||
class CoreLightningWallet(Wallet):
|
class CoreLightningWallet(Wallet):
|
||||||
__node_cls__ = CoreLightningNode
|
__node_cls__ = CoreLightningNode
|
||||||
|
|
||||||
|
async def cleanup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
rpc = settings.corelightning_rpc or settings.clightning_rpc
|
rpc = settings.corelightning_rpc or settings.clightning_rpc
|
||||||
if not rpc:
|
if not rpc:
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ class FakeWallet(Wallet):
|
||||||
32,
|
32,
|
||||||
).hex()
|
).hex()
|
||||||
|
|
||||||
|
async def cleanup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
async def status(self) -> StatusResponse:
|
async def status(self) -> StatusResponse:
|
||||||
logger.info(
|
logger.info(
|
||||||
"FakeWallet funding source is for using LNbits as a centralised,"
|
"FakeWallet funding source is for using LNbits as a centralised,"
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,9 @@ class LndWallet(Wallet):
|
||||||
def metadata_callback(self, _, callback):
|
def metadata_callback(self, _, callback):
|
||||||
callback([("macaroon", self.macaroon)], None)
|
callback([("macaroon", self.macaroon)], None)
|
||||||
|
|
||||||
|
async def cleanup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
async def status(self) -> StatusResponse:
|
async def status(self) -> StatusResponse:
|
||||||
try:
|
try:
|
||||||
resp = await self.rpc.ChannelBalance(ln.ChannelBalanceRequest())
|
resp = await self.rpc.ChannelBalance(ln.ChannelBalanceRequest())
|
||||||
|
|
|
||||||
|
|
@ -77,12 +77,12 @@ class SparkWallet(Wallet):
|
||||||
httpx.HTTPError,
|
httpx.HTTPError,
|
||||||
httpx.TimeoutException,
|
httpx.TimeoutException,
|
||||||
) as exc:
|
) as exc:
|
||||||
raise UnknownError(f"error connecting to spark: {exc}")
|
raise UnknownError(f"error connecting to spark: {exc}") from exc
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = r.json()
|
data = r.json()
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
raise UnknownError(r.text)
|
raise UnknownError(r.text) from exc
|
||||||
|
|
||||||
if r.is_error:
|
if r.is_error:
|
||||||
if r.status_code == 401:
|
if r.status_code == 401:
|
||||||
|
|
@ -171,7 +171,7 @@ class SparkWallet(Wallet):
|
||||||
raise SparkError(
|
raise SparkError(
|
||||||
f"listpays({payment_hash}) returned an unexpected response:"
|
f"listpays({payment_hash}) returned an unexpected response:"
|
||||||
f" {listpays}"
|
f" {listpays}"
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
if pay["status"] == "failed":
|
if pay["status"] == "failed":
|
||||||
return PaymentResponse(False, None, None, None, str(exc))
|
return PaymentResponse(False, None, None, None, str(exc))
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@ from .base import (
|
||||||
|
|
||||||
|
|
||||||
class VoidWallet(Wallet):
|
class VoidWallet(Wallet):
|
||||||
|
|
||||||
|
async def cleanup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
async def create_invoice(self, *_, **__) -> InvoiceResponse:
|
async def create_invoice(self, *_, **__) -> InvoiceResponse:
|
||||||
return InvoiceResponse(
|
return InvoiceResponse(
|
||||||
ok=False, error_message="VoidWallet cannot create invoices."
|
ok=False, error_message="VoidWallet cannot create invoices."
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,8 @@ extend-exclude = [
|
||||||
# N - naming
|
# N - naming
|
||||||
# UP - pyupgrade
|
# UP - pyupgrade
|
||||||
# RUF - ruff specific rules
|
# RUF - ruff specific rules
|
||||||
select = ["F", "E", "W", "I", "A", "C", "N", "UP", "RUF"]
|
# B - bugbear
|
||||||
|
select = ["F", "E", "W", "I", "A", "C", "N", "UP", "RUF", "B"]
|
||||||
# UP007: pyupgrade: use X | Y instead of Optional. (python3.10)
|
# UP007: pyupgrade: use X | Y instead of Optional. (python3.10)
|
||||||
# RUF012: mutable-class-default
|
# RUF012: mutable-class-default
|
||||||
ignore = ["UP007", "RUF012"]
|
ignore = ["UP007", "RUF012"]
|
||||||
|
|
@ -206,3 +207,12 @@ classmethod-decorators = [
|
||||||
[tool.ruff.lint.mccabe]
|
[tool.ruff.lint.mccabe]
|
||||||
# TODO: Decrease this to 10.
|
# TODO: Decrease this to 10.
|
||||||
max-complexity = 16
|
max-complexity = 16
|
||||||
|
|
||||||
|
[tool.ruff.lint.flake8-bugbear]
|
||||||
|
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.
|
||||||
|
extend-immutable-calls = [
|
||||||
|
"fastapi.Depends",
|
||||||
|
"fastapi.Query",
|
||||||
|
"fastapi.Body",
|
||||||
|
"lnbits.decorators.parse_filters"
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ def load_funding_source(funding_source: FundingSourceConfig) -> BaseWallet:
|
||||||
custom_settings = funding_source.settings
|
custom_settings = funding_source.settings
|
||||||
original_settings = {}
|
original_settings = {}
|
||||||
|
|
||||||
settings = getattr(wallets_module, "settings")
|
settings = wallets_module.settings
|
||||||
|
|
||||||
for s in custom_settings:
|
for s in custom_settings:
|
||||||
original_settings[s] = getattr(settings, s)
|
original_settings[s] = getattr(settings, s)
|
||||||
|
|
@ -93,7 +93,7 @@ async def check_assertions(wallet, _test_data: WalletTest):
|
||||||
elif "expect_error" in test_data:
|
elif "expect_error" in test_data:
|
||||||
await _assert_error(wallet, tested_func, call_params, _test_data.expect_error)
|
await _assert_error(wallet, tested_func, call_params, _test_data.expect_error)
|
||||||
else:
|
else:
|
||||||
assert False, "Expected outcome not specified"
|
raise AssertionError("Expected outcome not specified")
|
||||||
|
|
||||||
|
|
||||||
async def _assert_data(wallet, tested_func, call_params, expect):
|
async def _assert_data(wallet, tested_func, call_params, expect):
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import argparse
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import sys
|
import sys
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
|
||||||
|
|
@ -90,21 +90,23 @@ def insert_to_pg(query, data):
|
||||||
for d in data:
|
for d in data:
|
||||||
try:
|
try:
|
||||||
cursor.execute(query, d)
|
cursor.execute(query, d)
|
||||||
except Exception as e:
|
except Exception as exc:
|
||||||
if args.ignore_errors:
|
if args.ignore_errors:
|
||||||
print(e)
|
print(exc)
|
||||||
print(f"Failed to insert {d}")
|
print(f"Failed to insert {d}")
|
||||||
else:
|
else:
|
||||||
print("query:", query)
|
print("query:", query)
|
||||||
print("data:", d)
|
print("data:", d)
|
||||||
raise ValueError(f"Failed to insert {d}")
|
raise ValueError(f"Failed to insert {d}") from exc
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
def migrate_core(file: str, exclude_tables: List[str] = []):
|
def migrate_core(file: str, exclude_tables: Optional[List[str]] = None):
|
||||||
|
if exclude_tables is None:
|
||||||
|
exclude_tables = []
|
||||||
print(f"Migrating core: {file}")
|
print(f"Migrating core: {file}")
|
||||||
migrate_db(file, "public", exclude_tables)
|
migrate_db(file, "public", exclude_tables)
|
||||||
print("✅ Migrated core")
|
print("✅ Migrated core")
|
||||||
|
|
@ -118,8 +120,10 @@ def migrate_ext(file: str):
|
||||||
print(f"✅ Migrated ext: {schema}")
|
print(f"✅ Migrated ext: {schema}")
|
||||||
|
|
||||||
|
|
||||||
def migrate_db(file: str, schema: str, exclude_tables: List[str] = []):
|
def migrate_db(file: str, schema: str, exclude_tables: Optional[List[str]] = None):
|
||||||
# first we check if this file exists:
|
# first we check if this file exists:
|
||||||
|
if exclude_tables is None:
|
||||||
|
exclude_tables = []
|
||||||
assert os.path.isfile(file), f"{file} does not exist!"
|
assert os.path.isfile(file), f"{file} does not exist!"
|
||||||
|
|
||||||
cursor = get_sqlite_cursor(file)
|
cursor = get_sqlite_cursor(file)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue