[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
This commit is contained in:
parent
f5ccf5c157
commit
0547abe54a
4 changed files with 64 additions and 13 deletions
|
|
@ -21,6 +21,7 @@ from lnbits.core.crud import (
|
||||||
get_installed_extensions,
|
get_installed_extensions,
|
||||||
update_installed_extension_state,
|
update_installed_extension_state,
|
||||||
)
|
)
|
||||||
|
from lnbits.core.crud.extensions import create_installed_extension
|
||||||
from lnbits.core.helpers import migrate_extension_database
|
from lnbits.core.helpers import migrate_extension_database
|
||||||
from lnbits.core.services.extensions import deactivate_extension, get_valid_extensions
|
from lnbits.core.services.extensions import deactivate_extension, get_valid_extensions
|
||||||
from lnbits.core.tasks import ( # watchdog_task
|
from lnbits.core.tasks import ( # watchdog_task
|
||||||
|
|
@ -265,6 +266,25 @@ async def build_all_installed_extensions_list(
|
||||||
installed_extensions = await get_installed_extensions()
|
installed_extensions = await get_installed_extensions()
|
||||||
settings.lnbits_all_extensions_ids = {e.id for e in 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:
|
for ext_id in settings.lnbits_extensions_default_install:
|
||||||
if ext_id in settings.lnbits_all_extensions_ids:
|
if ext_id in settings.lnbits_all_extensions_ids:
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -169,11 +169,7 @@ class Extension(BaseModel):
|
||||||
name=ext_info.name,
|
name=ext_info.name,
|
||||||
short_description=ext_info.short_description,
|
short_description=ext_info.short_description,
|
||||||
tile=ext_info.icon,
|
tile=ext_info.icon,
|
||||||
upgrade_hash=(
|
upgrade_hash=ext_info.hash if ext_info.ext_upgrade_dir.is_dir() else "",
|
||||||
ext_info.hash
|
|
||||||
if settings.extension_has_been_activated(ext_info.id)
|
|
||||||
else ""
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -551,6 +547,41 @@ class InstallableExtension(BaseModel):
|
||||||
meta=meta,
|
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
|
@classmethod
|
||||||
async def get_installable_extensions(
|
async def get_installable_extensions(
|
||||||
cls,
|
cls,
|
||||||
|
|
|
||||||
|
|
@ -1023,6 +1023,10 @@
|
||||||
})
|
})
|
||||||
if (this.uninstallAndDropDb) {
|
if (this.uninstallAndDropDb) {
|
||||||
this.showDropDb()
|
this.showDropDb()
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload()
|
||||||
|
}, 300)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|
@ -1051,6 +1055,9 @@
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
message: 'Extension DB deleted!'
|
message: 'Extension DB deleted!'
|
||||||
})
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload()
|
||||||
|
}, 300)
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
LNbits.utils.notifyApiError(err)
|
LNbits.utils.notifyApiError(err)
|
||||||
|
|
@ -1073,6 +1080,7 @@
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
LNbits.utils.notifyApiError(err)
|
LNbits.utils.notifyApiError(err)
|
||||||
|
extension.isActive = false
|
||||||
extension.inProgress = false
|
extension.inProgress = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -129,10 +129,6 @@ class InstalledExtensionsSettings(LNbitsSettings):
|
||||||
# list of all extension ids
|
# list of all extension ids
|
||||||
lnbits_all_extensions_ids: set[str] = Field(default=[])
|
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(
|
def find_extension_redirect(
|
||||||
self, path: str, req_headers: list[tuple[bytes, bytes]]
|
self, path: str, req_headers: list[tuple[bytes, bytes]]
|
||||||
) -> Optional[RedirectPath]:
|
) -> Optional[RedirectPath]:
|
||||||
|
|
@ -165,15 +161,11 @@ class InstalledExtensionsSettings(LNbitsSettings):
|
||||||
self._activate_extension_redirects(ext_id, ext_redirects)
|
self._activate_extension_redirects(ext_id, ext_redirects)
|
||||||
|
|
||||||
self.lnbits_all_extensions_ids.add(ext_id)
|
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):
|
def deactivate_extension_paths(self, ext_id: str):
|
||||||
self.lnbits_deactivated_extensions.add(ext_id)
|
self.lnbits_deactivated_extensions.add(ext_id)
|
||||||
self._remove_extension_redirects(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:
|
def extension_upgrade_hash(self, ext_id: str) -> str:
|
||||||
return settings.lnbits_upgraded_extensions.get(ext_id, "")
|
return settings.lnbits_upgraded_extensions.get(ext_id, "")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue