[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 ( from lnbits.helpers import (
download_url, download_url,
file_hash, file_hash,
is_lnbits_version_ok,
version_parse, version_parse,
) )
from lnbits.settings import settings from lnbits.settings import settings
@ -32,6 +33,7 @@ class ExplicitRelease(BaseModel):
icon: Optional[str] icon: Optional[str]
short_description: Optional[str] short_description: Optional[str]
min_lnbits_version: Optional[str] min_lnbits_version: Optional[str]
max_lnbits_version: Optional[str]
html_url: Optional[str] # todo: release_url html_url: Optional[str] # todo: release_url
warning: Optional[str] warning: Optional[str]
info_notification: Optional[str] info_notification: Optional[str]
@ -40,9 +42,7 @@ class ExplicitRelease(BaseModel):
pay_link: Optional[str] pay_link: Optional[str]
def is_version_compatible(self): def is_version_compatible(self):
if not self.min_lnbits_version: return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
return True
return version_parse(self.min_lnbits_version) <= version_parse(settings.version)
class GitHubRelease(BaseModel): class GitHubRelease(BaseModel):
@ -79,11 +79,10 @@ class ExtensionConfig(BaseModel):
tile: str = "" tile: str = ""
warning: Optional[str] = "" warning: Optional[str] = ""
min_lnbits_version: Optional[str] min_lnbits_version: Optional[str]
max_lnbits_version: Optional[str]
def is_version_compatible(self): def is_version_compatible(self) -> bool:
if not self.min_lnbits_version: return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
return True
return version_parse(self.min_lnbits_version) <= version_parse(settings.version)
@classmethod @classmethod
async def fetch_github_release_config( async def fetch_github_release_config(
@ -181,6 +180,7 @@ class ExtensionRelease(BaseModel):
is_github_release: bool = False is_github_release: bool = False
hash: Optional[str] = None hash: Optional[str] = None
min_lnbits_version: Optional[str] = None min_lnbits_version: Optional[str] = None
max_lnbits_version: Optional[str] = None
is_version_compatible: Optional[bool] = True is_version_compatible: Optional[bool] = True
html_url: Optional[str] = None html_url: Optional[str] = None
description: Optional[str] = None description: Optional[str] = None
@ -251,6 +251,7 @@ class ExtensionRelease(BaseModel):
source_repo=source_repo, source_repo=source_repo,
description=e.short_description, description=e.short_description,
min_lnbits_version=e.min_lnbits_version, min_lnbits_version=e.min_lnbits_version,
max_lnbits_version=e.max_lnbits_version,
is_version_compatible=e.is_version_compatible(), is_version_compatible=e.is_version_compatible(),
warning=e.warning, warning=e.warning,
html_url=e.html_url, html_url=e.html_url,
@ -578,6 +579,7 @@ class InstallableExtension(BaseModel):
archive=f"{conf_path}", archive=f"{conf_path}",
source_repo=f"{conf_path}", source_repo=f"{conf_path}",
min_lnbits_version=config_json.get("min_lnbits_version"), 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: 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: if installed_ext and installed_ext.meta:
ext_info.meta = ext_info.meta or ExtensionMeta()
ext_info.meta.payments = installed_ext.meta.payments ext_info.meta.payments = installed_ext.meta.payments
await ext_info.download_archive() await ext_info.download_archive()
ext_info.extract_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) await migrate_extension_database(ext_info, db_version)
# if the extensions does not exist in the installed extensions table, create it # 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) extension = Extension.from_installable_ext(ext_info)
if extension.is_upgrade_extension: if extension.is_upgrade_extension:
# call stop while the old routes are still active # 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 return extension

View file

@ -538,10 +538,26 @@
></a> ></a>
</q-card-section> </q-card-section>
<q-card-section v-else> <q-card-section v-else>
<span v-text="$t('extension_min_lnbits_version')"></span> <span
<strong> v-text="$t('extension_required_lnbits_version')"
<span v-text="release.min_lnbits_version"></span> ></span>
</strong> <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-section>
<q-card v-if="release.warning"> <q-card v-if="release.warning">
<q-card-section> <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" 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 release.payment_hash = data.payment_hash
ext_meta = ExtensionMeta(installed_release=release) ext_meta = ExtensionMeta(installed_release=release)
ext_info = InstallableExtension( ext_info = InstallableExtension(

View file

@ -241,6 +241,21 @@ def version_parse(v: str):
return version.parse("0.0.0") 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): def download_url(url, save_path):
with request.urlopen(url, timeout=60) as dl_file: with request.urlopen(url, timeout=60) as dl_file:
with open(save_path, "wb") as out_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!', 'Todos os dados da extensão serão permanentemente excluídos. Não há como desfazer essa operação!',
extension_db_drop_warning: 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:', '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', 'Esta versão requer no mínimo a versão do LNbits',
payment_hash: 'Hash de pagamento', payment_hash: 'Hash de pagamento',
fee: 'Taxa', fee: 'Taxa',

View file

@ -136,7 +136,7 @@ window.localisation.cn = {
extension_db_drop_info: '该扩展程序的所有数据将被永久删除。此操作无法撤销!', extension_db_drop_info: '该扩展程序的所有数据将被永久删除。此操作无法撤销!',
extension_db_drop_warning: extension_db_drop_warning:
'您即将删除该扩展的所有数据。请继续输入扩展程序名称以确认操作:', '您即将删除该扩展的所有数据。请继续输入扩展程序名称以确认操作:',
extension_min_lnbits_version: '此版本要求最低的 LNbits 版本为', extension_required_lnbits_version: '此版本要求最低的 LNbits 版本为',
payment_hash: '付款哈希', payment_hash: '付款哈希',
fee: '费', fee: '费',
amount: '金额', 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!', 'Všechna data pro rozšíření budou trvale odstraněna. Tuto operaci nelze vrátit zpět!',
extension_db_drop_warning: 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í:', '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', payment_hash: 'Hash platby',
fee: 'Poplatek', fee: 'Poplatek',
amount: 'Částka', 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!', '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: 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:', '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', 'Diese Version erfordert mindestens die LNbits-Version',
payment_hash: 'Zahlungs-Hash', payment_hash: 'Zahlungs-Hash',
fee: 'Gebühr', 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!', 'All data for the extension will be permanently deleted. There is no way to undo this operation!',
extension_db_drop_warning: extension_db_drop_warning:
'You are about to remove all data for the extension. Please type the extension name to continue:', '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', payment_hash: 'Payment Hash',
fee: 'Fee', fee: 'Fee',
amount: 'Amount', 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!', 'Todos los datos para la extensión se eliminarán permanentemente. ¡No hay manera de deshacer esta operación!',
extension_db_drop_warning: 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:', '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', 'Esta versión requiere al menos una versión de LNbits',
payment_hash: 'Hash de pago', payment_hash: 'Hash de pago',
fee: 'Cuota', 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!', 'Kaikki laajennuksen tallettama tieto poistetaan pysyvästi. Poistoa ei voi jälkikäteen peruuttaa!',
extension_db_drop_warning: extension_db_drop_warning:
'Olet tuhoamassa laajennuksen tallettamat tiedot. Vahvista poisto kirjoittamalla viivalle seuraavassa näkyvä laajennuksen nimi:', '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', payment_hash: 'Maksun tiiviste',
fee: 'Kulu', fee: 'Kulu',
amount: 'Määrä', 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 !", "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: 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 :", "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', 'Cette version nécessite au moins LNbits version',
payment_hash: 'Hash de paiement', payment_hash: 'Hash de paiement',
fee: 'Frais', 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!", "Tutti i dati relativi all'estensione saranno cancellati permanentemente. Non c'è modo di annullare questa operazione!",
extension_db_drop_warning: extension_db_drop_warning:
"Stai per rimuovere tutti i dati per l'estensione. Digita il nome dell'estensione per continuare:", "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', 'Questa versione richiede almeno la versione LNbits',
payment_hash: 'Hash del pagamento', payment_hash: 'Hash del pagamento',
fee: 'Tariffa', fee: 'Tariffa',

View file

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

View file

@ -142,7 +142,7 @@ window.localisation.kr = {
'해당 확장 기능의 모든 데이터가 영구적으로 삭제됩니다. 작업 수행 후에는 되돌릴 수 없습니다!', '해당 확장 기능의 모든 데이터가 영구적으로 삭제됩니다. 작업 수행 후에는 되돌릴 수 없습니다!',
extension_db_drop_warning: extension_db_drop_warning:
'해당 확장 기능의 모든 데이터가 영구적으로 삭제될 겁니다. 계속하려면 확장 기능의 이름을 입력해주세요:', '해당 확장 기능의 모든 데이터가 영구적으로 삭제될 겁니다. 계속하려면 확장 기능의 이름을 입력해주세요:',
extension_min_lnbits_version: extension_required_lnbits_version:
'이 배포 버전은 더 높은 버전의 lnbits가 설치되어 있어야 합니다.', '이 배포 버전은 더 높은 버전의 lnbits가 설치되어 있어야 합니다.',
payment_hash: '결제 해쉬값', payment_hash: '결제 해쉬값',
fee: '수수료', 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!', 'Alle gegevens voor de extensie zullen permanent worden verwijderd. Er is geen manier om deze bewerking ongedaan te maken!',
extension_db_drop_warning: 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:', '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', payment_hash: 'Betalings-hash',
fee: 'Kosten', fee: 'Kosten',
amount: 'Bedrag', 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!", "All data fer th' extension will be permanently deleted. There be no way to undo this operation!",
extension_db_drop_warning: extension_db_drop_warning:
"Ye be about to scuttle all data fer th' extension. Please scribble th' extension name to continue:", "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", "This release be needin' at least LNbits version",
payment_hash: 'Payment Hash like a treasure map, arrr', payment_hash: 'Payment Hash like a treasure map, arrr',
fee: 'Fee like a toll to cross a strait, matey', 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ę!', 'Wszystkie dane dla rozszerzenia zostaną trwale usunięte. Nie ma sposobu, aby cofnąć tę operację!',
extension_db_drop_warning: extension_db_drop_warning:
'Za chwilę usuniesz wszystkie dane dla rozszerzenia. Proszę wpisz nazwę rozszerzenia, aby kontynuować:', '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', payment_hash: 'Hash Płatności',
fee: 'Opłata', fee: 'Opłata',
amount: 'Wartość', 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!', 'Todos os dados da extensão serão permanentemente excluídos. Não há como desfazer essa operação!',
extension_db_drop_warning: 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:', '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', payment_hash: 'Hash de pagamento',
fee: 'Taxa', fee: 'Taxa',
amount: 'Quantidade', 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ť!', 'Všetky údaje pre rozšírenie budú trvalo vymazané. Túto operáciu nie je možné vrátiť!',
extension_db_drop_warning: 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:', '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', payment_hash: 'Hash platby',
fee: 'Poplatok', fee: 'Poplatok',
amount: 'Suma', 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!", "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: extension_db_drop_warning:
"Rydych chi ar fin dileu'r holl ddata ar gyfer yr estyniad. Teipiwch enw'r estyniad i barhau:", "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", "Mae'r rhyddhau hwn yn gofyn o leiaf am fersiwn LNbits",
payment_hash: 'Hais Taliad', payment_hash: 'Hais Taliad',
fee: 'Fee', fee: 'Fee',