From d2cd9726635686bc009f34b1e2f9e7391599b0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 6 Dec 2022 10:58:59 +0100 Subject: [PATCH] refactor from settings.py into admin crud, but broke frontend formatting --- lnbits/app.py | 1 - lnbits/extensions/admin/crud.py | 88 ++++++++++++++--------- lnbits/extensions/admin/migrations.py | 6 +- lnbits/extensions/admin/models.py | 9 ++- lnbits/extensions/admin/views_api.py | 14 ++-- lnbits/settings.py | 100 ++++++++++---------------- 6 files changed, 108 insertions(+), 110 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index ed279af23..78420feda 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -4,7 +4,6 @@ import logging import signal import sys import traceback -import warnings from http import HTTPStatus from fastapi import FastAPI, Request diff --git a/lnbits/extensions/admin/crud.py b/lnbits/extensions/admin/crud.py index e635dab31..179175690 100644 --- a/lnbits/extensions/admin/crud.py +++ b/lnbits/extensions/admin/crud.py @@ -1,6 +1,6 @@ from typing import Optional -from lnbits.core.crud import create_payment +from lnbits.core.crud import create_account, create_payment from lnbits.helpers import urlsafe_short_hash from lnbits.settings import readonly_variables, settings from lnbits.tasks import internal_invoice_queue @@ -11,7 +11,6 @@ from .models import AdminSettings, UpdateSettings async def update_wallet_balance(wallet_id: str, amount: int): internal_id = f"internal_{urlsafe_short_hash()}" - payment = await create_payment( wallet_id=wallet_id, checking_id=internal_id, @@ -23,45 +22,64 @@ async def update_wallet_balance(wallet_id: str, amount: int): ) # manually send this for now await internal_invoice_queue.put(internal_id) - return payment -async def get_admin_settings() -> AdminSettings: +async def get_admin_settings() -> Optional[AdminSettings]: row = await db.fetchone("SELECT * FROM admin.settings") - admin_settings = AdminSettings( - **row, lnbits_allowed_funding_sources=settings.lnbits_allowed_funding_sources + if not row: + return None + return AdminSettings( + lnbits_allowed_funding_sources=settings.lnbits_allowed_funding_sources, **row ) - for key, _ in row.items(): - if hasattr(admin_settings, key): - setattr(admin_settings, key, getattr(settings, key)) - return admin_settings - - -async def update_admin_settings(data: UpdateSettings) -> Optional[AdminSettings]: - fields = [] - # TODO: issue typens? - # somehow data, is type dict, but should be type UpdateSettings - # for key, value in data.dict().items(): #type: ignore - for key, value in data.items(): # type: ignore - if not key in readonly_variables: - setattr(settings, key, value) - if type(value) == list: - joined = ",".join(value) - fields.append(f"{key} = '{joined}'") - if type(value) == int or type(value) == float: - fields.append(f"{key} = {value}") - if type(value) == bool: - fields.append(f"{key} = {'true' if value else 'false'}") - if type(value) == str: - value = value.replace("'", "") - fields.append(f"{key} = '{value}'") - q = ", ".join(fields) - await db.execute(f"UPDATE admin.settings SET {q}") - row = await db.fetchone("SELECT * FROM admin.settings") - assert row, "Newly updated settings couldn't be retrieved" - return AdminSettings(**row) if row else None async def delete_admin_settings(): await db.execute("DELETE FROM admin.settings") + + +async def update_admin_settings(data: UpdateSettings): + # TODO why are those field here, they are not in UpdateSettings + # TODO: why is UpdateSettings of type dict here? thats why type:ignore is needed + data.pop("lnbits_allowed_funding_sources") # type: ignore + data.pop("super_user") # type: ignore + q, values = get_q_and_values(data) + await db.execute(f"UPDATE admin.settings SET {q}", (values,)) # type: ignore + + +def get_q_and_values(data): + keys = [] + values = [] + for key, value in data.items(): + setattr(settings, key, value) + keys.append(f"{key} = ?") + if type(value) == list: + value = ",".join(value) + values.append(value) + return ", ".join(keys), values + + +async def create_admin_settings(): + account = await create_account() + settings.super_user = account.id + keys = [] + values = "" + for key, value in settings.dict(exclude_none=True).items(): + if not key in readonly_variables: + keys.append(key) + if type(value) == list: + joined = ",".join(value) + values += f"'{joined}'" + if type(value) == int or type(value) == float: + values += str(value) + if type(value) == bool: + values += "true" if value else "false" + if type(value) == str: + value = value.replace("'", "") + values += f"'{value}'" + values += "," + q = ", ".join(keys) + v = values.rstrip(",") + + sql = f"INSERT INTO admin.settings ({q}) VALUES ({v})" + await db.execute(sql) diff --git a/lnbits/extensions/admin/migrations.py b/lnbits/extensions/admin/migrations.py index 1ad1cea6d..434ba200e 100644 --- a/lnbits/extensions/admin/migrations.py +++ b/lnbits/extensions/admin/migrations.py @@ -15,8 +15,6 @@ async def m001_create_admin_settings_table(db): lnbits_ad_space TEXT, lnbits_ad_space_title TEXT, lnbits_ad_space_enabled BOOLEAN, - lnbits_data_folder TEXT, - lnbits_database_url TEXT, lnbits_force_https TEXT, lnbits_reserve_fee_min TEXT, lnbits_reserve_fee_percent TEXT, @@ -31,9 +29,13 @@ async def m001_create_admin_settings_table(db): corelightning_rpc TEXT, eclair_url TEXT, eclair_pass TEXT, + lnd_cert TEXT, + lnd_admin_macaroon TEXT, + lnd_invoice_macaroon TEXT, lnd_rest_endpoint TEXT, lnd_rest_cert TEXT, lnd_rest_macaroon TEXT, + lnd_rest_macaroon_encrypted TEXT, lnd_grpc_endpoint TEXT, lnd_grpc_cert TEXT, lnd_grpc_port INTEGER, diff --git a/lnbits/extensions/admin/models.py b/lnbits/extensions/admin/models.py index 938ff2348..5793ff2d6 100644 --- a/lnbits/extensions/admin/models.py +++ b/lnbits/extensions/admin/models.py @@ -1,10 +1,10 @@ from typing import List, Optional from fastapi import Query -from pydantic import BaseModel, validator +from pydantic import BaseModel, Extra, validator -class UpdateSettings(BaseModel): +class UpdateSettings(BaseModel, extra=Extra.forbid): @validator( "lnbits_admin_users", "lnbits_allowed_users", @@ -73,6 +73,11 @@ class UpdateSettings(BaseModel): lntips_admin_key: str = Query(None) lntips_invoice_key: str = Query(None) + boltz_mempool_space_url: str = Query(None) + boltz_mempool_space_url_ws: str = Query(None) + boltz_network: str = Query(None) + boltz_url: str = Query(None) + class AdminSettings(UpdateSettings): lnbits_allowed_funding_sources: Optional[List[str]] diff --git a/lnbits/extensions/admin/views_api.py b/lnbits/extensions/admin/views_api.py index 43392ff3c..42887aed7 100644 --- a/lnbits/extensions/admin/views_api.py +++ b/lnbits/extensions/admin/views_api.py @@ -1,6 +1,7 @@ from http import HTTPStatus +from typing import Optional -from fastapi import Body +from fastapi import Body, Query from fastapi.params import Depends from starlette.exceptions import HTTPException @@ -27,7 +28,7 @@ async def api_restart_server() -> dict[str, str]: @admin_ext.get("/api/v1/settings/", dependencies=[Depends(check_admin)]) -async def api_get_settings() -> AdminSettings: +async def api_get_settings() -> Optional[AdminSettings]: return await get_admin_settings() @@ -52,12 +53,9 @@ async def api_update_balance( @admin_ext.put( "/api/v1/settings/", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)] ) -async def api_update_settings( - data: UpdateSettings = Body(...), -): - settings = await update_admin_settings(data) - if settings: - return {"status": "Success", "settings": settings.dict()} +async def api_update_settings(data: UpdateSettings): + await update_admin_settings(data) + return {"status": "Success"} @admin_ext.delete( diff --git a/lnbits/settings.py b/lnbits/settings.py index cccfe95d7..4d9f1dd78 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -25,6 +25,8 @@ readonly_variables = [ "host", "port", "debug", + "lnbits_data_folder", + "lnbits_database_url", "lnbits_allowed_funding_sources", "lnbits_admin_extensions", "lnbits_saas_secret", @@ -70,7 +72,7 @@ class Settings(BaseSettings): default=["classic", "flamingo", "mint", "salvador", "monochrome", "autumn"] ) lnbits_custom_logo: str = Field(default=None) - lnbits_ad_space_title: str = Field(default="Suported by") + lnbits_ad_space_title: str = Field(default="Supported by") lnbits_ad_space: str = Field( default="https://shop.lnbits.com/;/static/images/lnbits-shop-light.png;/static/images/lnbits-shop-dark.png" ) # sneaky sneaky @@ -199,25 +201,30 @@ def set_cli_settings(**kwargs): async def check_admin_settings(): + if settings.lnbits_admin_ui: - ext_db = importlib.import_module(f"lnbits.extensions.admin").db - async with ext_db.connect() as db: - row = await db.fetchone("SELECT * FROM admin.settings") - # create new settings if table is empty - if not row or len(row) == 0: - logger.warning( - "admin.settings empty. inserting new settings and creating admin account" - ) - row = await create_admin_settings(db) + # if not imported here, circular import error + from lnbits.extensions.admin.crud import ( + create_admin_settings, + get_admin_settings, + ) - # setting settings from database into memory - from lnbits.extensions.admin.models import AdminSettings + # ext_db = importlib.import_module(f"lnbits.extensions.admin").db + # async with ext_db.connect() as db: + # row = await db.fetchone("SELECT * FROM admin.settings") - sets = AdminSettings( - **row, - lnbits_allowed_funding_sources=settings.lnbits_allowed_funding_sources, + sets = await get_admin_settings() + # create new settings if table is empty + if not sets: + logger.warning( + "admin.settings empty. inserting new settings and creating admin account" ) + await create_admin_settings() + logger.warning("initialized admin.settings from enviroment variables.") + sets = await get_admin_settings() + + if sets: for key, value in sets.dict().items(): if not key in readonly_variables: try: @@ -225,22 +232,24 @@ async def check_admin_settings(): except: logger.error(f"error overriding setting: {key}, value: {value}") - # printing settings for debugging - logger.debug(f"Admin settings:") - for key, value in settings.dict(exclude_none=True).items(): - logger.debug(f"{key}: {value}") + # printing settings for debugging + logger.debug(f"Admin settings:") + for key, value in settings.dict(exclude_none=True).items(): + logger.debug(f"{key}: {value}") - http = "https" if settings.lnbits_force_https else "http" - admin_url = f"{http}://{settings.host}:{settings.port}/wallet?usr={settings.super_user}" - logger.success(f"✔️ Access admin user account at: {admin_url}") + http = "https" if settings.lnbits_force_https else "http" + admin_url = ( + f"{http}://{settings.host}:{settings.port}/wallet?usr={settings.super_user}" + ) + logger.success(f"✔️ Access admin user account at: {admin_url}") - # callback for saas - if ( - settings.lnbits_saas_callback - and settings.lnbits_saas_secret - and settings.lnbits_saas_instance_id - ): - send_admin_user_to_saas() + # callback for saas + if ( + settings.lnbits_saas_callback + and settings.lnbits_saas_secret + and settings.lnbits_saas_instance_id + ): + send_admin_user_to_saas() wallets_module = importlib.import_module("lnbits.wallets") @@ -252,39 +261,6 @@ def get_wallet_class(): return wallet_class() -async def create_admin_settings(db): - - # if not imported here, circular import error - from lnbits.core.crud import create_account - - account = await create_account() - settings.super_user = account.id - keys = [] - values = "" - for key, value in settings.dict(exclude_none=True).items(): - if not key in readonly_variables: - keys.append(key) - if type(value) == list: - joined = ",".join(value) - values += f"'{joined}'" - if type(value) == int or type(value) == float: - values += str(value) - if type(value) == bool: - values += "true" if value else "false" - if type(value) == str: - value = value.replace("'", "") - values += f"'{value}'" - values += "," - q = ", ".join(keys) - v = values.rstrip(",") - sql = f"INSERT INTO admin.settings ({q}) VALUES ({v})" - await db.execute(sql) - logger.warning("initialized admin.settings from enviroment variables.") - row = await db.fetchone("SELECT * FROM admin.settings") - assert row, "Newly updated settings couldn't be retrieved" - return row - - def send_admin_user_to_saas(): if settings.lnbits_saas_callback: with httpx.Client() as client: