mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-01-19 05:33:47 +01:00
feat: install GitHib releases also
This commit is contained in:
parent
41ce316fc6
commit
9d0cedfcb2
@ -75,25 +75,22 @@ async def add_installed_extension(
|
||||
ext_id: str,
|
||||
version: str,
|
||||
active: bool,
|
||||
hash: str,
|
||||
meta: dict,
|
||||
conn: Optional[Connection] = None,
|
||||
) -> None:
|
||||
await (conn or db).execute(
|
||||
"""
|
||||
INSERT INTO installed_extensions (id, version, active, hash, meta) VALUES (?, ?, ?, ?, ?)
|
||||
INSERT INTO installed_extensions (id, version, active, meta) VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT (id) DO
|
||||
UPDATE SET (version, active, hash, meta) = (?, ?, ?, ?)
|
||||
UPDATE SET (version, active, meta) = (?, ?, ?)
|
||||
""",
|
||||
(
|
||||
ext_id,
|
||||
version,
|
||||
active,
|
||||
hash,
|
||||
json.dumps(meta),
|
||||
version,
|
||||
active,
|
||||
hash,
|
||||
json.dumps(meta),
|
||||
),
|
||||
)
|
||||
|
@ -278,7 +278,6 @@ async def m009_create_installed_extensions_table(db):
|
||||
id TEXT PRIMARY KEY,
|
||||
version TEXT NOT NULL,
|
||||
active BOOLEAN DEFAULT false,
|
||||
hash TEXT NOT NULL,
|
||||
meta TEXT NOT NULL DEFAULT '{}'
|
||||
);
|
||||
"""
|
||||
|
@ -176,14 +176,16 @@
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-btn type="a" color="primary unelevated mt-lg pt-lg"
|
||||
<q-btn
|
||||
@click="installExtension(release)"
|
||||
color="primary unelevated mt-lg pt-lg"
|
||||
>Install</q-btn
|
||||
>
|
||||
</q-card-section>
|
||||
<q-separator></q-separator>
|
||||
<div
|
||||
v-if="selectedExtension.details"
|
||||
v-html="selectedExtension.details"
|
||||
v-if="release.details_html"
|
||||
v-html="release.details_html"
|
||||
></div> </q-card
|
||||
></q-expansion-item>
|
||||
</q-list>
|
||||
@ -258,13 +260,19 @@
|
||||
)
|
||||
.filter(extensionNameContains(term))
|
||||
},
|
||||
installExtension: async function (extension) {
|
||||
installExtension: async function (release) {
|
||||
const extension = this.selectedExtension
|
||||
try {
|
||||
extension.inProgress = true
|
||||
await LNbits.api.request(
|
||||
'POST',
|
||||
`/api/v1/extension/${extension.id}/${extension.hash}?usr=${this.g.user.id}`,
|
||||
this.g.user.wallets[0].adminkey
|
||||
`/api/v1/extension?usr=${this.g.user.id}`,
|
||||
this.g.user.wallets[0].adminkey,
|
||||
{
|
||||
ext_id: extension.id,
|
||||
archive: release.archive,
|
||||
source_repo: release.source_repo
|
||||
}
|
||||
)
|
||||
window.location.href = [
|
||||
"{{ url_for('install.extensions') }}",
|
||||
|
@ -41,6 +41,7 @@ from lnbits.decorators import (
|
||||
require_invoice_key,
|
||||
)
|
||||
from lnbits.extension_manger import (
|
||||
CreateExtension,
|
||||
Extension,
|
||||
ExtensionRelease,
|
||||
InstallableExtension,
|
||||
@ -720,13 +721,30 @@ async def websocket_update_get(item_id: str, data: str):
|
||||
return {"sent": False, "data": data}
|
||||
|
||||
|
||||
@core_app.post("/api/v1/extension/{ext_id}/{hash}")
|
||||
@core_app.post("/api/v1/extension")
|
||||
async def api_install_extension(
|
||||
ext_id: str, hash: str, user: User = Depends(check_admin)
|
||||
data: CreateExtension, user: User = Depends(check_admin)
|
||||
):
|
||||
ext_info: InstallableExtension = await InstallableExtension.get_extension_info(
|
||||
ext_id, hash
|
||||
# ext_info: InstallableExtension = await InstallableExtension.get_extension_info(
|
||||
# data.ext_id, data.archive
|
||||
# )
|
||||
|
||||
all_releases: List[
|
||||
ExtensionRelease
|
||||
] = await InstallableExtension.get_extension_releases(data.ext_id)
|
||||
selected_release = [
|
||||
r
|
||||
for r in all_releases
|
||||
if r.archive == data.archive and r.source_repo == data.source_repo
|
||||
]
|
||||
if len(selected_release) == 0:
|
||||
raise Exception("uuuuuuu")
|
||||
|
||||
installed_release = selected_release[0]
|
||||
ext_info = InstallableExtension(
|
||||
id=data.ext_id, name=data.ext_id, installed_release=installed_release
|
||||
)
|
||||
|
||||
ext_info.download_archive()
|
||||
|
||||
try:
|
||||
@ -734,17 +752,16 @@ async def api_install_extension(
|
||||
|
||||
extension = Extension.from_installable_ext(ext_info)
|
||||
|
||||
db_version = (await get_dbversions()).get(ext_id, 0)
|
||||
db_version = (await get_dbversions()).get(data.ext_id, 0)
|
||||
await migrate_extension_database(extension, db_version)
|
||||
|
||||
await add_installed_extension(
|
||||
ext_id=ext_id,
|
||||
version=ext_info.version,
|
||||
ext_id=data.ext_id,
|
||||
version=installed_release.version,
|
||||
active=False,
|
||||
hash=hash,
|
||||
meta=dict(ext_info),
|
||||
meta={"installed_release": dict(installed_release)},
|
||||
)
|
||||
settings.lnbits_disabled_extensions += [ext_id]
|
||||
settings.lnbits_disabled_extensions += [data.ext_id]
|
||||
|
||||
# mount routes for the new version
|
||||
core_app_extra.register_new_ext_routes(extension)
|
||||
|
@ -103,13 +103,10 @@ async def extensions_install(
|
||||
lambda ext: {
|
||||
"id": ext.id,
|
||||
"name": ext.name,
|
||||
"hash": ext.hash,
|
||||
"version": ext.version,
|
||||
"icon": ext.icon,
|
||||
"iconUrl": ext.icon_url,
|
||||
"shortDescription": ext.short_description,
|
||||
"stars": ext.stars,
|
||||
"details": ext.details,
|
||||
"dependencies": ext.dependencies,
|
||||
"isInstalled": ext.id in installed_extensions,
|
||||
"isActive": not ext.id in inactive_extensions,
|
||||
|
@ -111,6 +111,7 @@ class ExtensionRelease(BaseModel):
|
||||
published_at: Optional[str]
|
||||
html_url: Optional[str]
|
||||
description: Optional[str]
|
||||
details_html: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
def from_github_release(cls, source_repo: str, r: dict) -> "ExtensionRelease":
|
||||
@ -146,19 +147,25 @@ class ExtensionRelease(BaseModel):
|
||||
class InstallableExtension(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
archive: str # todo: move to installed_release
|
||||
hash: str
|
||||
short_description: Optional[str] = None
|
||||
details: Optional[str] = None
|
||||
icon: Optional[str] = None
|
||||
icon_url: Optional[str] = None
|
||||
dependencies: List[str] = []
|
||||
is_admin_only: bool = False
|
||||
version: str = "none" # todo: move to Release
|
||||
stars: int = 0
|
||||
latest_release: Optional[ExtensionRelease]
|
||||
installed_release: Optional[ExtensionRelease]
|
||||
|
||||
@property
|
||||
def hash(self) -> str:
|
||||
if self.installed_release:
|
||||
if self.installed_release.hash:
|
||||
return self.installed_release.hash
|
||||
m = hashlib.sha256()
|
||||
m.update(f"{self.installed_release.archive}".encode())
|
||||
return m.hexdigest()
|
||||
return "not-installed"
|
||||
|
||||
@property
|
||||
def zip_path(self) -> str:
|
||||
extensions_data_dir = os.path.join(settings.lnbits_data_folder, "extensions")
|
||||
@ -186,7 +193,7 @@ class InstallableExtension(BaseModel):
|
||||
if os.path.isfile(ext_zip_file):
|
||||
os.remove(ext_zip_file)
|
||||
try:
|
||||
download_url(self.archive, ext_zip_file)
|
||||
download_url(self.installed_release.archive, ext_zip_file)
|
||||
except Exception as ex:
|
||||
logger.warning(ex)
|
||||
raise HTTPException(
|
||||
@ -195,7 +202,7 @@ class InstallableExtension(BaseModel):
|
||||
)
|
||||
|
||||
archive_hash = file_hash(ext_zip_file)
|
||||
if self.hash != archive_hash:
|
||||
if self.installed_release.hash and self.installed_release.hash != archive_hash:
|
||||
# remove downloaded archive
|
||||
if os.path.isfile(ext_zip_file):
|
||||
os.remove(ext_zip_file)
|
||||
@ -205,15 +212,22 @@ class InstallableExtension(BaseModel):
|
||||
)
|
||||
|
||||
def extract_archive(self):
|
||||
shutil.rmtree(self.ext_dir, True)
|
||||
with zipfile.ZipFile(self.zip_path, "r") as zip_ref:
|
||||
zip_ref.extractall(os.path.join("lnbits", "extensions"))
|
||||
|
||||
os.makedirs(os.path.join("lnbits", "upgrades"), exist_ok=True)
|
||||
shutil.rmtree(self.ext_upgrade_dir, True)
|
||||
with zipfile.ZipFile(self.zip_path, "r") as zip_ref:
|
||||
zip_ref.extractall(self.ext_upgrade_dir)
|
||||
generated_dir_name = os.listdir(self.ext_upgrade_dir)[0]
|
||||
os.rename(
|
||||
os.path.join(self.ext_upgrade_dir, generated_dir_name),
|
||||
os.path.join(self.ext_upgrade_dir, self.id),
|
||||
)
|
||||
|
||||
shutil.rmtree(self.ext_dir, True)
|
||||
shutil.copytree(
|
||||
os.path.join(self.ext_upgrade_dir, self.id),
|
||||
os.path.join("lnbits", "extensions", self.id),
|
||||
)
|
||||
|
||||
def nofiy_upgrade(self) -> None:
|
||||
"""Update the the list of upgraded extensions. The middleware will perform redirects based on this"""
|
||||
if not self.hash:
|
||||
@ -261,15 +275,15 @@ class InstallableExtension(BaseModel):
|
||||
logger.warning(e)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def get_extension_info(cls, ext_id: str, hash: str) -> "InstallableExtension":
|
||||
@classmethod # todo: remove
|
||||
async def get_extension_info(
|
||||
cls, ext_id: str, archive: str
|
||||
) -> "InstallableExtension":
|
||||
installable_extensions: List[
|
||||
InstallableExtension
|
||||
] = await InstallableExtension.get_installable_extensions()
|
||||
|
||||
valid_extensions = [
|
||||
e for e in installable_extensions if e.id == ext_id and e.hash == hash
|
||||
]
|
||||
valid_extensions = [e for e in installable_extensions if e.id == ext_id]
|
||||
if len(valid_extensions) == 0:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
@ -322,7 +336,6 @@ class InstallableExtension(BaseModel):
|
||||
archive=e["archive"],
|
||||
hash=e["hash"],
|
||||
short_description=e["shortDescription"],
|
||||
details=e["details"] if "details" in e else "",
|
||||
icon=e["icon"],
|
||||
dependencies=e["dependencies"]
|
||||
if "dependencies" in e
|
||||
@ -365,6 +378,7 @@ class InstallableExtension(BaseModel):
|
||||
hash=e["hash"],
|
||||
source_repo=url,
|
||||
description=e["shortDescription"],
|
||||
details_html=e.get("details"),
|
||||
)
|
||||
]
|
||||
|
||||
@ -415,6 +429,12 @@ class InstalledExtensionMiddleware:
|
||||
await self.app(scope, receive, send)
|
||||
|
||||
|
||||
class CreateExtension(BaseModel):
|
||||
ext_id: str
|
||||
archive: str
|
||||
source_repo: str
|
||||
|
||||
|
||||
def get_valid_extensions(include_disabled_exts=False) -> List[Extension]:
|
||||
return [
|
||||
extension
|
||||
|
Loading…
Reference in New Issue
Block a user