From 0547abe54af6edabab0d8e80604a0bcbcf113a13 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Thu, 28 Nov 2024 12:51:29 +0200 Subject: [PATCH] [feat] install extensions from dir (#2781) * feat: search ext dir and install * fix: `upgrade_hash` logic * chore: clean-up `upgrade_hash` logic * fix: screen refresh * fix: ignore non-ext dirs * fix: ext migration --- lnbits/app.py | 20 +++++++++++ lnbits/core/models/extensions.py | 41 +++++++++++++++++++--- lnbits/core/templates/core/extensions.html | 8 +++++ lnbits/settings.py | 8 ----- 4 files changed, 64 insertions(+), 13 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 2487edba..d9f2d9f6 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -21,6 +21,7 @@ from lnbits.core.crud import ( get_installed_extensions, update_installed_extension_state, ) +from lnbits.core.crud.extensions import create_installed_extension 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 @@ -265,6 +266,25 @@ async def build_all_installed_extensions_list( installed_extensions = await get_installed_extensions() settings.lnbits_all_extensions_ids = {e.id for e in installed_extensions} + for ext_dir in Path(settings.lnbits_extensions_path, "extensions").iterdir(): + try: + if not ext_dir.is_dir(): + continue + ext_id = ext_dir.name + if ext_id in settings.lnbits_all_extensions_ids: + continue + ext_info = InstallableExtension.from_ext_dir(ext_id) + if not ext_info: + continue + + installed_extensions.append(ext_info) + await create_installed_extension(ext_info) + current_version = await get_db_version(ext_id) + await migrate_extension_database(ext_info, current_version) + + except Exception as e: + logger.warning(e) + for ext_id in settings.lnbits_extensions_default_install: if ext_id in settings.lnbits_all_extensions_ids: continue diff --git a/lnbits/core/models/extensions.py b/lnbits/core/models/extensions.py index bf43ce54..b3a000f6 100644 --- a/lnbits/core/models/extensions.py +++ b/lnbits/core/models/extensions.py @@ -169,11 +169,7 @@ class Extension(BaseModel): name=ext_info.name, short_description=ext_info.short_description, tile=ext_info.icon, - upgrade_hash=( - ext_info.hash - if settings.extension_has_been_activated(ext_info.id) - else "" - ), + upgrade_hash=ext_info.hash if ext_info.ext_upgrade_dir.is_dir() else "", ) @@ -551,6 +547,41 @@ class InstallableExtension(BaseModel): meta=meta, ) + @classmethod + def from_ext_dir(cls, ext_id: str) -> Optional[InstallableExtension]: + try: + conf_path = Path( + settings.lnbits_extensions_path, "extensions", ext_id, "config.json" + ) + if not conf_path.is_file(): + return None + with open(conf_path, "r+") as json_file: + config_json = json.load(json_file) + version = config_json.get("version", "0.0") + + return InstallableExtension( + id=ext_id, + name=config_json.get("name", ext_id), + active=True, + version=version, + short_description=config_json.get("short_description"), + icon=config_json.get("tile"), + meta=ExtensionMeta( + installed_release=ExtensionRelease( + name=ext_id, + version=version, + archive=f"{conf_path}", + source_repo=f"{conf_path}", + min_lnbits_version=config_json.get("min_lnbits_version"), + ) + ), + ) + + except Exception as e: + logger.warning(e) + + return None + @classmethod async def get_installable_extensions( cls, diff --git a/lnbits/core/templates/core/extensions.html b/lnbits/core/templates/core/extensions.html index d67f142b..7d0bf9e6 100644 --- a/lnbits/core/templates/core/extensions.html +++ b/lnbits/core/templates/core/extensions.html @@ -1023,6 +1023,10 @@ }) if (this.uninstallAndDropDb) { this.showDropDb() + } else { + setTimeout(() => { + window.location.reload() + }, 300) } }) .catch(err => { @@ -1051,6 +1055,9 @@ type: 'positive', message: 'Extension DB deleted!' }) + setTimeout(() => { + window.location.reload() + }, 300) }) .catch(err => { LNbits.utils.notifyApiError(err) @@ -1073,6 +1080,7 @@ }) .catch(err => { LNbits.utils.notifyApiError(err) + extension.isActive = false extension.inProgress = false }) }, diff --git a/lnbits/settings.py b/lnbits/settings.py index 4c13b4bf..f6117bb4 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -129,10 +129,6 @@ class InstalledExtensionsSettings(LNbitsSettings): # list of all extension ids lnbits_all_extensions_ids: set[str] = Field(default=[]) - # list of all extension ids that have been activated at least once - # only add to this set, do not remove - lnbits_activated_paths_extensions_ids: set[str] = Field(default=[]) - def find_extension_redirect( self, path: str, req_headers: list[tuple[bytes, bytes]] ) -> Optional[RedirectPath]: @@ -165,15 +161,11 @@ class InstalledExtensionsSettings(LNbitsSettings): self._activate_extension_redirects(ext_id, ext_redirects) self.lnbits_all_extensions_ids.add(ext_id) - self.lnbits_activated_paths_extensions_ids.add(ext_id) def deactivate_extension_paths(self, ext_id: str): self.lnbits_deactivated_extensions.add(ext_id) self._remove_extension_redirects(ext_id) - def extension_has_been_activated(self, ext_id: str) -> bool: - return ext_id in settings.lnbits_activated_paths_extensions_ids - def extension_upgrade_hash(self, ext_id: str) -> str: return settings.lnbits_upgraded_extensions.get(ext_id, "")