mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2024-11-19 09:54:21 +01:00
feat: use github releases for installing extensions
This commit is contained in:
parent
38a132604b
commit
496346b3ba
@ -73,7 +73,7 @@ async def get_user(user_id: str, conn: Optional[Connection] = None) -> Optional[
|
||||
async def add_installed_extension(
|
||||
*,
|
||||
ext_id: str,
|
||||
version,
|
||||
version: str,
|
||||
active: bool,
|
||||
hash: str,
|
||||
meta: dict,
|
||||
|
@ -276,7 +276,7 @@ async def m009_create_installed_extensions_table(db):
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS installed_extensions (
|
||||
id TEXT PRIMARY KEY,
|
||||
version INT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
active BOOLEAN DEFAULT false,
|
||||
hash TEXT NOT NULL,
|
||||
meta TEXT NOT NULL DEFAULT '{}'
|
||||
|
@ -40,8 +40,14 @@
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<!-- hack must find better solution -->
|
||||
<q-img
|
||||
v-if="extension.iconUrl"
|
||||
:src="extension.iconUrl"
|
||||
spinner-color="white"
|
||||
style="max-width: 100%"
|
||||
></q-img>
|
||||
<q-icon
|
||||
v-else
|
||||
:name="extension.icon"
|
||||
color="grey-5"
|
||||
style="font-size: 4rem"
|
||||
@ -107,34 +113,20 @@
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<q-rating
|
||||
max="5"
|
||||
v-model="maxStars"
|
||||
size="1.5em"
|
||||
color="yellow"
|
||||
icon="star_border"
|
||||
icon-selected="star"
|
||||
icon-half="star_half"
|
||||
readonly
|
||||
no-dimming
|
||||
class="float-right"
|
||||
>
|
||||
<template v-slot:tip-1>
|
||||
<q-tooltip>User Review Comming Soon</q-tooltip>
|
||||
</template>
|
||||
<template v-slot:tip-2>
|
||||
<q-tooltip>User Review Comming Soon</q-tooltip>
|
||||
</template>
|
||||
<template v-slot:tip-3>
|
||||
<q-tooltip>User Review Comming Soon</q-tooltip>
|
||||
</template>
|
||||
<template v-slot:tip-4>
|
||||
<q-tooltip>User Review Comming Soon</q-tooltip>
|
||||
</template>
|
||||
<template v-slot:tip-5>
|
||||
<q-tooltip>User Review Comming Soon</q-tooltip>
|
||||
</template>
|
||||
</q-rating>
|
||||
<div class="float-right">
|
||||
<small v-text="extension.stars"> </small>
|
||||
<q-rating
|
||||
max="1"
|
||||
v-model="maxStars"
|
||||
size="1.5em"
|
||||
color="yellow"
|
||||
icon="star"
|
||||
icon-selected="star"
|
||||
readonly
|
||||
no-dimming
|
||||
>
|
||||
</q-rating>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
@ -279,6 +271,7 @@
|
||||
inProgress: false
|
||||
}))
|
||||
this.filteredExtensions = this.extensions.concat([])
|
||||
console.log('### his.filteredExtensions', this.filteredExtensions)
|
||||
},
|
||||
mixins: [windowMixin]
|
||||
})
|
||||
|
@ -740,7 +740,6 @@ async def api_install_extension(
|
||||
db_version = (await get_dbversions()).get(ext_id, 0)
|
||||
await migrate_extension_database(extension, db_version)
|
||||
|
||||
# disable by default
|
||||
await add_installed_extension(
|
||||
ext_id=ext_id,
|
||||
version=ext_info.version,
|
||||
|
@ -105,7 +105,9 @@ async def extensions_install(
|
||||
"name": ext.name,
|
||||
"hash": ext.hash,
|
||||
"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,
|
||||
|
@ -6,6 +6,7 @@ import sys
|
||||
import urllib.request
|
||||
import zipfile
|
||||
from http import HTTPStatus
|
||||
from platform import release
|
||||
from typing import List, NamedTuple, Optional
|
||||
|
||||
import httpx
|
||||
@ -102,6 +103,27 @@ class ExtensionManager:
|
||||
return output
|
||||
|
||||
|
||||
class ExtensionRelease(BaseModel):
|
||||
name: str
|
||||
version: str
|
||||
archive: str
|
||||
description: str
|
||||
|
||||
@classmethod
|
||||
def from_github_releases(cls, releases: dict) -> List["ExtensionRelease"]:
|
||||
return list(
|
||||
map(
|
||||
lambda r: ExtensionRelease(
|
||||
name=r["name"],
|
||||
version=r["tag_name"],
|
||||
archive=r["zipball_url"],
|
||||
description=r["body"],
|
||||
),
|
||||
releases,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class InstallableExtension(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
@ -110,9 +132,12 @@ class InstallableExtension(BaseModel):
|
||||
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: Optional[int] = 0
|
||||
version: str = "none" # todo: move to Release
|
||||
stars: int = 0
|
||||
releases: Optional[List[ExtensionRelease]]
|
||||
|
||||
@property
|
||||
def zip_path(self) -> str:
|
||||
@ -194,6 +219,25 @@ class InstallableExtension(BaseModel):
|
||||
|
||||
shutil.rmtree(self.ext_upgrade_dir, True)
|
||||
|
||||
@classmethod
|
||||
async def from_repo(cls, org, repository) -> Optional["InstallableExtension"]:
|
||||
try:
|
||||
repo, releases, config = await fetch_github_repo_info(org, repository)
|
||||
|
||||
return InstallableExtension(
|
||||
id=repo["name"],
|
||||
name=config.get("name"),
|
||||
short_description=config.get("short_description"),
|
||||
archive="xx",
|
||||
hash="123",
|
||||
stars=repo["stargazers_count"],
|
||||
icon_url=icon_to_github_url(org, config.get("tile")),
|
||||
releases=ExtensionRelease.from_github_releases(releases),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(e)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def get_extension_info(cls, ext_id: str, hash: str) -> "InstallableExtension":
|
||||
installable_extensions: List[
|
||||
@ -229,27 +273,35 @@ class InstallableExtension(BaseModel):
|
||||
try:
|
||||
resp = await client.get(url)
|
||||
if resp.status_code != 200:
|
||||
logger.warning(
|
||||
f"Unable to fetch extension list for repository: {url}"
|
||||
)
|
||||
logger.warning(f"Cannot fetch extensions manifest at: {url}")
|
||||
continue
|
||||
for e in resp.json()["extensions"]:
|
||||
extension_list += [
|
||||
InstallableExtension(
|
||||
id=e["id"],
|
||||
name=e["name"],
|
||||
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
|
||||
else [],
|
||||
manifest = resp.json()
|
||||
if "extensions" in manifest:
|
||||
for e in manifest["extensions"] or []:
|
||||
extension_list += [
|
||||
InstallableExtension(
|
||||
id=e["id"],
|
||||
name=e["name"],
|
||||
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
|
||||
else [],
|
||||
)
|
||||
]
|
||||
if "repos" in manifest:
|
||||
for r in manifest["repos"]:
|
||||
ext = await InstallableExtension.from_repo(
|
||||
r["organisation"], r["repository"]
|
||||
)
|
||||
]
|
||||
print("#### repo_extensions", ext)
|
||||
if ext:
|
||||
extension_list += [ext]
|
||||
except Exception as e:
|
||||
logger.warning(e)
|
||||
logger.warning(f"Manifest {url} failed with '{str(e)}'")
|
||||
|
||||
return extension_list
|
||||
|
||||
@ -317,3 +369,45 @@ def file_hash(filename):
|
||||
while n := f.readinto(mv):
|
||||
h.update(mv[:n])
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def icon_to_github_url(org: str, path: Optional[str]) -> str:
|
||||
if not path:
|
||||
return ""
|
||||
_, repo, *rest = path.split("/")
|
||||
tail = "/".join(rest)
|
||||
return f"https://github.com/{org}/{repo}/raw/main/{tail}"
|
||||
|
||||
|
||||
async def fetch_github_repo_info(org: str, repository: str):
|
||||
async with httpx.AsyncClient() as client:
|
||||
repo_url = f"https://api.github.com/repos/{org}/{repository}"
|
||||
resp = await client.get(repo_url)
|
||||
if resp.status_code != 200:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND,
|
||||
detail=f"Cannot fetch extension repo: {repo_url}",
|
||||
)
|
||||
repo = resp.json()
|
||||
|
||||
releases_url = f"https://api.github.com/repos/{org}/{repository}/releases"
|
||||
resp = await client.get(releases_url)
|
||||
if resp.status_code != 200:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND,
|
||||
detail=f"Cannot fetch extension releases: {releases_url}",
|
||||
)
|
||||
|
||||
releases = resp.json()
|
||||
|
||||
config_url = f"""https://raw.githubusercontent.com/{org}/{repository}/{repo["default_branch"]}/config.json"""
|
||||
resp = await client.get(config_url)
|
||||
if resp.status_code != 200:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND,
|
||||
detail=f"Cannot fetch config for extension: {config_url}",
|
||||
)
|
||||
|
||||
config = resp.json()
|
||||
|
||||
return repo, releases, config
|
||||
|
Loading…
Reference in New Issue
Block a user