From 524a4c921355da141e17f5bae5200f8c5f72d9b9 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Fri, 13 Dec 2024 14:01:54 +0200 Subject: [PATCH] [feat] custom exchange providers (#2797) --- lnbits/app.py | 2 + lnbits/core/crud/settings.py | 4 +- lnbits/core/services/settings.py | 7 +- lnbits/core/tasks.py | 25 +++ .../admin/_tab_exchange_providers.html | 195 ++++++++++++++++++ lnbits/core/templates/admin/index.html | 93 +++++++-- lnbits/core/views/admin_api.py | 12 +- lnbits/core/views/api.py | 8 + lnbits/settings.py | 112 +++++++++- lnbits/static/bundle-components.min.js.old | 1 - lnbits/static/bundle.min.js | 2 +- lnbits/static/i18n/en.js | 1 + lnbits/static/js/admin.js | 161 ++++++++++++++- lnbits/utils/exchange_rates.py | 142 ++++--------- poetry.lock | 28 ++- pyproject.toml | 2 + 16 files changed, 665 insertions(+), 130 deletions(-) create mode 100644 lnbits/core/templates/admin/_tab_exchange_providers.html delete mode 100644 lnbits/static/bundle-components.min.js.old diff --git a/lnbits/app.py b/lnbits/app.py index d9f2d9f6d..e3c46f699 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -26,6 +26,7 @@ from lnbits.core.helpers import migrate_extension_database from lnbits.core.services.extensions import deactivate_extension, get_valid_extensions from lnbits.core.tasks import ( # watchdog_task audit_queue, + collect_exchange_rates_data, killswitch_task, purge_audit_data, wait_for_audit_data, @@ -455,6 +456,7 @@ def register_async_tasks(app: FastAPI): # create_permanent_task(watchdog_task) create_permanent_task(killswitch_task) create_permanent_task(purge_audit_data) + create_permanent_task(collect_exchange_rates_data) # server logs for websocket if settings.lnbits_admin_ui: diff --git a/lnbits/core/crud/settings.py b/lnbits/core/crud/settings.py index 39a0bc809..026eec89d 100644 --- a/lnbits/core/crud/settings.py +++ b/lnbits/core/crud/settings.py @@ -4,6 +4,7 @@ from typing import Any, Optional from loguru import logger from lnbits.core.db import db +from lnbits.db import dict_to_model from lnbits.settings import ( AdminSettings, EditableSettings, @@ -18,7 +19,8 @@ async def get_super_settings() -> Optional[SuperSettings]: if data: super_user = await get_settings_field("super_user") super_user_id = super_user.value if super_user else None - return SuperSettings(**{"super_user": super_user_id, **data}) + settings_dict = {"super_user": super_user_id, **data} + return dict_to_model(settings_dict, SuperSettings) return None diff --git a/lnbits/core/services/settings.py b/lnbits/core/services/settings.py index 08e9f43fe..8fa6c5703 100644 --- a/lnbits/core/services/settings.py +++ b/lnbits/core/services/settings.py @@ -3,6 +3,7 @@ from loguru import logger from py_vapid import Vapid from py_vapid.utils import b64urlencode +from lnbits.db import dict_to_model from lnbits.settings import ( EditableSettings, readonly_variables, @@ -37,14 +38,16 @@ async def check_webpush_settings(): def update_cached_settings(sets_dict: dict): - for key, value in sets_dict.items(): + editable_settings = dict_to_model(sets_dict, EditableSettings) + for key in sets_dict.keys(): if key in readonly_variables: continue if key not in settings.dict().keys(): continue try: + value = getattr(editable_settings, key) setattr(settings, key, value) except Exception: - logger.warning(f"Failed overriding setting: {key}, value: {value}") + logger.warning(f"Failed overriding setting: {key}.") if "super_user" in sets_dict: settings.super_user = sets_dict["super_user"] diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index 2bbdf2912..cf5ffeab6 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -19,6 +19,7 @@ from lnbits.core.services import ( ) from lnbits.settings import get_funding_source, settings from lnbits.tasks import send_push_notification +from lnbits.utils.exchange_rates import btc_rates api_invoice_listeners: Dict[str, asyncio.Queue] = {} audit_queue: asyncio.Queue = asyncio.Queue() @@ -188,3 +189,27 @@ async def purge_audit_data(): # clean every hour await asyncio.sleep(60 * 60) + + +async def collect_exchange_rates_data(): + """ + Collect exchange rates data. Used for monitoring only. + """ + while settings.lnbits_running: + currency = settings.lnbits_default_accounting_currency or "USD" + max_history_size = settings.lnbits_exchange_history_size + sleep_time = settings.lnbits_exchange_history_refresh_interval_seconds + + if sleep_time > 0: + try: + rates = await btc_rates(currency) + if rates: + rates_values = [r[1] for r in rates] + lnbits_rate = sum(rates_values) / len(rates_values) + rates.append(("LNbits", lnbits_rate)) + settings.append_exchange_rate_datapoint(dict(rates), max_history_size) + except Exception as ex: + logger.warning(ex) + else: + sleep_time = 60 + await asyncio.sleep(sleep_time) diff --git a/lnbits/core/templates/admin/_tab_exchange_providers.html b/lnbits/core/templates/admin/_tab_exchange_providers.html new file mode 100644 index 000000000..90d2e2e22 --- /dev/null +++ b/lnbits/core/templates/admin/_tab_exchange_providers.html @@ -0,0 +1,195 @@ + +
Exchange Providers
+ +
+
+
+ +
+
+
+ + + + +
    +
  • + Refresh Interval and History Size are for + historical purposes only. +
  • +
  • These two settings do not affect the live price computation.
  • +
  • + Chart currency: + +
  • +
+
+
+ +
+
+ + +
+
+ + +
+
+ + + + + + + + +
+
+ + +
+
+
+
diff --git a/lnbits/core/templates/admin/index.html b/lnbits/core/templates/admin/index.html index e77e09369..9970d6cd1 100644 --- a/lnbits/core/templates/admin/index.html +++ b/lnbits/core/templates/admin/index.html @@ -63,7 +63,12 @@