From 6a5afe302580c3f44e1c4e5bc04bf444c959efc2 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 15 Feb 2023 15:34:09 +0200 Subject: [PATCH 1/6] feat: stop extension background work on `uninstall` or `upgrade` --- lnbits/core/helpers.py | 10 ++++++++++ lnbits/core/views/api.py | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lnbits/core/helpers.py b/lnbits/core/helpers.py index 214fee2fa..54c3cd353 100644 --- a/lnbits/core/helpers.py +++ b/lnbits/core/helpers.py @@ -2,6 +2,7 @@ import importlib import re from typing import Any +import httpx from loguru import logger from lnbits.db import Connection @@ -42,3 +43,12 @@ async def run_migration(db: Connection, migrations_module: Any, current_version: else: async with core_db.connect() as conn: await update_migration_version(conn, db_name, version) + + +async def stop_extension_work(ext_id: str, user: str): + """Stop background workk for extension (like asyncio.Tasks, WebSockets, etc)""" + async with httpx.AsyncClient() as client: + try: + await client.delete(url=f"/{ext_id}/api/v1?usr={user}") + except Exception as ex: + logger.warning(ex) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 07049b73c..365bc5a12 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -29,7 +29,7 @@ from sse_starlette.sse import EventSourceResponse from starlette.responses import RedirectResponse, StreamingResponse from lnbits import bolt11, lnurl -from lnbits.core.helpers import migrate_extension_database +from lnbits.core.helpers import migrate_extension_database, stop_extension_work from lnbits.core.models import Payment, User, Wallet from lnbits.decorators import ( WalletTypeInfo, @@ -755,6 +755,8 @@ async def api_install_extension( if data.ext_id not in settings.lnbits_deactivated_extensions: settings.lnbits_deactivated_extensions += [data.ext_id] + # call stop while the old routes are still active + await stop_extension_work(data.ext_id, settings.super_user) # mount routes for the new version core_app_extra.register_new_ext_routes(extension) @@ -798,6 +800,9 @@ async def api_uninstall_extension(ext_id: str, user: User = Depends(check_admin) ) try: + # call stop while the old routes are still active + await stop_extension_work(ext_id, settings.super_user) + if ext_id not in settings.lnbits_deactivated_extensions: settings.lnbits_deactivated_extensions += [ext_id] From 6540cd9d764ca9904e34f6230ca4e0d5f8a9aa50 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 15 Feb 2023 15:45:28 +0200 Subject: [PATCH 2/6] feat: add endpoint to stop background tasks --- lnbits/extensions/lnurlp/__init__.py | 5 ++++- lnbits/extensions/lnurlp/views_api.py | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/lnurlp/__init__.py b/lnbits/extensions/lnurlp/__init__.py index f5ea0cd29..aa13bb921 100644 --- a/lnbits/extensions/lnurlp/__init__.py +++ b/lnbits/extensions/lnurlp/__init__.py @@ -1,4 +1,5 @@ import asyncio +from typing import List from fastapi import APIRouter from fastapi.staticfiles import StaticFiles @@ -16,6 +17,7 @@ lnurlp_static_files = [ "name": "lnurlp_static", } ] +scheduled_tasks: List[asyncio.Task] = [] lnurlp_ext: APIRouter = APIRouter(prefix="/lnurlp", tags=["lnurlp"]) @@ -32,4 +34,5 @@ from .views_api import * # noqa: F401,F403 def lnurlp_start(): loop = asyncio.get_event_loop() - loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) + task = loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) + scheduled_tasks.append(task) diff --git a/lnbits/extensions/lnurlp/views_api.py b/lnbits/extensions/lnurlp/views_api.py index badaaebfa..b4af29493 100644 --- a/lnbits/extensions/lnurlp/views_api.py +++ b/lnbits/extensions/lnurlp/views_api.py @@ -1,4 +1,5 @@ import json +from asyncio.log import logger from http import HTTPStatus from fastapi import Depends, Query, Request @@ -6,10 +7,10 @@ from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl from starlette.exceptions import HTTPException from lnbits.core.crud import get_user -from lnbits.decorators import WalletTypeInfo, get_key_type +from lnbits.decorators import WalletTypeInfo, check_admin, get_key_type from lnbits.utils.exchange_rates import currencies, get_fiat_rate_satoshis -from . import lnurlp_ext +from . import lnurlp_ext, scheduled_tasks from .crud import ( create_pay_link, delete_pay_link, @@ -166,3 +167,14 @@ async def api_check_fiat_rate(currency): rate = None return {"rate": rate} + + +@lnurlp_ext.delete("/api/v1", status_code=HTTPStatus.OK) +async def api_stop(wallet: WalletTypeInfo = Depends(check_admin)): + for t in scheduled_tasks: + try: + t.cancel() + except Exception as ex: + logger.warning(ex) + + return {"success": True} From e3ac70000526314258ce643ee80176291c00e8a6 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 15 Feb 2023 17:25:58 +0200 Subject: [PATCH 3/6] fix: extension `DELETE` call --- lnbits/core/helpers.py | 16 +++++++++++++--- lnbits/core/views/api.py | 14 +++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lnbits/core/helpers.py b/lnbits/core/helpers.py index 54c3cd353..c40f031e8 100644 --- a/lnbits/core/helpers.py +++ b/lnbits/core/helpers.py @@ -7,6 +7,7 @@ from loguru import logger from lnbits.db import Connection from lnbits.extension_manager import Extension +from lnbits.settings import settings from . import db as core_db from .crud import update_migration_version @@ -45,10 +46,19 @@ async def run_migration(db: Connection, migrations_module: Any, current_version: await update_migration_version(conn, db_name, version) -async def stop_extension_work(ext_id: str, user: str): - """Stop background workk for extension (like asyncio.Tasks, WebSockets, etc)""" +async def stop_extension_background_work(ext_id: str, user: str): + """ + Stop background workk for extension (like asyncio.Tasks, WebSockets, etc) + It tries first to call the endpoint using `http` and if ti fails it tries using `https` + """ async with httpx.AsyncClient() as client: try: - await client.delete(url=f"/{ext_id}/api/v1?usr={user}") + url = f"http://{settings.host}:{settings.port}/{ext_id}/api/v1?usr={user}" + await client.delete(url) except Exception as ex: logger.warning(ex) + try: + # try https + url = f"https://{settings.host}:{settings.port}/{ext_id}/api/v1?usr={user}" + except Exception as ex: + logger.warning(ex) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 365bc5a12..b6c083cea 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -29,7 +29,10 @@ from sse_starlette.sse import EventSourceResponse from starlette.responses import RedirectResponse, StreamingResponse from lnbits import bolt11, lnurl -from lnbits.core.helpers import migrate_extension_database, stop_extension_work +from lnbits.core.helpers import ( + migrate_extension_database, + stop_extension_background_work, +) from lnbits.core.models import Payment, User, Wallet from lnbits.decorators import ( WalletTypeInfo, @@ -729,7 +732,6 @@ async def websocket_update_get(item_id: str, data: str): async def api_install_extension( data: CreateExtension, user: User = Depends(check_admin) ): - release = await InstallableExtension.get_extension_release( data.ext_id, data.source_repo, data.archive ) @@ -752,11 +754,13 @@ async def api_install_extension( await migrate_extension_database(extension, db_version) await add_installed_extension(ext_info) + + # call stop while the old routes are still active + await stop_extension_background_work(data.ext_id, user.id) + if data.ext_id not in settings.lnbits_deactivated_extensions: settings.lnbits_deactivated_extensions += [data.ext_id] - # call stop while the old routes are still active - await stop_extension_work(data.ext_id, settings.super_user) # mount routes for the new version core_app_extra.register_new_ext_routes(extension) @@ -801,7 +805,7 @@ async def api_uninstall_extension(ext_id: str, user: User = Depends(check_admin) try: # call stop while the old routes are still active - await stop_extension_work(ext_id, settings.super_user) + await stop_extension_background_work(ext_id, user.id) if ext_id not in settings.lnbits_deactivated_extensions: settings.lnbits_deactivated_extensions += [ext_id] From ca28420e4832010a669499756bdc0da43c4a4d38 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 15 Feb 2023 17:26:08 +0200 Subject: [PATCH 4/6] chore: code format --- lnbits/extensions/lnurlp/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lnbits/extensions/lnurlp/models.py b/lnbits/extensions/lnurlp/models.py index 4ee82aad5..de66d4064 100644 --- a/lnbits/extensions/lnurlp/models.py +++ b/lnbits/extensions/lnurlp/models.py @@ -61,9 +61,9 @@ class PayLink(BaseModel): def success_action(self, payment_hash: str) -> Optional[Dict]: if self.success_url: url: ParseResult = urlparse(self.success_url) - #qs = parse_qs(url.query) - #setattr(qs, "payment_hash", payment_hash) - #url = url._replace(query=urlencode(qs, doseq=True)) + # qs = parse_qs(url.query) + # setattr(qs, "payment_hash", payment_hash) + # url = url._replace(query=urlencode(qs, doseq=True)) return { "tag": "url", "description": self.success_text or "~", From 7ef4c18b56340df0bdf849137678637651cee32e Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 15 Feb 2023 17:31:10 +0200 Subject: [PATCH 5/6] doc: update coments --- lnbits/core/helpers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lnbits/core/helpers.py b/lnbits/core/helpers.py index c40f031e8..7f3554010 100644 --- a/lnbits/core/helpers.py +++ b/lnbits/core/helpers.py @@ -48,8 +48,9 @@ async def run_migration(db: Connection, migrations_module: Any, current_version: async def stop_extension_background_work(ext_id: str, user: str): """ - Stop background workk for extension (like asyncio.Tasks, WebSockets, etc) - It tries first to call the endpoint using `http` and if ti fails it tries using `https` + Stop background work for extension (like asyncio.Tasks, WebSockets, etc) + Extensions SHOULD expoze a DELETE enpoint at the root level of their API. + This function tries first to call the endpoint using `http` and if if fails it tries using `https`. """ async with httpx.AsyncClient() as client: try: From d32ad082c7b6db62f2ba3b8506bf6228eecc618b Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Wed, 15 Feb 2023 17:32:49 +0200 Subject: [PATCH 6/6] chore: fix typos --- lnbits/core/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/core/helpers.py b/lnbits/core/helpers.py index 7f3554010..6769d5851 100644 --- a/lnbits/core/helpers.py +++ b/lnbits/core/helpers.py @@ -48,8 +48,8 @@ async def run_migration(db: Connection, migrations_module: Any, current_version: async def stop_extension_background_work(ext_id: str, user: str): """ - Stop background work for extension (like asyncio.Tasks, WebSockets, etc) - Extensions SHOULD expoze a DELETE enpoint at the root level of their API. + Stop background work for extension (like asyncio.Tasks, WebSockets, etc). + Extensions SHOULD expose a DELETE enpoint at the root level of their API. This function tries first to call the endpoint using `http` and if if fails it tries using `https`. """ async with httpx.AsyncClient() as client: