mirror of
https://github.com/lnbits/lnbits.git
synced 2025-09-27 20:36:16 +02:00
feat: separate extension release into new API
This commit is contained in:
@@ -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) {
|
||||||
|
@@ -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(
|
||||||
|
@@ -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,
|
||||||
)
|
)
|
||||||
|
@@ -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:
|
||||||
|
Reference in New Issue
Block a user