[feat] introduce max_lnbits_version for extensions (#2834)

* feat: add `max_lnbits_version`

* fix: min/max lables

* chore: `make bundle`

* refactor: extract `is_version_compatible_with_lnbits`

* refactor: rename method
This commit is contained in:
Vlad Stan 2024-12-16 17:41:36 +02:00 committed by GitHub
parent 3ff85363f2
commit 27241e01f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 80 additions and 40 deletions

View File

@ -16,6 +16,7 @@ from pydantic import BaseModel
from lnbits.helpers import (
download_url,
file_hash,
is_lnbits_version_ok,
version_parse,
)
from lnbits.settings import settings
@ -32,6 +33,7 @@ class ExplicitRelease(BaseModel):
icon: Optional[str]
short_description: Optional[str]
min_lnbits_version: Optional[str]
max_lnbits_version: Optional[str]
html_url: Optional[str] # todo: release_url
warning: Optional[str]
info_notification: Optional[str]
@ -40,9 +42,7 @@ class ExplicitRelease(BaseModel):
pay_link: Optional[str]
def is_version_compatible(self):
if not self.min_lnbits_version:
return True
return version_parse(self.min_lnbits_version) <= version_parse(settings.version)
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
class GitHubRelease(BaseModel):
@ -79,11 +79,10 @@ class ExtensionConfig(BaseModel):
tile: str = ""
warning: Optional[str] = ""
min_lnbits_version: Optional[str]
max_lnbits_version: Optional[str]
def is_version_compatible(self):
if not self.min_lnbits_version:
return True
return version_parse(self.min_lnbits_version) <= version_parse(settings.version)
def is_version_compatible(self) -> bool:
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
@classmethod
async def fetch_github_release_config(
@ -181,6 +180,7 @@ class ExtensionRelease(BaseModel):
is_github_release: bool = False
hash: Optional[str] = None
min_lnbits_version: Optional[str] = None
max_lnbits_version: Optional[str] = None
is_version_compatible: Optional[bool] = True
html_url: Optional[str] = None
description: Optional[str] = None
@ -251,6 +251,7 @@ class ExtensionRelease(BaseModel):
source_repo=source_repo,
description=e.short_description,
min_lnbits_version=e.min_lnbits_version,
max_lnbits_version=e.max_lnbits_version,
is_version_compatible=e.is_version_compatible(),
warning=e.warning,
html_url=e.html_url,
@ -578,6 +579,7 @@ class InstallableExtension(BaseModel):
archive=f"{conf_path}",
source_repo=f"{conf_path}",
min_lnbits_version=config_json.get("min_lnbits_version"),
max_lnbits_version=config_json.get("max_lnbits_version"),
)
),
)

View File

@ -23,18 +23,23 @@ from ..models.extensions import Extension, ExtensionMeta, InstallableExtension
async def install_extension(ext_info: InstallableExtension) -> Extension:
ext_id = ext_info.id
installed_ext = await get_installed_extension(ext_id)
ext_info.meta = ext_info.meta or ExtensionMeta()
if ext_info.meta.installed_release:
assert (
ext_info.meta.installed_release.is_version_compatible
), "Incompatible extension version"
installed_ext = await get_installed_extension(ext_info.id)
if installed_ext and installed_ext.meta:
ext_info.meta = ext_info.meta or ExtensionMeta()
ext_info.meta.payments = installed_ext.meta.payments
await ext_info.download_archive()
ext_info.extract_archive()
db_version = await get_db_version(ext_id)
db_version = await get_db_version(ext_info.id)
await migrate_extension_database(ext_info, db_version)
# if the extensions does not exist in the installed extensions table, create it
@ -47,9 +52,9 @@ async def install_extension(ext_info: InstallableExtension) -> Extension:
extension = Extension.from_installable_ext(ext_info)
if extension.is_upgrade_extension:
# call stop while the old routes are still active
await stop_extension_background_work(ext_id)
await stop_extension_background_work(ext_info.id)
await start_extension_background_work(ext_id)
await start_extension_background_work(ext_info.id)
return extension

View File

@ -538,10 +538,26 @@
></a>
</q-card-section>
<q-card-section v-else>
<span v-text="$t('extension_min_lnbits_version')"></span>
<strong>
<span v-text="release.min_lnbits_version"></span>
</strong>
<span
v-text="$t('extension_required_lnbits_version')"
></span>
<span class="q-mr-sm">:</span>
<ul>
<li v-if="release.min_lnbits_version">
<span v-text="$t('min_version')"></span>
<span class="q-mr-sm">:</span>
<strong>
<span v-text="release.min_lnbits_version"></span>
</strong>
</li>
<li v-if="release.max_lnbits_version">
<span v-text="$t('max_version')"></span>
<span class="q-mr-sm">:</span>
<strong>
<span v-text="release.max_lnbits_version"></span>
</strong>
</li>
</ul>
</q-card-section>
<q-card v-if="release.warning">
<q-card-section>

View File

@ -69,11 +69,6 @@ async def api_install_extension(data: CreateExtension):
status_code=HTTPStatus.NOT_FOUND, detail="Release not found"
)
if not release.is_version_compatible:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Incompatible extension version"
)
release.payment_hash = data.payment_hash
ext_meta = ExtensionMeta(installed_release=release)
ext_info = InstallableExtension(

View File

@ -241,6 +241,21 @@ def version_parse(v: str):
return version.parse("0.0.0")
def is_lnbits_version_ok(
min_lnbits_version: Optional[str], max_lnbits_version: Optional[str]
) -> bool:
if min_lnbits_version and (
version_parse(min_lnbits_version) > version_parse(settings.version)
):
return False
if max_lnbits_version and (
version_parse(max_lnbits_version) <= version_parse(settings.version)
):
return False
return True
def download_url(url, save_path):
with request.urlopen(url, timeout=60) as dl_file:
with open(save_path, "wb") as out_file:

File diff suppressed because one or more lines are too long

View File

@ -143,7 +143,7 @@ window.localisation.br = {
'Todos os dados da extensão serão permanentemente excluídos. Não há como desfazer essa operação!',
extension_db_drop_warning:
'Você está prestes a remover todos os dados para a extensão. Por favor, digite o nome da extensão para continuar:',
extension_min_lnbits_version:
extension_required_lnbits_version:
'Esta versão requer no mínimo a versão do LNbits',
payment_hash: 'Hash de pagamento',
fee: 'Taxa',

View File

@ -136,7 +136,7 @@ window.localisation.cn = {
extension_db_drop_info: '该扩展程序的所有数据将被永久删除。此操作无法撤销!',
extension_db_drop_warning:
'您即将删除该扩展的所有数据。请继续输入扩展程序名称以确认操作:',
extension_min_lnbits_version: '此版本要求最低的 LNbits 版本为',
extension_required_lnbits_version: '此版本要求最低的 LNbits 版本为',
payment_hash: '付款哈希',
fee: '费',
amount: '金额',

View File

@ -143,7 +143,8 @@ window.localisation.cs = {
'Všechna data pro rozšíření budou trvale odstraněna. Tuto operaci nelze vrátit zpět!',
extension_db_drop_warning:
'Chystáte se odstranit všechna data pro rozšíření. Prosím, pokračujte zadáním názvu rozšíření:',
extension_min_lnbits_version: 'Toto vydání vyžaduje alespoň verzi LNbits',
extension_required_lnbits_version:
'Toto vydání vyžaduje alespoň verzi LNbits',
payment_hash: 'Hash platby',
fee: 'Poplatek',
amount: 'Částka',

View File

@ -145,7 +145,7 @@ window.localisation.de = {
'Alle Daten für die Erweiterung werden dauerhaft gelöscht. Es gibt keine Möglichkeit, diesen Vorgang rückgängig zu machen!',
extension_db_drop_warning:
'Sie sind dabei, alle Daten für die Erweiterung zu entfernen. Bitte geben Sie den Namen der Erweiterung ein, um fortzufahren:',
extension_min_lnbits_version:
extension_required_lnbits_version:
'Diese Version erfordert mindestens die LNbits-Version',
payment_hash: 'Zahlungs-Hash',
fee: 'Gebühr',

View File

@ -145,7 +145,9 @@ window.localisation.en = {
'All data for the extension will be permanently deleted. There is no way to undo this operation!',
extension_db_drop_warning:
'You are about to remove all data for the extension. Please type the extension name to continue:',
extension_min_lnbits_version: 'This release requires at least LNbits version',
extension_required_lnbits_version: 'This release requires LNbits version',
min_version: 'Minimum (included)',
max_version: 'Maximum (excluded)',
payment_hash: 'Payment Hash',
fee: 'Fee',
amount: 'Amount',

View File

@ -144,7 +144,7 @@ window.localisation.es = {
'Todos los datos para la extensión se eliminarán permanentemente. ¡No hay manera de deshacer esta operación!',
extension_db_drop_warning:
'Está a punto de eliminar todos los datos para la extensión. Por favor, escriba el nombre de la extensión para continuar:',
extension_min_lnbits_version:
extension_required_lnbits_version:
'Esta versión requiere al menos una versión de LNbits',
payment_hash: 'Hash de pago',
fee: 'Cuota',

View File

@ -144,7 +144,8 @@ window.localisation.fi = {
'Kaikki laajennuksen tallettama tieto poistetaan pysyvästi. Poistoa ei voi jälkikäteen peruuttaa!',
extension_db_drop_warning:
'Olet tuhoamassa laajennuksen tallettamat tiedot. Vahvista poisto kirjoittamalla viivalle seuraavassa näkyvä laajennuksen nimi:',
extension_min_lnbits_version: 'Tämä julkaisu vaatii vähintään LNbits-version',
extension_required_lnbits_version:
'Tämä julkaisu vaatii vähintään LNbits-version',
payment_hash: 'Maksun tiiviste',
fee: 'Kulu',
amount: 'Määrä',

View File

@ -148,7 +148,7 @@ window.localisation.fr = {
"Toutes les données pour l'extension seront supprimées de manière permanente. Il n'est pas possible d'annuler cette opération !",
extension_db_drop_warning:
"Vous êtes sur le point de supprimer toutes les données de l'extension. Veuillez taper le nom de l'extension pour continuer :",
extension_min_lnbits_version:
extension_required_lnbits_version:
'Cette version nécessite au moins LNbits version',
payment_hash: 'Hash de paiement',
fee: 'Frais',

View File

@ -145,7 +145,7 @@ window.localisation.it = {
"Tutti i dati relativi all'estensione saranno cancellati permanentemente. Non c'è modo di annullare questa operazione!",
extension_db_drop_warning:
"Stai per rimuovere tutti i dati per l'estensione. Digita il nome dell'estensione per continuare:",
extension_min_lnbits_version:
extension_required_lnbits_version:
'Questa versione richiede almeno la versione LNbits',
payment_hash: 'Hash del pagamento',
fee: 'Tariffa',

View File

@ -142,7 +142,7 @@ window.localisation.jp = {
'エクステンションのすべてのデータが完全に削除されます。この操作を元に戻す方法はありません!',
extension_db_drop_warning:
'エクステンションのすべてのデータを削除しようとしています。続行するには、エクステンションの名前を入力してください:',
extension_min_lnbits_version:
extension_required_lnbits_version:
'このリリースには少なくとも LNbits バージョンが必要です',
payment_hash: '支払いハッシュ',
fee: '料金',

View File

@ -142,7 +142,7 @@ window.localisation.kr = {
'해당 확장 기능의 모든 데이터가 영구적으로 삭제됩니다. 작업 수행 후에는 되돌릴 수 없습니다!',
extension_db_drop_warning:
'해당 확장 기능의 모든 데이터가 영구적으로 삭제될 겁니다. 계속하려면 확장 기능의 이름을 입력해주세요:',
extension_min_lnbits_version:
extension_required_lnbits_version:
'이 배포 버전은 더 높은 버전의 lnbits가 설치되어 있어야 합니다.',
payment_hash: '결제 해쉬값',
fee: '수수료',

View File

@ -145,7 +145,8 @@ window.localisation.nl = {
'Alle gegevens voor de extensie zullen permanent worden verwijderd. Er is geen manier om deze bewerking ongedaan te maken!',
extension_db_drop_warning:
'U staat op het punt alle gegevens voor de extensie te verwijderen. Typ de naam van de extensie om door te gaan:',
extension_min_lnbits_version: 'Deze release vereist ten minste LNbits-versie',
extension_required_lnbits_version:
'Deze release vereist ten minste LNbits-versie',
payment_hash: 'Betalings-hash',
fee: 'Kosten',
amount: 'Bedrag',

View File

@ -144,7 +144,7 @@ window.localisation.pi = {
"All data fer th' extension will be permanently deleted. There be no way to undo this operation!",
extension_db_drop_warning:
"Ye be about to scuttle all data fer th' extension. Please scribble th' extension name to continue:",
extension_min_lnbits_version:
extension_required_lnbits_version:
"This release be needin' at least LNbits version",
payment_hash: 'Payment Hash like a treasure map, arrr',
fee: 'Fee like a toll to cross a strait, matey',

View File

@ -143,7 +143,7 @@ window.localisation.pl = {
'Wszystkie dane dla rozszerzenia zostaną trwale usunięte. Nie ma sposobu, aby cofnąć tę operację!',
extension_db_drop_warning:
'Za chwilę usuniesz wszystkie dane dla rozszerzenia. Proszę wpisz nazwę rozszerzenia, aby kontynuować:',
extension_min_lnbits_version: 'To wymaga przynajmniej wersji LNbits',
extension_required_lnbits_version: 'To wymaga przynajmniej wersji LNbits',
payment_hash: 'Hash Płatności',
fee: 'Opłata',
amount: 'Wartość',

View File

@ -144,7 +144,8 @@ window.localisation.pt = {
'Todos os dados da extensão serão permanentemente excluídos. Não há como desfazer essa operação!',
extension_db_drop_warning:
'Você está prestes a remover todos os dados para a extensão. Por favor, digite o nome da extensão para continuar:',
extension_min_lnbits_version: 'Esta versão requer pelo menos a versão LNbits',
extension_required_lnbits_version:
'Esta versão requer pelo menos a versão LNbits',
payment_hash: 'Hash de pagamento',
fee: 'Taxa',
amount: 'Quantidade',

View File

@ -143,7 +143,8 @@ window.localisation.sk = {
'Všetky údaje pre rozšírenie budú trvalo vymazané. Túto operáciu nie je možné vrátiť!',
extension_db_drop_warning:
'Chystáte sa odstrániť všetky údaje pre rozšírenie. Pre pokračovanie prosím napíšte názov rozšírenia:',
extension_min_lnbits_version: 'Toto vydanie vyžaduje aspoň verziu LNbits',
extension_required_lnbits_version:
'Toto vydanie vyžaduje aspoň verziu LNbits',
payment_hash: 'Hash platby',
fee: 'Poplatok',
amount: 'Suma',

View File

@ -142,7 +142,7 @@ window.localisation.we = {
"Bydd yr holl ddata ar gyfer yr estyniad yn cael ei ddileu'n barhaol. Does dim ffordd o dadwneud y weithrediad hwn!",
extension_db_drop_warning:
"Rydych chi ar fin dileu'r holl ddata ar gyfer yr estyniad. Teipiwch enw'r estyniad i barhau:",
extension_min_lnbits_version:
extension_required_lnbits_version:
"Mae'r rhyddhau hwn yn gofyn o leiaf am fersiwn LNbits",
payment_hash: 'Hais Taliad',
fee: 'Fee',