This commit is contained in:
Arc
2026-05-25 14:56:20 +03:00
committed by Vlad Stan
parent 7d20c81ff9
commit 18c31280ef
5 changed files with 157 additions and 219 deletions
+16 -52
View File
@@ -1,7 +1,6 @@
import asyncio
import glob
import importlib
import json
import os
import shutil
import sys
@@ -25,7 +24,7 @@ from lnbits.core.crud import (
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 get_extension_type, migrate_extension_database
from lnbits.core.models.notifications import NotificationType
from lnbits.core.services.extensions import deactivate_extension, get_valid_extensions
from lnbits.core.services.notifications import enqueue_admin_notification
@@ -436,14 +435,24 @@ def register_ext_tasks(ext: Extension) -> None:
def register_ext_routes(app: FastAPI, ext: Extension) -> None:
"""Register FastAPI routes for extension."""
if ext.extension_type != "wasm":
ext.extension_type = _load_extension_type(ext.code) or ext.extension_type
ext.extension_type = get_extension_type(ext.code) or ext.extension_type
if ext.extension_type == "wasm":
settings.activate_extension_paths(ext.code, ext.upgrade_hash, [])
try:
module = importlib.import_module(
"lnbits.extensions.wasm.wasm_host.extension_host"
)
register_wasm_ext_routes = getattr(module, "register_wasm_ext_routes", None)
register_wasm_ext_routes = None
for module_name in (
"wasm.wasm_host.extension_host",
"lnbits.extensions.wasm.wasm_host.extension_host",
):
try:
module = importlib.import_module(module_name)
except Exception:
continue
register_wasm_ext_routes = getattr(
module, "register_wasm_ext_routes", None
)
if register_wasm_ext_routes is not None:
break
except Exception: # pragma: no cover - optional parent extension
logger.error(
"WASM host extension not installed; cannot register wasm extension "
@@ -483,51 +492,6 @@ def register_ext_routes(app: FastAPI, ext: Extension) -> None:
app.include_router(router=ext_route, prefix=prefix)
def _load_extension_type(ext_id: str) -> str | None:
base_dirs = [
Path(settings.lnbits_extensions_path, "extensions", ext_id),
Path(settings.lnbits_extensions_path, ext_id),
Path(settings.lnbits_path, "lnbits", "extensions", ext_id),
Path(settings.lnbits_path, "extensions", ext_id),
Path.cwd() / "lnbits" / "extensions" / ext_id,
Path.cwd() / "extensions" / ext_id,
]
for base in base_dirs:
try:
conf_path = base / "config.json"
if conf_path.is_file():
with open(conf_path) as json_file:
config_json = json.load(json_file)
ext_type = config_json.get("extension_type")
if ext_type:
return ext_type
except Exception as exc:
logger.debug(
"Failed to read extension config.json for '{}' in '{}': {}",
ext_id,
base,
exc,
)
continue
for base in base_dirs:
try:
wasm_dir = base / "wasm"
if (wasm_dir / "module.wasm").is_file() or (
wasm_dir / "module.wat"
).is_file():
return "wasm"
except Exception as exc:
logger.debug(
"Failed to probe wasm files for '{}' in '{}': {}",
ext_id,
base,
exc,
)
continue
return None
async def check_and_register_extensions(app: FastAPI) -> None:
await check_installed_extensions(app)
for ext in await get_valid_extensions(False):
+48 -26
View File
@@ -1,5 +1,7 @@
import importlib
import json
import re
from pathlib import Path
from typing import Any
from urllib.parse import urlparse
from uuid import UUID
@@ -36,41 +38,61 @@ async def migrate_extension_database(
await run_migration(ext_conn, ext_migrations, ext.id, current_version)
def _is_wasm_extension(ext: InstallableExtension) -> bool:
if ext.meta and getattr(ext.meta, "extension_type", None) == "wasm":
return True
try:
import json
from pathlib import Path
candidate_dirs = [
Path(ext.ext_dir),
Path(settings.lnbits_extensions_path, "extensions", ext.id),
Path(settings.lnbits_extensions_path, ext.id),
Path(settings.lnbits_path, "lnbits", "extensions", ext.id),
Path(settings.lnbits_path, "extensions", ext.id),
Path.cwd() / "lnbits" / "extensions" / ext.id,
Path.cwd() / "extensions" / ext.id,
def get_extension_type(ext_id: str, ext_dir: str | None = None) -> str | None:
candidate_dirs = []
if ext_dir:
candidate_dirs.append(Path(ext_dir))
candidate_dirs.extend(
[
Path(settings.lnbits_extensions_path, "extensions", ext_id),
Path(settings.lnbits_extensions_path, ext_id),
Path(settings.lnbits_path, "lnbits", "extensions", ext_id),
Path(settings.lnbits_path, "extensions", ext_id),
Path.cwd() / "lnbits" / "extensions" / ext_id,
Path.cwd() / "extensions" / ext_id,
]
for base in candidate_dirs:
conf_path = Path(base, "config.json")
)
for base in candidate_dirs:
try:
conf_path = base / "config.json"
if not conf_path.is_file():
continue
with open(conf_path) as json_file:
config_json = json.load(json_file)
if config_json.get("extension_type") == "wasm":
return True
for base in candidate_dirs:
wasm_dir = Path(base, "wasm")
ext_type = config_json.get("extension_type")
if ext_type:
return ext_type
except Exception as exc:
logger.debug(
"Failed to read extension config.json for '{}' in '{}': {}",
ext_id,
base,
exc,
)
for base in candidate_dirs:
try:
wasm_dir = base / "wasm"
if (wasm_dir / "module.wasm").is_file() or (
wasm_dir / "module.wat"
).is_file():
return True
except Exception as exc:
logger.debug(f"Failed to load extension config for '{ext.id}': {exc!s}")
return "wasm"
except Exception as exc:
logger.debug(
"Failed to probe wasm files for '{}' in '{}': {}",
ext_id,
base,
exc,
)
return None
return False
def _is_wasm_extension(ext: InstallableExtension) -> bool:
if ext.meta and getattr(ext.meta, "extension_type", None) == "wasm":
return True
return get_extension_type(ext.id, ext.ext_dir) == "wasm"
async def run_migration(
+2 -50
View File
@@ -1,8 +1,6 @@
import json
import sys
import traceback
from http import HTTPStatus
from pathlib import Path
import httpx
from bolt11 import decode as bolt11_decode
@@ -13,6 +11,7 @@ from loguru import logger
from lnbits.core.crud.extensions import get_user_extensions
from lnbits.core.crud.wallets import get_wallets_ids
from lnbits.core.db import db
from lnbits.core.helpers import get_extension_type
from lnbits.core.models import (
SimpleStatus,
)
@@ -63,53 +62,6 @@ from ..crud import (
update_user_extension,
)
def _load_extension_type(ext_id: str) -> str:
base_dirs = [
Path(settings.lnbits_extensions_path, "extensions", ext_id),
Path(settings.lnbits_extensions_path, ext_id),
Path(settings.lnbits_path, "lnbits", "extensions", ext_id),
Path(settings.lnbits_path, "extensions", ext_id),
Path.cwd() / "lnbits" / "extensions" / ext_id,
Path.cwd() / "extensions" / ext_id,
]
for base in base_dirs:
try:
conf_path = base / "config.json"
if not conf_path.is_file():
continue
with open(conf_path) as json_file:
config_json = json.load(json_file)
ext_type = config_json.get("extension_type")
if ext_type:
return ext_type
except Exception as exc:
logger.debug(
"Failed to read extension config.json for '{}' in '{}': {}",
ext_id,
base,
exc,
)
continue
for base in base_dirs:
try:
wasm_dir = base / "wasm"
if (wasm_dir / "module.wasm").is_file() or (
wasm_dir / "module.wat"
).is_file():
return "wasm"
except Exception as exc:
logger.debug(
"Failed to probe wasm files for '{}' in '{}': {}",
ext_id,
base,
exc,
)
continue
return "python"
extension_router = APIRouter(
tags=["Extension Managment"],
prefix="/api/v1/extension",
@@ -645,7 +597,7 @@ async def extensions(account_id: AccountId = Depends(check_account_id_exists)):
"isPaymentRequired": ext.requires_payment,
"inProgress": False,
"selectedForUpdate": False,
"extensionType": _load_extension_type(ext.id),
"extensionType": get_extension_type(ext.id) or "python",
}
for ext in installable_exts
]