feat: separate extension release into new API

This commit is contained in:
Vlad Stan
2023-01-16 14:56:34 +02:00
parent a73e8ae44d
commit 27b308ae1a
4 changed files with 74 additions and 17 deletions

View File

@@ -153,18 +153,25 @@
<q-dialog v-model="showUpgradeDialog"> <q-dialog v-model="showUpgradeDialog">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card"> <q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<div class="col-12 col-md-5 q-gutter-y-md"> <div class="col-12 col-md-5 q-gutter-y-md" v-if="selectedExtensionRepos">
<q-card> <q-card v-for="repo of Object.keys(selectedExtensionRepos)" :key="repo">
<q-card-section> <q-card-section>
<h6 class="text-subtitle1 q-my-none">Repo name</h6> <h6 class="text-subtitle1 q-my-none">
<q-badge color="primary" rounded
><small v-text="repo"></small
></q-badge>
</h6>
</q-card-section> </q-card-section>
<q-card-section class="q-pa-none"> <q-card-section class="q-pa-none">
<q-separator></q-separator> <q-separator></q-separator>
<q-list> <q-list>
<q-expansion-item <q-expansion-item
v-for="release of selectedExtensionRepos[repo]"
:key="release.version"
group="extras" group="extras"
icon="download" icon="download"
label="Version" :label="release.description"
:caption="'Version: ' + release.version"
:content-inset-level="0.5" :content-inset-level="0.5"
> >
<q-expansion-item <q-expansion-item
@@ -229,6 +236,8 @@
showUpgradeDialog: false, showUpgradeDialog: false,
showDetailsDialog: false, showDetailsDialog: false,
selectedExtension: null, selectedExtension: null,
selectedExtensionReleases: null,
selectedExtensionRepos: null,
maxStars: 0 maxStars: 0
} }
}, },
@@ -317,12 +326,19 @@
console.log('### showUpgrade') console.log('### showUpgrade')
this.selectedExtension = extension this.selectedExtension = extension
this.showUpgradeDialog = true this.showUpgradeDialog = true
const releases = await LNbits.api.request( const {data} = await LNbits.api.request(
'GET', 'GET',
`/api/v1/extension/${extension.id}/releases?usr=${this.g.user.id}`, `/api/v1/extension/${extension.id}/releases?usr=${this.g.user.id}`,
this.g.user.wallets[0].adminkey this.g.user.wallets[0].adminkey
) )
console.log('### releases', releases) this.selectedExtensionReleases = data
this.selectedExtensionRepos = data.reduce((repos, release) => {
repos[release.source_repo] = repos[release.source_repo] || []
repos[release.source_repo].push(release)
return repos
}, {})
console.log('### releases', this.selectedExtensionReleases)
console.log('### repos', this.selectedExtensionRepos)
}, },
showExtensionDetails: function (extension) { showExtensionDetails: function (extension) {

View File

@@ -42,6 +42,7 @@ from lnbits.decorators import (
) )
from lnbits.extension_manger import ( from lnbits.extension_manger import (
Extension, Extension,
ExtensionRelease,
InstallableExtension, InstallableExtension,
get_valid_extensions, get_valid_extensions,
) )
@@ -800,12 +801,11 @@ async def api_uninstall_extension(ext_id: str, user: User = Depends(check_admin)
@core_app.get("/api/v1/extension/{ext_id}/releases") @core_app.get("/api/v1/extension/{ext_id}/releases")
async def get_extension_releases(ext_id: str, user: User = Depends(check_admin)): async def get_extension_releases(ext_id: str, user: User = Depends(check_admin)):
try: try:
installable_extensions: List[ extension_releases: List[
InstallableExtension ExtensionRelease
] = await InstallableExtension.get_installable_extensions() ] = await InstallableExtension.get_extension_releases(ext_id)
extensions = [e for e in installable_extensions if e.id == ext_id]
return extensions return extension_releases
except Exception as ex: except Exception as ex:
raise HTTPException( raise HTTPException(

View File

@@ -113,7 +113,9 @@ async def extensions_install(
"dependencies": ext.dependencies, "dependencies": ext.dependencies,
"isInstalled": ext.id in installed_extensions, "isInstalled": ext.id in installed_extensions,
"isActive": not ext.id in inactive_extensions, "isActive": not ext.id in inactive_extensions,
"release": dict(ext.release) if ext.release else None, "latestRelease": dict(ext.latest_release)
if ext.latest_release
else None,
}, },
extension_list, extension_list,
) )

View File

@@ -6,7 +6,6 @@ import sys
import urllib.request import urllib.request
import zipfile import zipfile
from http import HTTPStatus from http import HTTPStatus
from platform import release
from typing import List, NamedTuple, Optional from typing import List, NamedTuple, Optional
import httpx import httpx
@@ -107,6 +106,7 @@ class ExtensionRelease(BaseModel):
name: str name: str
version: str version: str
archive: str archive: str
source_repo: str
hash: Optional[str] hash: Optional[str]
published_at: Optional[str] published_at: Optional[str]
url: Optional[str] url: Optional[str]
@@ -118,6 +118,7 @@ class ExtensionRelease(BaseModel):
name=r["name"], name=r["name"],
version=r["tag_name"], version=r["tag_name"],
archive=r["zipball_url"], archive=r["zipball_url"],
# source_repo=r[]
# description=r["body"], # bad for JSON # description=r["body"], # bad for JSON
published_at=r["published_at"], published_at=r["published_at"],
url=r["html_url"], url=r["html_url"],
@@ -127,7 +128,7 @@ class ExtensionRelease(BaseModel):
class InstallableExtension(BaseModel): class InstallableExtension(BaseModel):
id: str id: str
name: str name: str
archive: str #todo: move to installed_release archive: str # todo: move to installed_release
hash: str hash: str
short_description: Optional[str] = None short_description: Optional[str] = None
details: Optional[str] = None details: Optional[str] = None
@@ -137,7 +138,9 @@ class InstallableExtension(BaseModel):
is_admin_only: bool = False is_admin_only: bool = False
version: str = "none" # todo: move to Release version: str = "none" # todo: move to Release
stars: int = 0 stars: int = 0
release: Optional[ExtensionRelease] latest_release: Optional[ExtensionRelease]
installed_release: Optional[ExtensionRelease]
all_releases: List[ExtensionRelease] = []
@property @property
def zip_path(self) -> str: def zip_path(self) -> str:
@@ -233,7 +236,7 @@ class InstallableExtension(BaseModel):
version="0", version="0",
stars=repo["stargazers_count"], stars=repo["stargazers_count"],
icon_url=icon_to_github_url(org, config.get("tile")), icon_url=icon_to_github_url(org, config.get("tile")),
release=ExtensionRelease.from_github_release(latest_release), latest_release=ExtensionRelease.from_github_release(latest_release),
) )
except Exception as e: except Exception as e:
logger.warning(e) logger.warning(e)
@@ -268,6 +271,7 @@ class InstallableExtension(BaseModel):
@classmethod @classmethod
async def get_installable_extensions(cls) -> List["InstallableExtension"]: async def get_installable_extensions(cls) -> List["InstallableExtension"]:
extension_list: List[InstallableExtension] = [] extension_list: List[InstallableExtension] = []
extension_id_list: List[str] = []
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
for url in settings.lnbits_extensions_manifests: for url in settings.lnbits_extensions_manifests:
@@ -278,7 +282,9 @@ class InstallableExtension(BaseModel):
continue continue
manifest = resp.json() manifest = resp.json()
if "extensions" in manifest: if "extensions" in manifest:
for e in manifest["extensions"] or []: for e in manifest["extensions"]:
if e["id"] in extension_id_list:
continue
extension_list += [ extension_list += [
InstallableExtension( InstallableExtension(
id=e["id"], id=e["id"],
@@ -293,18 +299,51 @@ class InstallableExtension(BaseModel):
else [], else [],
) )
] ]
extension_id_list += [e["id"]]
if "repos" in manifest: if "repos" in manifest:
for r in manifest["repos"]: for r in manifest["repos"]:
ext = await InstallableExtension.from_repo( ext = await InstallableExtension.from_repo(
r["organisation"], r["repository"] r["organisation"], r["repository"]
) )
if ext: if ext:
if ext.id in extension_id_list:
continue
extension_list += [ext] extension_list += [ext]
extension_id_list += [ext.id]
except Exception as e: except Exception as e:
logger.warning(f"Manifest {url} failed with '{str(e)}'") logger.warning(f"Manifest {url} failed with '{str(e)}'")
return extension_list return extension_list
@classmethod
async def get_extension_releases(cls, ext_id: str) -> List["ExtensionRelease"]:
extension_releases: List[ExtensionRelease] = []
async with httpx.AsyncClient() as client:
for url in settings.lnbits_extensions_manifests:
try:
resp = await client.get(url)
if resp.status_code != 200:
logger.warning(f"Cannot fetch extensions manifest at: {url}")
continue
manifest = resp.json()
if "extensions" in manifest:
for e in manifest["extensions"]:
if e["id"] == ext_id:
extension_releases += [
ExtensionRelease(
name=e["name"],
version=e["version"],
archive=e["archive"],
hash=e["hash"],
source_repo=url,
description=e["shortDescription"],
)
]
except Exception as e:
logger.warning(f"Manifest {url} failed with '{str(e)}'")
return extension_releases
class InstalledExtensionMiddleware: class InstalledExtensionMiddleware:
def __init__(self, app: ASGIApp) -> None: def __init__(self, app: ASGIApp) -> None: