feat: move '/extensions' into vue component (#3480)
This commit is contained in:
parent
c2a3fbc6c0
commit
0d5661cda7
11 changed files with 1225 additions and 1166 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -36,12 +36,14 @@ from lnbits.decorators import (
|
|||
check_admin,
|
||||
check_user_exists,
|
||||
)
|
||||
from lnbits.settings import settings
|
||||
|
||||
from ..crud import (
|
||||
create_user_extension,
|
||||
delete_dbversion,
|
||||
drop_extension_db,
|
||||
get_db_version,
|
||||
get_db_versions,
|
||||
get_installed_extension,
|
||||
get_installed_extensions,
|
||||
get_user_extension,
|
||||
|
|
@ -492,3 +494,82 @@ async def delete_extension_db(ext_id: str):
|
|||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
detail=f"Cannot delete data for extension '{ext_id}'",
|
||||
) from exc
|
||||
|
||||
|
||||
# TODO: create a response model for this
|
||||
@extension_router.get("/all")
|
||||
async def extensions(user: User = Depends(check_user_exists)):
|
||||
installed_exts: list[InstallableExtension] = await get_installed_extensions()
|
||||
installed_exts_ids = [e.id for e in installed_exts]
|
||||
|
||||
installable_exts = await InstallableExtension.get_installable_extensions()
|
||||
installable_exts_ids = [e.id for e in installable_exts]
|
||||
installable_exts += [e for e in installed_exts if e.id not in installable_exts_ids]
|
||||
|
||||
for e in installable_exts:
|
||||
installed_ext = next((ie for ie in installed_exts if e.id == ie.id), None)
|
||||
if installed_ext and installed_ext.meta:
|
||||
installed_release = installed_ext.meta.installed_release
|
||||
if installed_ext.meta.pay_to_enable and not user.admin:
|
||||
# not a security leak, but better not to share the wallet id
|
||||
installed_ext.meta.pay_to_enable.wallet = None
|
||||
pay_to_enable = installed_ext.meta.pay_to_enable
|
||||
|
||||
if e.meta:
|
||||
e.meta.installed_release = installed_release
|
||||
e.meta.pay_to_enable = pay_to_enable
|
||||
else:
|
||||
e.meta = ExtensionMeta(
|
||||
installed_release=installed_release,
|
||||
pay_to_enable=pay_to_enable,
|
||||
)
|
||||
# use the installed extension values
|
||||
e.name = installed_ext.name
|
||||
e.short_description = installed_ext.short_description
|
||||
e.icon = installed_ext.icon
|
||||
|
||||
all_ext_ids = [ext.code for ext in await get_valid_extensions()]
|
||||
inactive_extensions = [e.id for e in await get_installed_extensions(active=False)]
|
||||
db_versions = await get_db_versions()
|
||||
|
||||
extension_data = [
|
||||
{
|
||||
"id": ext.id,
|
||||
"name": ext.name,
|
||||
"icon": ext.icon,
|
||||
"shortDescription": ext.short_description,
|
||||
"stars": ext.stars,
|
||||
"isFeatured": ext.meta.featured if ext.meta else False,
|
||||
"dependencies": ext.meta.dependencies if ext.meta else "",
|
||||
"isInstalled": ext.id in installed_exts_ids,
|
||||
"hasDatabaseTables": next(
|
||||
(True for version in db_versions if version.db == ext.id), False
|
||||
),
|
||||
"isAvailable": ext.id in all_ext_ids,
|
||||
"isAdminOnly": ext.id in settings.lnbits_admin_extensions,
|
||||
"isActive": ext.id not in inactive_extensions,
|
||||
"latestRelease": (
|
||||
dict(ext.meta.latest_release)
|
||||
if ext.meta and ext.meta.latest_release
|
||||
else None
|
||||
),
|
||||
"hasPaidRelease": ext.meta.has_paid_release if ext.meta else False,
|
||||
"hasFreeRelease": ext.meta.has_free_release if ext.meta else False,
|
||||
"paidFeatures": ext.meta.paid_features if ext.meta else False,
|
||||
"installedRelease": (
|
||||
dict(ext.meta.installed_release)
|
||||
if ext.meta and ext.meta.installed_release
|
||||
else None
|
||||
),
|
||||
"payToEnable": (
|
||||
dict(ext.meta.pay_to_enable)
|
||||
if ext.meta and ext.meta.pay_to_enable
|
||||
else {}
|
||||
),
|
||||
"isPaymentRequired": ext.requires_payment,
|
||||
"inProgress": False,
|
||||
"selectedForUpdate": False,
|
||||
}
|
||||
for ext in installable_exts
|
||||
]
|
||||
return extension_data
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ from pydantic.types import UUID4
|
|||
|
||||
from lnbits.core.helpers import to_valid_user_id
|
||||
from lnbits.core.models import User
|
||||
from lnbits.core.models.extensions import ExtensionMeta, InstallableExtension
|
||||
from lnbits.core.services import create_invoice, create_user_account
|
||||
from lnbits.core.services.extensions import get_valid_extensions
|
||||
from lnbits.decorators import (
|
||||
check_admin,
|
||||
check_admin_ui,
|
||||
|
|
@ -29,8 +27,6 @@ from lnbits.settings import settings
|
|||
from ...utils.exchange_rates import allowed_currencies
|
||||
from ..crud import (
|
||||
create_wallet,
|
||||
get_db_versions,
|
||||
get_installed_extensions,
|
||||
get_user,
|
||||
get_wallet,
|
||||
)
|
||||
|
|
@ -122,97 +118,6 @@ async def robots():
|
|||
return HTMLResponse(content=data, media_type="text/plain")
|
||||
|
||||
|
||||
@generic_router.get("/extensions", name="extensions", response_class=HTMLResponse)
|
||||
async def extensions(request: Request, user: User = Depends(check_user_exists)):
|
||||
installed_exts: list[InstallableExtension] = await get_installed_extensions()
|
||||
installed_exts_ids = [e.id for e in installed_exts]
|
||||
|
||||
installable_exts = await InstallableExtension.get_installable_extensions()
|
||||
installable_exts_ids = [e.id for e in installable_exts]
|
||||
installable_exts += [e for e in installed_exts if e.id not in installable_exts_ids]
|
||||
|
||||
for e in installable_exts:
|
||||
installed_ext = next((ie for ie in installed_exts if e.id == ie.id), None)
|
||||
if installed_ext and installed_ext.meta:
|
||||
installed_release = installed_ext.meta.installed_release
|
||||
if installed_ext.meta.pay_to_enable and not user.admin:
|
||||
# not a security leak, but better not to share the wallet id
|
||||
installed_ext.meta.pay_to_enable.wallet = None
|
||||
pay_to_enable = installed_ext.meta.pay_to_enable
|
||||
|
||||
if e.meta:
|
||||
e.meta.installed_release = installed_release
|
||||
e.meta.pay_to_enable = pay_to_enable
|
||||
else:
|
||||
e.meta = ExtensionMeta(
|
||||
installed_release=installed_release,
|
||||
pay_to_enable=pay_to_enable,
|
||||
)
|
||||
# use the installed extension values
|
||||
e.name = installed_ext.name
|
||||
e.short_description = installed_ext.short_description
|
||||
e.icon = installed_ext.icon
|
||||
|
||||
all_ext_ids = [ext.code for ext in await get_valid_extensions()]
|
||||
inactive_extensions = [e.id for e in await get_installed_extensions(active=False)]
|
||||
db_versions = await get_db_versions()
|
||||
|
||||
extension_data = [
|
||||
{
|
||||
"id": ext.id,
|
||||
"name": ext.name,
|
||||
"icon": ext.icon,
|
||||
"shortDescription": ext.short_description,
|
||||
"stars": ext.stars,
|
||||
"isFeatured": ext.meta.featured if ext.meta else False,
|
||||
"dependencies": ext.meta.dependencies if ext.meta else "",
|
||||
"isInstalled": ext.id in installed_exts_ids,
|
||||
"hasDatabaseTables": next(
|
||||
(True for version in db_versions if version.db == ext.id), False
|
||||
),
|
||||
"isAvailable": ext.id in all_ext_ids,
|
||||
"isAdminOnly": ext.id in settings.lnbits_admin_extensions,
|
||||
"isActive": ext.id not in inactive_extensions,
|
||||
"latestRelease": (
|
||||
dict(ext.meta.latest_release)
|
||||
if ext.meta and ext.meta.latest_release
|
||||
else None
|
||||
),
|
||||
"hasPaidRelease": ext.meta.has_paid_release if ext.meta else False,
|
||||
"hasFreeRelease": ext.meta.has_free_release if ext.meta else False,
|
||||
"paidFeatures": ext.meta.paid_features if ext.meta else False,
|
||||
"installedRelease": (
|
||||
dict(ext.meta.installed_release)
|
||||
if ext.meta and ext.meta.installed_release
|
||||
else None
|
||||
),
|
||||
"payToEnable": (
|
||||
dict(ext.meta.pay_to_enable)
|
||||
if ext.meta and ext.meta.pay_to_enable
|
||||
else {}
|
||||
),
|
||||
"isPaymentRequired": ext.requires_payment,
|
||||
}
|
||||
for ext in installable_exts
|
||||
]
|
||||
|
||||
# refresh user state. Eg: enabled extensions.
|
||||
# TODO: refactor
|
||||
# user = await get_user(user.id) or user
|
||||
|
||||
return template_renderer().TemplateResponse(
|
||||
request,
|
||||
"core/extensions.html",
|
||||
{
|
||||
"user": user.json(),
|
||||
"extension_data": extension_data,
|
||||
"extension_builder_enabled": user.admin
|
||||
or settings.lnbits_extensions_builder_activate_non_admins,
|
||||
"ajax": _is_ajax_request(request),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@generic_router.get(
|
||||
"/extensions/builder/preview/{ext_id}",
|
||||
name="extensions builder",
|
||||
|
|
@ -370,6 +275,7 @@ admin_ui_checks = [Depends(check_admin), Depends(check_admin_ui)]
|
|||
@generic_router.get("/payments")
|
||||
@generic_router.get("/wallets")
|
||||
@generic_router.get("/account")
|
||||
@generic_router.get("/extensions")
|
||||
@generic_router.get("/users", dependencies=admin_ui_checks)
|
||||
@generic_router.get("/audit", dependencies=admin_ui_checks)
|
||||
@generic_router.get("/node", dependencies=admin_ui_checks)
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ def template_renderer(additional_folders: list | None = None) -> Jinja2Templates
|
|||
"has_holdinvoice": settings.has_holdinvoice,
|
||||
"LNBITS_NOSTR_CONFIGURED": settings.is_nostr_notifications_configured(),
|
||||
"LNBITS_TELEGRAM_CONFIGURED": settings.is_telegram_notifications_configured(),
|
||||
"LNBITS_EXT_BUILDER": settings.lnbits_extensions_builder_activate_non_admins,
|
||||
}
|
||||
|
||||
t.env.globals["WINDOW_SETTINGS"] = window_settings
|
||||
|
|
|
|||
2
lnbits/static/bundle-components.min.js
vendored
2
lnbits/static/bundle-components.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -139,15 +139,6 @@ const routes = [
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/extensions',
|
||||
name: 'Extensions',
|
||||
component: DynamicComponent,
|
||||
props: {
|
||||
fetchUrl: '/extensions',
|
||||
scripts: ['/static/js/extensions.js']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/node',
|
||||
name: 'Node',
|
||||
|
|
@ -192,6 +183,11 @@ const routes = [
|
|||
path: '/extensions/builder',
|
||||
name: 'ExtensionsBuilder',
|
||||
component: PageExtensionBuilder
|
||||
},
|
||||
{
|
||||
path: '/extensions',
|
||||
name: 'Extensions',
|
||||
component: PageExtensions
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
window.ExtensionsPageLogic = {
|
||||
data: function () {
|
||||
window.PageExtensions = {
|
||||
template: '#page-extensions',
|
||||
mixins: [windowMixin],
|
||||
data() {
|
||||
return {
|
||||
extbuilderEnabled: false,
|
||||
slide: 0,
|
||||
fullscreen: false,
|
||||
autoplay: true,
|
||||
|
|
@ -33,10 +36,10 @@ window.ExtensionsPageLogic = {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
handleTabChanged: function (tab) {
|
||||
handleTabChanged(tab) {
|
||||
this.filterExtensions(this.searchTerm, tab)
|
||||
},
|
||||
filterExtensions: function (term, tab) {
|
||||
filterExtensions(term, tab) {
|
||||
// Filter the extensions list
|
||||
function extensionNameContains(searchTerm) {
|
||||
return function (extension) {
|
||||
|
|
@ -65,7 +68,7 @@ window.ExtensionsPageLogic = {
|
|||
this.tab = tab
|
||||
},
|
||||
|
||||
installExtension: async function (release) {
|
||||
async installExtension(release) {
|
||||
// no longer required to check if the invoice was paid
|
||||
// the install logic has been triggered one way or another
|
||||
this.unsubscribeFromPaylinkWs()
|
||||
|
|
@ -101,7 +104,7 @@ window.ExtensionsPageLogic = {
|
|||
LNbits.utils.notifyApiError(err)
|
||||
})
|
||||
},
|
||||
uninstallExtension: async function () {
|
||||
async uninstallExtension() {
|
||||
const extension = this.selectedExtension
|
||||
this.showManageExtensionDialog = false
|
||||
this.showUninstallDialog = false
|
||||
|
|
@ -137,7 +140,7 @@ window.ExtensionsPageLogic = {
|
|||
extension.inProgress = false
|
||||
})
|
||||
},
|
||||
dropExtensionDb: async function () {
|
||||
async dropExtensionDb() {
|
||||
const extension = this.selectedExtension
|
||||
this.showManageExtensionDialog = false
|
||||
this.showDropDbDialog = false
|
||||
|
|
@ -187,14 +190,14 @@ window.ExtensionsPageLogic = {
|
|||
extension.inProgress = false
|
||||
})
|
||||
},
|
||||
enableExtensionForUser: function (extension) {
|
||||
async enableExtensionForUser(extension) {
|
||||
if (extension.isPaymentRequired) {
|
||||
this.showPayToEnable(extension)
|
||||
return
|
||||
}
|
||||
this.enableExtension(extension)
|
||||
},
|
||||
enableExtension: function (extension) {
|
||||
async enableExtension(extension) {
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
|
|
@ -215,7 +218,7 @@ window.ExtensionsPageLogic = {
|
|||
LNbits.utils.notifyApiError(err)
|
||||
})
|
||||
},
|
||||
disableExtension: function (extension) {
|
||||
disableExtension(extension) {
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
|
|
@ -236,14 +239,14 @@ window.ExtensionsPageLogic = {
|
|||
LNbits.utils.notifyApiError(err)
|
||||
})
|
||||
},
|
||||
showPayToEnable: function (extension) {
|
||||
showPayToEnable(extension) {
|
||||
this.selectedExtension = extension
|
||||
this.selectedExtension.payToEnable.paidAmount =
|
||||
extension.payToEnable.amount
|
||||
this.selectedExtension.payToEnable.showQRCode = false
|
||||
this.showPayToEnableDialog = true
|
||||
},
|
||||
updatePayToInstallData: function (extension) {
|
||||
updatePayToInstallData(extension) {
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
|
|
@ -268,17 +271,17 @@ window.ExtensionsPageLogic = {
|
|||
})
|
||||
},
|
||||
|
||||
showUninstall: function () {
|
||||
showUninstall() {
|
||||
this.showManageExtensionDialog = false
|
||||
this.showUninstallDialog = true
|
||||
this.uninstallAndDropDb = false
|
||||
},
|
||||
|
||||
showDropDb: function () {
|
||||
showDropDb() {
|
||||
this.showDropDbDialog = true
|
||||
},
|
||||
|
||||
showManageExtension: async function (extension) {
|
||||
async showManageExtension(extension) {
|
||||
this.selectedExtension = extension
|
||||
this.selectedRelease = null
|
||||
this.selectedExtensionRepos = null
|
||||
|
|
@ -323,7 +326,7 @@ window.ExtensionsPageLogic = {
|
|||
}
|
||||
},
|
||||
|
||||
showExtensionDetails: async function (extId, detailsLink) {
|
||||
async showExtensionDetails(extId, detailsLink) {
|
||||
if (!detailsLink) {
|
||||
return
|
||||
}
|
||||
|
|
@ -528,14 +531,14 @@ window.ExtensionsPageLogic = {
|
|||
}
|
||||
},
|
||||
|
||||
hasNewVersion: function (extension) {
|
||||
hasNewVersion(extension) {
|
||||
if (extension.installedRelease && extension.latestRelease) {
|
||||
return (
|
||||
extension.installedRelease.version !== extension.latestRelease.version
|
||||
)
|
||||
}
|
||||
},
|
||||
isInstalledVersion: function (extension, release) {
|
||||
isInstalledVersion(extension, release) {
|
||||
if (extension.installedRelease) {
|
||||
return (
|
||||
extension.installedRelease.source_repo === release.source_repo &&
|
||||
|
|
@ -543,19 +546,19 @@ window.ExtensionsPageLogic = {
|
|||
)
|
||||
}
|
||||
},
|
||||
getReleaseIcon: function (release) {
|
||||
getReleaseIcon(release) {
|
||||
if (!release.is_version_compatible) return 'block'
|
||||
if (release.isInstalled) return 'download_done'
|
||||
|
||||
return 'download'
|
||||
},
|
||||
getReleaseIconColor: function (release) {
|
||||
getReleaseIconColor(release) {
|
||||
if (!release.is_version_compatible) return 'text-red'
|
||||
if (release.isInstalled) return 'text-green'
|
||||
|
||||
return ''
|
||||
},
|
||||
getGitHubReleaseDetails: async function (release) {
|
||||
async getGitHubReleaseDetails(release) {
|
||||
if (!release.is_github_release || release.loaded) {
|
||||
return
|
||||
}
|
||||
|
|
@ -579,10 +582,10 @@ window.ExtensionsPageLogic = {
|
|||
release.inProgress = false
|
||||
}
|
||||
},
|
||||
selectAllUpdatableExtensionss: async function () {
|
||||
async selectAllUpdatableExtensionss() {
|
||||
this.updatableExtensions.forEach(e => (e.selectedForUpdate = true))
|
||||
},
|
||||
updateSelectedExtensions: async function () {
|
||||
async updateSelectedExtensions() {
|
||||
let count = 0
|
||||
for (const ext of this.updatableExtensions) {
|
||||
try {
|
||||
|
|
@ -628,14 +631,21 @@ window.ExtensionsPageLogic = {
|
|||
setTimeout(() => {
|
||||
this.refreshRoute()
|
||||
}, 2000)
|
||||
},
|
||||
async fetchAllExtensions() {
|
||||
try {
|
||||
const {data} = await LNbits.api.request('GET', `/api/v1/extension/all`)
|
||||
return data
|
||||
} catch (error) {
|
||||
console.warn(error)
|
||||
LNbits.utils.notifyApiError(error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.extensions = window.extension_data.map(e => ({
|
||||
...e,
|
||||
inProgress: false,
|
||||
selectedForUpdate: false
|
||||
}))
|
||||
async created() {
|
||||
this.extensions = await this.fetchAllExtensions()
|
||||
this.extbuilderEnabled = user.admin || this.LNBITS_EXT_BUILDER
|
||||
this.filteredExtensions = this.extensions.concat([])
|
||||
const hash = window.location.hash.replace('#', '')
|
||||
const ext = this.filteredExtensions.find(ext => ext.id === hash)
|
||||
|
|
@ -651,6 +661,5 @@ window.ExtensionsPageLogic = {
|
|||
this.updatableExtensions = this.extensions.filter(ext =>
|
||||
this.hasNewVersion(ext)
|
||||
)
|
||||
},
|
||||
mixins: [windowMixin]
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@
|
|||
],
|
||||
"components": [
|
||||
"js/pages/extensions_builder.js",
|
||||
"js/pages/extensions.js",
|
||||
"js/pages/payments.js",
|
||||
"js/pages/node.js",
|
||||
"js/pages/node-public.js",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{% include('pages/payments.vue') %} {% include('pages/node.vue') %} {%
|
||||
include('pages/audit.vue') %} {% include('pages/wallets.vue') %} {%
|
||||
include('pages/users.vue') %} {% include('pages/admin.vue') %} {%
|
||||
include('pages/account.vue') %} {% include('pages/extensions_builder.vue') %}
|
||||
include('pages/account.vue') %} {% include('pages/extensions_builder.vue') %} {%
|
||||
include('pages/extensions.vue') %}
|
||||
|
|
|
|||
1092
lnbits/templates/pages/extensions.vue
Normal file
1092
lnbits/templates/pages/extensions.vue
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -96,6 +96,7 @@
|
|||
],
|
||||
"components": [
|
||||
"js/pages/extensions_builder.js",
|
||||
"js/pages/extensions.js",
|
||||
"js/pages/payments.js",
|
||||
"js/pages/node.js",
|
||||
"js/pages/node-public.js",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue