diff --git a/lnbits/app.py b/lnbits/app.py index 97fca316..5625241e 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -16,7 +16,11 @@ from slowapi import Limiter from slowapi.util import get_remote_address from starlette.middleware.sessions import SessionMiddleware -from lnbits.core.crud import get_dbversions, get_installed_extensions +from lnbits.core.crud import ( + get_dbversions, + get_installed_extensions, + update_installed_extension_state, +) from lnbits.core.helpers import migrate_extension_database from lnbits.core.tasks import ( # watchdog_task killswitch_task, @@ -42,7 +46,6 @@ from .core import init_core_routers from .core.db import core_app_extra from .core.services import check_admin_settings, check_webpush_settings from .core.views.extension_api import add_installed_extension -from .core.views.generic import update_installed_extension_state from .extension_manager import ( Extension, InstallableExtension, diff --git a/lnbits/commands.py b/lnbits/commands.py index 0ce5008e..769ef285 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -29,7 +29,6 @@ from .core.crud import ( delete_wallet_by_id, delete_wallet_payment, get_dbversions, - get_inactive_extensions, get_installed_extension, get_installed_extensions, get_payments, @@ -154,6 +153,7 @@ async def migrate_databases(): # `installed_extensions` table has been created await load_disabled_extension_list() + # todo: revisit, use installed extensions for ext in get_valid_extensions(False): current_version = current_versions.get(ext.code, 0) try: @@ -315,8 +315,8 @@ async def check_invalid_payments( async def load_disabled_extension_list() -> None: """Update list of extensions that have been explicitly disabled""" - inactive_extensions = await get_inactive_extensions() - settings.lnbits_deactivated_extensions.update(inactive_extensions) + inactive_extensions = await get_installed_extensions(active=False) + settings.lnbits_deactivated_extensions.update([e.id for e in inactive_extensions]) @extensions.command("list") diff --git a/lnbits/core/__init__.py b/lnbits/core/__init__.py index aab1b524..a35c07a9 100644 --- a/lnbits/core/__init__.py +++ b/lnbits/core/__init__.py @@ -7,7 +7,7 @@ from .views.auth_api import auth_router from .views.extension_api import extension_router # this compat is needed for usermanager extension -from .views.generic import generic_router, update_user_extension +from .views.generic import generic_router from .views.node_api import node_router, public_node_router, super_node_router from .views.payment_api import payment_router from .views.public_api import public_router diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 97199488..7e51ed35 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -9,7 +9,12 @@ from passlib.context import CryptContext from lnbits.core.db import db from lnbits.db import DB_TYPE, SQLITE, Connection, Database, Filters, Page -from lnbits.extension_manager import InstallableExtension +from lnbits.extension_manager import ( + InstallableExtension, + PayToEnableInfo, + UserExtension, + UserExtensionInfo, +) from lnbits.settings import ( AdminSettings, EditableSettings, @@ -364,6 +369,7 @@ async def add_installed_extension( "installed_release": ( dict(ext.installed_release) if ext.installed_release else None ), + "pay_to_enable": (dict(ext.pay_to_enable) if ext.pay_to_enable else None), "dependencies": ext.dependencies, "payments": [dict(p) for p in ext.payments] if ext.payments else None, } @@ -373,8 +379,8 @@ async def add_installed_extension( await (conn or db).execute( """ INSERT INTO installed_extensions - (id, version, name, short_description, icon, stars, meta) - VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO UPDATE SET + (id, version, name, active, short_description, icon, stars, meta) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO UPDATE SET (version, name, active, short_description, icon, stars, meta) = (?, ?, ?, ?, ?, ?, ?) """, @@ -382,13 +388,14 @@ async def add_installed_extension( ext.id, version, ext.name, + ext.active, ext.short_description, ext.icon, ext.stars, json.dumps(meta), version, ext.name, - False, + ext.active, ext.short_description, ext.icon, ext.stars, @@ -408,6 +415,17 @@ async def update_installed_extension_state( ) +async def update_extension_pay_to_enable( + ext_id: str, payment_info: PayToEnableInfo, conn: Optional[Connection] = None +) -> None: + ext = await get_installed_extension(ext_id, conn) + if not ext: + return + ext.pay_to_enable = payment_info + + await add_installed_extension(ext, conn) + + async def delete_installed_extension( *, ext_id: str, conn: Optional[Connection] = None ) -> None: @@ -450,21 +468,44 @@ async def get_installed_extension( async def get_installed_extensions( + active: Optional[bool] = None, conn: Optional[Connection] = None, ) -> List["InstallableExtension"]: rows = await (conn or db).fetchall( "SELECT * FROM installed_extensions", (), ) - return [InstallableExtension.from_row(row) for row in rows] + all_extensions = [InstallableExtension.from_row(row) for row in rows] + if active is None: + return all_extensions + + return [e for e in all_extensions if e.active == active] -async def get_inactive_extensions(*, conn: Optional[Connection] = None) -> List[str]: - inactive_extensions = await (conn or db).fetchall( - """SELECT id FROM installed_extensions WHERE NOT active""", - (), +async def get_user_extension( + user_id: str, extension: str, conn: Optional[Connection] = None +) -> Optional[UserExtension]: + row = await (conn or db).fetchone( + """ + SELECT extension, active, extra as _extra FROM extensions + WHERE "user" = ? AND extension = ? + """, + (user_id, extension), ) - return [ext[0] for ext in inactive_extensions] + return UserExtension.from_row(row) if row else None + + +async def get_user_extensions( + user_id: str, conn: Optional[Connection] = None +) -> List[UserExtension]: + rows = await (conn or db).fetchall( + """ + SELECT extension, active, extra as _extra FROM extensions + WHERE "user" = ? + """, + (user_id,), + ) + return [UserExtension.from_row(row) for row in rows] async def update_user_extension( @@ -489,6 +530,22 @@ async def get_user_active_extensions_ids( return [e[0] for e in rows] +async def update_user_extension_extra( + user_id: str, + extension: str, + extra: UserExtensionInfo, + conn: Optional[Connection] = None, +) -> None: + extra_json = json.dumps(dict(extra)) + await (conn or db).execute( + """ + INSERT INTO extensions ("user", extension, extra) VALUES (?, ?, ?) + ON CONFLICT ("user", extension) DO UPDATE SET extra = ? + """, + (user_id, extension, extra_json, extra_json), + ) + + # wallets # ------- diff --git a/lnbits/core/helpers.py b/lnbits/core/helpers.py index 0c1982ee..f032c5c4 100644 --- a/lnbits/core/helpers.py +++ b/lnbits/core/helpers.py @@ -77,7 +77,9 @@ async def _stop_extension_background_work(ext_id) -> bool: stop_fn_name = next((fn for fn in stop_fns if hasattr(old_module, fn)), None) assert stop_fn_name, "No stop function found for '{ext.module_name}'" - await getattr(old_module, stop_fn_name)() + stop_fn = getattr(old_module, stop_fn_name) + if stop_fn: + await stop_fn() logger.info(f"Stopped background work for extension '{ext.module_name}'.") except Exception as ex: diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index f65f112c..c9c44639 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -513,3 +513,10 @@ async def m019_balances_view_based_on_wallets(db): GROUP BY apipayments.wallet """ ) + + +async def m020_add_column_column_to_user_extensions(db): + """ + Adds extra column to user extensions. + """ + await db.execute("ALTER TABLE extensions ADD COLUMN extra TEXT") diff --git a/lnbits/core/services.py b/lnbits/core/services.py index b3cf795f..e9237aa2 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -651,7 +651,7 @@ def fee_reserve_total(amount_msat: int, internal: bool = False) -> int: async def send_payment_notification(wallet: Wallet, payment: Payment): await websocket_updater( - wallet.id, + wallet.inkey, json.dumps( { "wallet_balance": wallet.balance, @@ -660,6 +660,10 @@ async def send_payment_notification(wallet: Wallet, payment: Payment): ), ) + await websocket_updater( + payment.payment_hash, json.dumps({"pending": payment.pending}) + ) + async def update_wallet_balance(wallet_id: str, amount: int): payment_hash, _ = await create_invoice( diff --git a/lnbits/core/templates/core/extensions.html b/lnbits/core/templates/core/extensions.html index ac8fe46c..a8fca553 100644 --- a/lnbits/core/templates/core/extensions.html +++ b/lnbits/core/templates/core/extensions.html @@ -188,10 +188,7 @@ v-if="user.extensions.includes(extension.id) && extension.isActive && extension.isInstalled" flat color="grey-5" - type="a" - :href="['{{ - url_for('install.extensions') - }}', '?disable=', extension.id].join('')" + @click="disableExtension(extension)" :label="$t('disable')" > + @@ -215,7 +210,7 @@ > - +
@@ -352,10 +347,30 @@
- -
-
-
+ + + + + + +
@@ -479,7 +494,7 @@ @@ -556,33 +571,189 @@ +
+ + + +
-
- - - +
+ + + + +
+ + + +
+ + + + +

+ +

+

+ + + +

+
+ +
+ +
+ +
+
+
+
+ +
+
+ +
+
+
+ + +
+
+
+ + + + + + + + +
+
+
+
+
+
{% endblock %} {% block scripts %} {{ window_vars(user) }}