Compare commits

...
12 Commits
Author SHA1 Message Date
ArcandGitHub ca41a92bd9 version update (#1715) 2023-05-19 19:09:17 +01:00
dni ⚡andGitHub f97c698917 FIX: qrcode scanner on legends, by updating the library (#1714) 2023-05-19 13:23:22 +01:00
dni ⚡andGitHub dd1c17f86f FIX: internal qrcode scanning check should always check for lowercase (#1713)
fixup
2023-05-19 12:41:58 +01:00
Tiago VasconcelosandGitHub 587b763f03 Add user role (#1712)
* Add user role

* make format
2023-05-19 09:32:02 +02:00
callebtcandGitHub e39241a9e9 Fix: get_payments crud default Filter values. (#1703)
* get_payments: deafult values for filter

* lololololoolol
2023-05-11 18:36:20 +02:00
callebtc b6223f0e6d get_payments: deafult values for filter 2023-05-11 18:29:08 +02:00
6f2771e334 Merges extensions into one page (#1656)
* Merged extensions into one page

* Bundle files updated

* Fixed install bug

* feat: client side version compatibility check

* fix: hide `Activated/Deactivated` toggle for non-admins

* feat: translate labels to `EN`

* feat: add other language translations

* chore: update bundle for i18n

* feat: check extension version server-side

* feat: show warning message

* refactor: nicer mapping

Co-authored-by: dni  <office@dnilabs.com>

* chore: code format

* chore: extra log

* feat: check_latest_version of ext

* feat: show tooltip for new version

* chore: `make bundle`

* chore: `mypy`

* chore: code clean-up

* feat: show version in badge (spacing is fine)

* chore: make bundle

* feat: check `min_lnbits_version` and `warning` in `config.json`

* chore: code formatting

* chore: downgrade log level

* fix: extract `ExtensionsInstallSettings` as readonly

* fix: do not show installed and deactivated extensions

* chore: format

* fix: `Enable` button tooltip

* fix: set installed release after installation

* fix: hide deactivated extensions from regular users

* bundle fundle

* bundle fundle

---------

Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
Co-authored-by: dni  <office@dnilabs.com>
2023-05-11 01:14:07 +01:00
3fe33dfb81 Translated en.js to Polish (#1701)
* Translated en.js to Polish

* Minor pl.js fixes

* Added to language menu

* Update bundle

---------

Co-authored-by: ben <ben@arc.wales>
2023-05-11 01:01:35 +01:00
4ec16b56bf Extend bar for mobile (#1544)
* extend bar for mobile

* smaller padding

* center buttons

* Added rounded style

* Added icons, looks better

* service worker

* Changed icon

* Fixed show if

---------

Co-authored-by: ben <ben@arc.wales>
2023-05-11 00:12:48 +01:00
ArcandGitHub 662f792d4f Added Welsh (#1696)
* Added Welsh

* format

* Added to base

* bundle fundle
2023-05-10 11:18:18 +01:00
dni ⚡andGitHub d8ccb4dcd8 fix old payments endpoint for lndhub (#1698)
* fix old payments endpoint for lndhub

* fixup tests
2023-05-10 11:47:04 +02:00
7e0beb508c Adding IT properly (#1695)
* Adding IT properly

* added to base + format + bundle

---------

Co-authored-by: ben <ben@arc.wales>
2023-05-10 10:33:17 +01:00
28 changed files with 12761 additions and 696 deletions
+5 -6
View File
@@ -426,13 +426,12 @@ async def get_payments(
Filters payments to be returned by complete | pending | outgoing | incoming.
"""
if not filters:
filters = Filters()
filters = filters or Filters()
if limit:
filters.limit = limit
if offset:
filters.offset = offset
filters.sortby = filters.sortby or "time"
filters.direction = filters.direction or "desc"
filters.limit = limit or filters.limit
filters.offset = offset or filters.offset
page = await get_payments_paginated(
wallet_id=wallet_id,
+2 -2
View File
@@ -166,7 +166,7 @@ async def pay_invoice(
assert internal_invoice is not None
if (
internal_invoice.amount != invoice.amount_msat
or internal_invoice.bolt11 != payment_request
or internal_invoice.bolt11 != payment_request.lower()
):
raise PaymentFailure("Invalid invoice.")
@@ -482,7 +482,7 @@ def update_cached_settings(sets_dict: dict):
try:
setattr(settings, key, value)
except:
logger.error(f"error overriding setting: {key}, value: {value}")
logger.warning(f"Failed overriding setting: {key}, value: {value}")
if "super_user" in sets_dict:
setattr(settings, "super_user", sets_dict["super_user"])
+1 -1
View File
@@ -1,6 +1,6 @@
// update cache version every time there is a new deployment
// so the service worker reinitializes the cache
const CACHE_VERSION = 8
const CACHE_VERSION = 18
const CURRENT_CACHE = `lnbits-${CACHE_VERSION}-`
const getApiKey = request => {
+541 -49
View File
@@ -1,46 +1,70 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block scripts %} {{ window_vars(user) }}
<script src="/core/static/js/extensions.js"></script>
{% endblock %} {% block page %}
%} {{ window_vars(user, extensions) }}{% block page %}
<div class="row q-col-gutter-md q-mb-md">
<div class="col-sm-9 gt-sm col-xs-12 mt-lg">
<p class="text-h4">
Extensions
<q-btn
flat
color="primary"
type="a"
:href="['/install?usr=', user.id].join('')"
:label="$t('manage_extensions')"
></q-btn>
</p>
<div class="col-sm-9 col-xs-12">
<p class="text-h4 gt-sm">{%raw%}{{ $t('extensions') }}{%endraw%}</p>
</div>
<div class="col-sm-3 col-xs-12 q-ml-auto">
<q-input v-model="searchTerm" label="Search extensions">
<q-input v-model="searchTerm" :label="$t('search_extensions')">
<q-icon
v-if="searchTerm !== ''"
name="close"
@click="searchTerm = ''"
class="cursor-pointer q-mt-lg"
class="cursor-pointer"
/>
</q-input>
</div>
</div>
<p v-if="!filteredExtensions.length" v-text="$t('no_extensions')"></p>
<div class="row q-col-gutter-md q-mb-md">
<div class="col-12">
<q-card>
<div class="q-pa-xs">
<div class="q-gutter-y-md">
<q-tabs
v-model="tab"
@input="handleTabChanged"
active-color="primary"
align="left"
>
<q-tab
name="installed"
:label="$t('installed')"
@update="val => tab = val.name"
></q-tab>
<q-tab
name="all"
:label="$t('all')"
@update="val => tab = val.name"
></q-tab>
<q-tab
name="featured"
:label="$t('featured')"
@update="val => tab = val.name"
></q-tab>
<i v-if="!g.user.admin && tab != 'installed'"
>{%raw%}{{ $t('only_admins_can_install') }}{%endraw%}</i
>
</q-tabs>
</div>
</div>
</q-card>
</div>
</div>
<div class="row q-col-gutter-md">
<div
class="col-6 col-md-4 col-lg-3"
v-for="extension in filteredExtensions"
:key="extension.code"
:key="extension.id + extension.hash"
>
<q-card>
<q-card-section style="min-height: 140px">
<q-card-section style="min-height: 140px" class="q-pb-none">
<div class="row">
<div class="col-3">
<q-img
v-if="extension.tile"
:src="extension.tile"
v-if="extension.icon"
:src="extension.icon"
spinner-color="white"
style="max-width: 100%"
></q-img>
@@ -60,6 +84,16 @@
</div>
</div>
<div class="col-9 q-pl-sm">
<q-badge
v-if="hasNewVersion(extension)"
color="green"
class="float-right"
>
<small>{%raw%}{{ $t('new_version') }}{%endraw%}</small>
<q-tooltip
><span v-text="extension.latestRelease.version"></span
></q-tooltip>
</q-badge>
{% raw %}
<div class="text-h5 gt-sm q-mt-sm q-mb-xs gt-sm">
{{ extension.name }}
@@ -88,6 +122,21 @@
{% endraw %}
</div>
</div>
<div class="row q-pt-sm">
<div class="col">
<small v-if="extension.dependencies?.length"
>{%raw%}{{ $t('extension_depends_on') }}{%endraw%}</small
>
<small v-else>&nbsp;</small>
<q-badge
v-for="dep in extension.dependencies"
:key="dep"
color="orange"
>
<small v-text="dep"></small>
</q-badge>
</div>
</div>
</q-card-section>
<q-card-section>
<div>
@@ -98,47 +147,490 @@
size="1.5em"
:max="5"
color="primary"
></q-rating>
><q-tooltip
>{%raw%}{{ $t('extension_rating_soon') }}{%endraw%}</q-tooltip
></q-rating
>
<q-rating
v-model="maxStars"
class="lt-md"
size="1.5em"
:max="5"
color="primary"
></q-rating
><q-tooltip>Ratings coming soon</q-tooltip>
><q-tooltip
>{%raw%}{{ $t('extension_rating_soon') }}{%endraw%}</q-tooltip
></q-rating
>
<q-toggle
v-if="extension.isAvailable && extension.isInstalled && g.user.admin"
:label="extension.isActive ? $t('activated'): $t('deactivated') "
color="secodary"
style="max-height: 21px"
v-model="extension.isActive"
@input="toggleExtension(extension)"
><q-tooltip
>{%raw%}{{ $t('activate_extension_details')
}}{%endraw%}</q-tooltip
></q-toggle
>
</div>
</q-card-section>
<q-separator></q-separator>
<q-card-actions>
<div v-if="extension.isEnabled">
<q-btn
flat
color="primary"
type="a"
:href="[extension.url, '?usr=', g.user.id].join('')"
>Open</q-btn
<q-card-actions style="min-height: 52px">
<div class="col-10">
<div v-if="!extension.inProgress">
<q-btn
v-if="user.extensions.includes(extension.id) && extension.isActive && extension.isInstalled"
flat
color="primary"
type="a"
:href="[extension.id, '?usr=', g.user.id].join('')"
>{%raw%}{{ $t('open') }}{%endraw%}</q-btn
>
<q-btn
v-if="user.extensions.includes(extension.id) && extension.isActive && extension.isInstalled"
flat
color="grey-5"
type="a"
:href="['{{
url_for('install.extensions')
}}', '?usr=', g.user.id, '&disable=', extension.id].join('')"
>
{%raw%}{{ $t('disable') }}{%endraw%}</q-btn
>
<q-btn
v-if="extension.isInstalled && !user.extensions.includes(extension.id) && extension.isActive"
flat
color="primary"
type="a"
:href="['{{
url_for('install.extensions')
}}', '?usr=', g.user.id, '&enable=', extension.id].join('')"
>
{%raw%}{{ $t('enable') }}{%endraw%}
<q-tooltip>
<span v-text="$t('enable_extension_details')">
</span> </q-tooltip
></q-btn>
<q-btn
@click="showUpgrade(extension)"
flat
color="primary"
v-if="g.user.admin"
>
{%raw%}{{ $t('manage') }}{%endraw%}<q-tooltip
>{%raw%}{{ $t('manage_extension_details')
}}{%endraw%}</q-tooltip
></q-btn
>
</div>
<div v-else>
<q-spinner color="primary" size="2.55em"></q-spinner>
</div>
</div>
<div class="col-2">
<div
v-if="extension.isInstalled && extension.installedRelease"
class="float-right"
>
<q-btn
flat
color="grey-5"
type="a"
:href="['{{ url_for('core.extensions') }}', '?usr=', g.user.id, '&disable=', extension.code].join('')"
>
Disable</q-btn
>
<q-badge>
{% raw %}{{ extension.installedRelease.version }}{% endraw
%}<q-tooltip
>{%raw%}{{ $t('extension_installed_version')
}}{%endraw%}</q-tooltip
>
</q-badge>
</div>
</div>
<q-btn
v-else
flat
color="primary"
type="a"
:href="['{{ url_for('core.extensions') }}', '?usr=', g.user.id, '&enable=', extension.code].join('')"
>
Enable</q-btn
>
</q-card-actions>
</q-card>
</div>
<q-dialog v-model="showUninstallDialog">
<q-card class="q-pa-lg">
<h6 class="q-my-md text-primary">{%raw%}{{ $t('warning') }}{%endraw%}</h6>
<p>
{%raw%}{{ $t('extension_uninstall_warning') }}{%endraw%} <br />
{%raw%}{{ $t('confirm_continue') }}{%endraw%}
</p>
<div class="row q-mt-lg">
<q-btn outline color="grey" @click="uninstallExtension()"
>{%raw%}{{ $t('uninstall_confirm') }}{%endraw%}</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
>{%raw%}{{ $t('cancel') }}{%endraw%}</q-btn
>
</div>
</q-card>
</q-dialog>
<q-dialog v-model="showUpgradeDialog">
<q-card class="q-pa-lg lnbits__dialog-card">
<q-card-section>
<div class="text-h6" v-text="selectedExtension?.name"></div>
</q-card-section>
<div class="col-12 col-md-5 q-gutter-y-md" v-if="selectedExtensionRepos">
<q-card
flat
bordered
class="my-card"
v-for="repoName of Object.keys(selectedExtensionRepos)"
:key="repoName"
>
<q-expansion-item
:key="repoName"
group="repos"
:caption="repoName"
:content-inset-level="0.5"
:default-opened="selectedExtensionRepos[repoName].isInstalled"
>
<template v-slot:header>
<q-item-section avatar>
<q-avatar
:icon="selectedExtensionRepos[repoName].isInstalled ? 'download_done': 'download'"
:text-color="selectedExtensionRepos[repoName].isInstalled ? 'green' : ''"
/>
</q-item-section>
<q-item-section>
<div class="row">
<div class="col-10">
{%raw%}{{ $t('repository') }}{%endraw%}
<br />
<small v-text="repoName"></small>
</div>
<div class="col-2"></div>
</div>
</q-item-section>
</template>
<q-card-section class="q-pa-none">
<q-separator></q-separator>
<q-list>
<q-expansion-item
v-for="release of selectedExtensionRepos[repoName].releases"
:key="release.version"
group="releases"
@click="getGitHubReleaseDetails(release)"
:icon="getReleaseIcon(release)"
:label="release.description"
:caption="release.version"
:content-inset-level="0.5"
:header-class="getReleaseIconColor(release)"
>
<div v-if="release.inProgress">
<q-spinner color="primary" size="2.55em"></q-spinner>
</div>
<div v-else-if="release.error">
<q-icon
class="gt-sm"
name="error"
color="pink"
size="70px"
></q-icon>
Cannot get the release details.
</div>
<q-card v-else>
<q-card-section v-if="release.is_version_compatible">
<q-btn
v-if="!release.isInstalled"
@click="installExtension(release)"
color="primary unelevated mt-lg pt-lg"
>{%raw%}{{ $t('install') }}{%endraw%}</q-btn
>
<q-btn v-else @click="showUninstall()" flat color="red">
{%raw%}{{ $t('uninstall') }}{%endraw%}</q-btn
>
<a
v-if="release.html_url"
class="text-secondary float-right"
:href="release.html_url"
target="_blank"
style="color: inherit"
>{%raw%}{{ $t('release_notes') }}{%endraw%}</a
>
</q-card-section>
<q-card-section v-else>
{%raw%}{{ $t('extension_min_lnbits_version') }}{%endraw%}
<strong>
<span v-text="release.min_lnbits_version"></span>
</strong>
</q-card-section>
<q-card v-if="release.warning">
<q-card-section>
<div class="text-h6">
<q-badge color="yellow" text-color="black">
{%raw%}{{ $t('warning') }}{%endraw%}
</q-badge>
</div>
<div class="text-subtitle2">
<span v-text="release.warning"></span>
</div>
</q-card-section>
</q-card>
<q-separator></q-separator> </q-card
></q-expansion-item>
</q-list>
</q-card-section>
</q-expansion-item>
</q-card>
</div>
<q-spinner v-else color="primary" size="2.55em"></q-spinner>
<div class="row q-mt-lg">
<q-btn
v-if="selectedExtension?.isInstalled"
@click="showUninstall()"
flat
color="red"
>
{%raw%}{{ $t('uninstall') }}{%endraw%}</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">
{%raw%}{{ $t('close') }}{%endraw%}</q-btn
>
</div>
</q-card>
</q-dialog>
</div>
{% endblock %} {% block scripts %} {{ window_vars(user) }}
<script>
new Vue({
el: '#vue',
data: function () {
return {
searchTerm: '',
tab: 'all',
filteredExtensions: null,
showUninstallDialog: false,
showUpgradeDialog: false,
selectedExtension: null,
selectedExtensionRepos: null,
maxStars: 5,
user: null
}
},
watch: {
searchTerm(term) {
this.filterExtensions(term, this.tab)
}
},
methods: {
handleTabChanged: function (tab) {
this.filterExtensions(this.searchTerm, tab)
},
filterExtensions: function (term, tab) {
// Filter the extensions list
function extensionNameContains(searchTerm) {
return function (extension) {
return (
extension.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
extension.shortDescription
?.toLowerCase()
.includes(searchTerm.toLowerCase())
)
}
}
this.filteredExtensions = this.extensions
.filter(e => (tab === 'installed' ? e.isInstalled : true))
.filter(e =>
tab === 'installed'
? e.isActive
? true
: !!this.g.user.admin
: true
)
.filter(e => (tab === 'featured' ? e.isFeatured : true))
.filter(extensionNameContains(term))
this.tab = tab
},
installExtension: async function (release) {
const extension = this.selectedExtension
extension.inProgress = true
this.showUpgradeDialog = false
LNbits.api
.request(
'POST',
`/api/v1/extension?usr=${this.g.user.id}`,
this.g.user.wallets[0].adminkey,
{
ext_id: extension.id,
archive: release.archive,
source_repo: release.source_repo
}
)
.then(response => {
extension.isAvailable = true
extension.isInstalled = true
extension.installedRelease = release
this.toggleExtension(extension)
extension.inProgress = false
this.filteredExtensions = this.extensions.concat([])
this.handleTabChanged('installed')
this.tab = 'installed'
})
.catch(err => {
LNbits.utils.notifyApiError(err)
extension.inProgress = false
})
},
uninstallExtension: async function () {
const extension = this.selectedExtension
this.showUpgradeDialog = false
this.showUninstallDialog = false
extension.inProgress = true
LNbits.api
.request(
'DELETE',
`/api/v1/extension/${extension.id}?usr=${this.g.user.id}`,
this.g.user.wallets[0].adminkey
)
.then(response => {
extension.isAvailable = false
extension.isInstalled = false
extension.inProgress = false
extension.installedRelease = null
this.filteredExtensions = this.extensions.concat([])
this.handleTabChanged('installed')
this.tab = 'installed'
})
.catch(err => {
LNbits.utils.notifyApiError(err)
extension.inProgress = false
})
},
toggleExtension: function (extension) {
const action = extension.isActive ? 'activate' : 'deactivate'
LNbits.api
.request(
'GET',
"{{ url_for('install.extensions') }}?usr=" +
this.g.user.id +
'&' +
action +
'=' +
extension.id
)
.then(response => {})
.catch(err => {
LNbits.utils.notifyApiError(err)
extension.inProgress = false
})
},
showUninstall: function () {
this.showUpgradeDialog = false
this.showUninstallDialog = true
},
showUpgrade: async function (extension) {
this.selectedExtension = extension
this.showUpgradeDialog = true
this.selectedExtensionRepos = null
try {
const {data} = await LNbits.api.request(
'GET',
`/api/v1/extension/${extension.id}/releases?usr=${this.g.user.id}`,
this.g.user.wallets[0].adminkey
)
this.selectedExtensionRepos = data.reduce((repos, release) => {
repos[release.source_repo] = repos[release.source_repo] || {
releases: [],
isInstalled: false
}
release.inProgress = false
release.error = null
release.loaded = false
release.isInstalled = this.isInstalledVersion(
this.selectedExtension,
release
)
if (release.isInstalled) {
repos[release.source_repo].isInstalled = true
}
repos[release.source_repo].releases.push(release)
return repos
}, {})
} catch (error) {
LNbits.utils.notifyApiError(error)
extension.inProgress = false
}
},
hasNewVersion: function (extension) {
if (extension.installedRelease && extension.latestRelease) {
return (
extension.installedRelease.version !==
extension.latestRelease.version
)
}
},
isInstalledVersion: function (extension, release) {
if (extension.installedRelease) {
return (
extension.installedRelease.source_repo === release.source_repo &&
extension.installedRelease.version === release.version
)
}
},
getReleaseIcon: function (release) {
if (!release.is_version_compatible) return 'block'
if (release.isInstalled) return 'download_done'
return 'download'
},
getReleaseIconColor: function (release) {
if (!release.is_version_compatible) return 'text-red'
if (release.isInstalled) return 'text-green'
return ''
},
getGitHubReleaseDetails: async function (release) {
if (!release.is_github_release || release.loaded) {
return
}
const [org, repo] = release.source_repo.split('/')
release.inProgress = true
try {
const {data} = await LNbits.api.request(
'GET',
`/api/v1/extension/release/${org}/${repo}/${release.version}?usr=${this.g.user.id}`,
this.g.user.wallets[0].adminkey
)
release.loaded = true
release.is_version_compatible = data.is_version_compatible
release.min_lnbits_version = data.min_lnbits_version
release.warning = data.warning
} catch (error) {
console.warn(error)
release.error = error
LNbits.utils.notifyApiError(error)
} finally {
release.inProgress = false
}
}
},
created: function () {
this.extensions = JSON.parse('{{extensions | tojson | safe}}').map(e => ({
...e,
inProgress: false
}))
this.filteredExtensions = this.extensions.concat([])
for (let i = 0; i < this.filteredExtensions.length; i++) {
if (this.filteredExtensions[i].isInstalled != false) {
this.handleTabChanged('installed')
this.tab = 'installed'
}
}
if (window.user) {
this.user = LNbits.map.user(window.user)
}
},
mixins: [windowMixin]
})
</script>
{% endblock %}
-505
View File
@@ -1,505 +0,0 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {{ window_vars(user, extensions) }}{% block page %}
<div class="row q-col-gutter-md q-mb-md">
<div class="col-sm-9 col-xs-12">
<p class="text-h4 gt-sm">Manage Extensions</p>
</div>
<div class="col-sm-3 col-xs-12 q-ml-auto">
<q-input v-model="searchTerm" label="Search extensions">
<q-icon
v-if="searchTerm !== ''"
name="close"
@click="searchTerm = ''"
class="cursor-pointer"
/>
</q-input>
</div>
</div>
<div class="row q-col-gutter-md q-mb-md">
<div class="col-12">
<q-card>
<div class="q-pa-xs">
<div class="q-gutter-y-md">
<q-tabs
v-model="tab"
@input="handleTabChanged"
active-color="primary"
align="left"
>
<q-tab
name="installed"
label="Installed"
@update="val => tab = val.name"
></q-tab>
<q-tab
name="all"
label="All"
@update="val => tab = val.name"
></q-tab>
<q-tab
name="featured"
label="Featured"
@update="val => tab = val.name"
></q-tab>
</q-tabs>
</div>
</div>
</q-card>
</div>
</div>
<div class="row q-col-gutter-md">
<div
class="col-6 col-md-4 col-lg-3"
v-for="extension in filteredExtensions"
:key="extension.id + extension.hash"
>
<q-card>
<q-card-section style="min-height: 140px">
<div class="row">
<div class="col-3">
<q-img
v-if="extension.icon"
:src="extension.icon"
spinner-color="white"
style="max-width: 100%"
></q-img>
<div v-else>
<q-icon
class="gt-sm"
name="extension"
color="primary"
size="70px"
></q-icon>
<q-icon
class="lt-md"
name="extension"
color="primary"
size="35px"
></q-icon>
</div>
</div>
<div class="col-9 q-pl-sm">
<q-badge
v-if="hasNewVersion(extension)"
color="green"
class="float-right"
>
<small>New Version</small>
</q-badge>
{% raw %}
<div class="text-h5 gt-sm q-mt-sm q-mb-xs gt-sm">
{{ extension.name }}
</div>
<div
class="text-h5 gt-sm q-mt-sm q-mb-xs lt-md"
style="min-height: 60px"
>
{{ extension.name }}
</div>
<div
class="text-subtitle2 gt-sm"
style="font-size: 11px; height: 34px"
>
{{ extension.shortDescription }}
</div>
<div class="text-subtitle1 lt-md q-mt-sm q-mb-xs">
{{ extension.name }}
</div>
<div
class="text-subtitle2 lt-md"
style="font-size: 9px; height: 34px"
>
{{ extension.shortDescription }}
</div>
{% endraw %}
</div>
</div>
<div class="row q-pt-sm">
<div class="col">
<small v-if="extension.dependencies?.length">Depends on:</small>
<small v-else>&nbsp;</small>
<q-badge
v-for="dep in extension.dependencies"
:key="dep"
color="orange"
>
<small v-text="dep"></small>
</q-badge>
</div>
</div>
</q-card-section>
<q-card-section>
<div>
<q-rating
class="gt-sm"
v-model="maxStars"
disable
size="1.5em"
:max="5"
color="primary"
></q-rating>
<q-rating
v-model="maxStars"
class="lt-md"
size="1.5em"
:max="5"
color="primary"
></q-rating
><q-tooltip>Ratings coming soon</q-tooltip>
</div>
</q-card-section>
<q-separator v-if="g.user.admin"></q-separator>
<q-card-actions>
<div class="col-10">
<div v-if="g.user.admin">
<div v-if="!extension.inProgress">
<q-btn @click="showUpgrade(extension)" flat color="primary">
Manage</q-btn
>
<q-toggle
v-if="extension.isAvailable && extension.isInstalled"
:label="extension.isActive ? 'Activated': 'Deactivated' "
color="secodary"
v-model="extension.isActive"
@input="toggleExtension(extension)"
></q-toggle>
</div>
<div v-else>
<q-spinner color="primary" size="2.55em"></q-spinner>
</div>
</div>
</div>
<div class="col-2">
<div class="float-right"></div>
</div>
</q-card-actions>
</q-card>
</div>
<q-dialog v-model="showUninstallDialog">
<q-card class="q-pa-lg">
<h6 class="q-my-md text-primary">Warning</h6>
<p>
You are about to remove the extension for all users. <br />
Are you sure you want to continue?
</p>
<div class="row q-mt-lg">
<q-btn outline color="grey" @click="uninstallExtension()"
>Yes, Uninstall</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
</div>
</q-card>
</q-dialog>
<q-dialog v-model="showUpgradeDialog">
<q-card class="q-pa-lg lnbits__dialog-card">
<q-card-section>
<div class="text-h6" v-text="selectedExtension?.name"></div>
</q-card-section>
<div class="col-12 col-md-5 q-gutter-y-md" v-if="selectedExtensionRepos">
<q-card
flat
bordered
class="my-card"
v-for="repoName of Object.keys(selectedExtensionRepos)"
:key="repoName"
>
<q-expansion-item
:key="repoName"
group="repos"
:caption="repoName"
:content-inset-level="0.5"
:default-opened="selectedExtensionRepos[repoName].isInstalled"
>
<template v-slot:header>
<q-item-section avatar>
<q-avatar
:icon="selectedExtensionRepos[repoName].isInstalled ? 'download_done': 'download'"
:text-color="selectedExtensionRepos[repoName].isInstalled ? 'green' : ''"
/>
</q-item-section>
<q-item-section>
<div class="row">
<div class="col-10">
Repository
<br />
<small v-text="repoName"></small>
</div>
<div class="col-2">
<!-- <div v-if="selectedExtension.stars" class="float-right">
<small v-text="selectedExtension.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>
</div>
</q-item-section>
</template>
<q-card-section class="q-pa-none">
<q-separator></q-separator>
<q-list>
<q-expansion-item
v-for="release of selectedExtensionRepos[repoName].releases"
:key="release.version"
group="releases"
:icon="release.isInstalled ? 'download_done' : 'download'"
:label="release.description"
:caption="release.version"
:content-inset-level="0.5"
:header-class="release.isInstalled ? 'text-green' : ''"
>
<q-card>
<q-card-section>
<q-btn
v-if="!release.isInstalled"
@click="installExtension(release)"
color="primary unelevated mt-lg pt-lg"
>Install</q-btn
>
<q-btn v-else @click="showUninstall()" flat color="red">
Uninstall</q-btn
>
<a
v-if="release.html_url"
class="text-secondary float-right"
:href="release.html_url"
target="_blank"
style="color: inherit"
>Release Notes</a
>
</q-card-section>
<div
v-if="release.details_html"
v-html="release.details_html"
></div>
<q-separator></q-separator> </q-card
></q-expansion-item>
</q-list>
</q-card-section>
</q-expansion-item>
</q-card>
</div>
<q-spinner v-else color="primary" size="2.55em"></q-spinner>
<div class="row q-mt-lg">
<q-btn
v-if="selectedExtension?.isInstalled"
@click="showUninstall()"
flat
color="red"
>
Uninstall</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div>
</q-card>
</q-dialog>
</div>
{% endblock %} {% block scripts %} {{ window_vars(user) }}
<script>
new Vue({
el: '#vue',
data: function () {
return {
searchTerm: '',
tab: 'all',
filteredExtensions: null,
showUninstallDialog: false,
showUpgradeDialog: false,
selectedExtension: null,
selectedExtensionRepos: null,
maxStars: 5
}
},
watch: {
searchTerm(term) {
this.filterExtensions(term, this.tab)
}
},
methods: {
handleTabChanged: function (tab) {
this.filterExtensions(this.searchTerm, tab)
},
filterExtensions: function (term, tab) {
// Filter the extensions list
function extensionNameContains(searchTerm) {
return function (extension) {
return (
extension.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
extension.shortDescription
?.toLowerCase()
.includes(searchTerm.toLowerCase())
)
}
}
this.filteredExtensions = this.extensions
.filter(e => (tab === 'installed' ? e.isInstalled : true))
.filter(e => (tab === 'featured' ? e.isFeatured : true))
.filter(extensionNameContains(term))
},
installExtension: async function (release) {
const extension = this.selectedExtension
extension.inProgress = true
this.showUpgradeDialog = false
LNbits.api
.request(
'POST',
`/api/v1/extension?usr=${this.g.user.id}`,
this.g.user.wallets[0].adminkey,
{
ext_id: extension.id,
archive: release.archive,
source_repo: release.source_repo
}
)
.then(response => {
extension.isAvailable = true
extension.isInstalled = true
this.toggleExtension(extension)
extension.inProgress = false
this.filteredExtensions = this.extensions.concat([])
this.handleTabChanged('installed')
this.tab = 'installed'
})
.catch(err => {
LNbits.utils.notifyApiError(err)
extension.inProgress = false
})
},
uninstallExtension: async function () {
const extension = this.selectedExtension
this.showUpgradeDialog = false
this.showUninstallDialog = false
extension.inProgress = true
LNbits.api
.request(
'DELETE',
`/api/v1/extension/${extension.id}?usr=${this.g.user.id}`,
this.g.user.wallets[0].adminkey
)
.then(response => {
extension.isAvailable = false
extension.isInstalled = false
extension.inProgress = false
this.filteredExtensions = this.extensions.concat([])
this.handleTabChanged('installed')
this.tab = 'installed'
})
.catch(err => {
LNbits.utils.notifyApiError(err)
extension.inProgress = false
})
},
toggleExtension: function (extension) {
const action = extension.isActive ? 'activate' : 'deactivate'
LNbits.api
.request(
'GET',
"{{ url_for('install.extensions') }}?usr=" +
this.g.user.id +
'&' +
action +
'=' +
extension.id
)
.then(response => {})
.catch(err => {
LNbits.utils.notifyApiError(err)
extension.inProgress = false
})
},
showUninstall: function () {
this.showUpgradeDialog = false
this.showUninstallDialog = true
},
showUpgrade: async function (extension) {
this.selectedExtension = extension
this.showUpgradeDialog = true
this.selectedExtensionRepos = null
try {
const {data} = await LNbits.api.request(
'GET',
`/api/v1/extension/${extension.id}/releases?usr=${this.g.user.id}`,
this.g.user.wallets[0].adminkey
)
this.selectedExtensionRepos = data.reduce((repos, release) => {
repos[release.source_repo] = repos[release.source_repo] || {
releases: [],
isInstalled: false
}
release.isInstalled = this.isInstalledVersion(
this.selectedExtension,
release
)
if (release.isInstalled) {
repos[release.source_repo].isInstalled = true
}
repos[release.source_repo].releases.push(release)
return repos
}, {})
} catch (error) {
LNbits.utils.notifyApiError(error)
extension.inProgress = false
}
},
hasNewVersion: function (extension) {
if (extension.installedRelease && extension.latestRelease) {
return (
extension.installedRelease.version !==
extension.latestRelease.version
)
}
},
isInstalledVersion: function (extension, release) {
if (extension.installedRelease) {
return (
extension.installedRelease.source_repo === release.source_repo &&
extension.installedRelease.version === release.version
)
}
}
},
created: function () {
if (!this.g.user.admin) {
this.$q.notify({
timeout: 3000,
message: 'Only admin accounts can install extensions',
icon: null
})
}
this.extensions = JSON.parse('{{extensions | tojson | safe}}').map(e => ({
...e,
inProgress: false
}))
this.filteredExtensions = this.extensions.concat([])
for (let i = 0; i < this.filteredExtensions.length; i++) {
if (this.filteredExtensions[i].isInstalled != false) {
this.handleTabChanged('installed')
this.tab = 'installed'
}
}
},
mixins: [windowMixin]
})
</script>
{% endblock %}
+13 -9
View File
@@ -779,23 +779,27 @@
</q-card-section>
</q-card>
</q-dialog>
<q-tabs
class="lt-md fixed-bottom left-0 right-0 bg-primary text-white shadow-2 z-top"
active-class="px-0"
indicator-color="transparent"
align="justify"
>
<q-tab
icon="account_balance_wallet"
label="Wallets"
@click="g.visibleDrawer = !g.visibleDrawer"
>
</q-tab>
<q-tab icon="content_paste" label="Paste" @click="showParseDialog">
</q-tab>
<q-tab icon="file_download" label="Receive" @click="showReceiveDialog">
</q-tab>
<q-tab icon="photo_camera" label="Scan" @click="showCamera"> </q-tab>
<q-tab @click="showParseDialog" icon="file_upload" label="Send">
</q-tab>
<q-btn
round
size="35px"
unelevated
icon="qr_code_scanner"
@click="showCamera"
class="text-white bg-primary z-top vertical-bottom absolute-center absolute"
>
</q-btn>
</q-tabs>
<q-dialog v-model="disclaimerDialog.show">
+28
View File
@@ -48,6 +48,7 @@ from lnbits.extension_manager import (
Extension,
ExtensionRelease,
InstallableExtension,
fetch_github_release_config,
get_valid_extensions,
)
from lnbits.helpers import generate_filter_params_openapi, url_for
@@ -779,6 +780,11 @@ async def api_install_extension(
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"
)
ext_info = InstallableExtension(
id=data.ext_id, name=data.ext_id, installed_release=release, icon=release.icon
)
@@ -851,6 +857,7 @@ async def api_uninstall_extension(ext_id: str, user: User = Depends(check_admin)
ext_info.clean_extension_files()
await delete_installed_extension(ext_id=ext_info.id)
logger.success(f"Extension '{ext_id}' uninstalled.")
except Exception as ex:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(ex)
@@ -874,6 +881,27 @@ async def get_extension_releases(ext_id: str):
)
@core_app.get(
"/api/v1/extension/release/{org}/{repo}/{tag_name}",
dependencies=[Depends(check_admin)],
)
async def get_extension_release(org: str, repo: str, tag_name: str):
try:
config = await fetch_github_release_config(org, repo, tag_name)
if not config:
return {}
return {
"min_lnbits_version": config.min_lnbits_version,
"is_version_compatible": config.is_version_compatible(),
"warning": config.warning,
}
except Exception as ex:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(ex)
)
# TINYURL
+6 -21
View File
@@ -57,11 +57,13 @@ async def robots():
@core_html_routes.get(
"/extensions", name="core.extensions", response_class=HTMLResponse
"/extensions", name="install.extensions", response_class=HTMLResponse
)
async def extensions(
async def extensions_install(
request: Request,
user: User = Depends(check_user_exists),
activate: str = Query(None),
deactivate: str = Query(None),
enable: str = Query(None),
disable: str = Query(None),
):
@@ -69,24 +71,7 @@ async def extensions(
# Update user as his extensions have been updated
if enable or disable:
updated_user = await get_user(user.id)
assert updated_user, "User does not exist."
user = updated_user
return template_renderer().TemplateResponse(
"core/extensions.html", {"request": request, "user": user.dict()}
)
@core_html_routes.get(
"/install", name="install.extensions", response_class=HTMLResponse
)
async def extensions_install(
request: Request,
user: User = Depends(check_user_exists),
activate: str = Query(None),
deactivate: str = Query(None),
):
user = await get_user(user.id) # type: ignore
try:
installed_exts: List["InstallableExtension"] = await get_installed_extensions()
installed_exts_ids = [e.id for e in installed_exts]
@@ -153,7 +138,7 @@ async def extensions_install(
)
return template_renderer().TemplateResponse(
"core/install.html",
"core/extensions.html",
{
"request": request,
"user": user.dict(),
+77 -21
View File
@@ -12,6 +12,7 @@ from urllib import request
import httpx
from fastapi import HTTPException
from loguru import logger
from packaging import version
from pydantic import BaseModel
from lnbits.settings import settings
@@ -26,11 +27,17 @@ class ExplicitRelease(BaseModel):
dependencies: List[str] = []
icon: Optional[str]
short_description: Optional[str]
html_url: Optional[str]
details: Optional[str]
min_lnbits_version: Optional[str]
html_url: Optional[str] # todo: release_url
warning: Optional[str]
info_notification: Optional[str]
critical_notification: 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)
class GitHubRelease(BaseModel):
id: str
@@ -61,6 +68,13 @@ class ExtensionConfig(BaseModel):
name: str
short_description: str
tile: str = ""
warning: Optional[str] = ""
min_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 download_url(url, save_path):
@@ -117,6 +131,17 @@ async def fetch_github_releases(org: str, repo: str) -> List[GitHubRepoRelease]:
return [GitHubRepoRelease.parse_obj(r) for r in releases]
async def fetch_github_release_config(
org: str, repo: str, tag_name: str
) -> Optional[ExtensionConfig]:
config_url = (
f"https://raw.githubusercontent.com/{org}/{repo}/{tag_name}/config.json"
)
error_msg = "Cannot fetch GitHub extension config"
config = await gihub_api_get(config_url, error_msg)
return ExtensionConfig.parse_obj(config)
async def gihub_api_get(url: str, error_msg: Optional[str]) -> Any:
async with httpx.AsyncClient() as client:
headers = (
@@ -224,9 +249,11 @@ class ExtensionRelease(BaseModel):
source_repo: str
is_github_release: bool = False
hash: Optional[str] = None
min_lnbits_version: Optional[str] = None
is_version_compatible: Optional[bool] = True
html_url: Optional[str] = None
description: Optional[str] = None
details_html: Optional[str] = None
warning: Optional[str] = None
icon: Optional[str] = None
@classmethod
@@ -244,6 +271,24 @@ class ExtensionRelease(BaseModel):
html_url=r.html_url,
)
@classmethod
def from_explicit_release(
cls, source_repo: str, e: "ExplicitRelease"
) -> "ExtensionRelease":
return ExtensionRelease(
name=e.name,
version=e.version,
archive=e.archive,
hash=e.hash,
source_repo=source_repo,
description=e.short_description,
min_lnbits_version=e.min_lnbits_version,
is_version_compatible=e.is_version_compatible(),
warning=e.warning,
html_url=e.html_url,
icon=e.icon,
)
@classmethod
async def all_releases(cls, org: str, repo: str) -> List["ExtensionRelease"]:
try:
@@ -394,6 +439,15 @@ class InstallableExtension(BaseModel):
shutil.rmtree(self.ext_upgrade_dir, True)
def check_latest_version(self, release: Optional[ExtensionRelease]):
if not release:
return
if not self.latest_release:
self.latest_release = release
return
if version.parse(self.latest_release.version) < version.parse(release.version):
self.latest_release = release
@classmethod
def from_row(cls, data: dict) -> "InstallableExtension":
meta = json.loads(data["meta"])
@@ -451,18 +505,30 @@ class InstallableExtension(BaseModel):
manifest = await fetch_manifest(url)
for r in manifest.repos:
if r.id in extension_id_list:
continue
ext = await InstallableExtension.from_github_release(r)
if ext:
ext.featured = ext.id in manifest.featured
extension_list += [ext]
extension_id_list += [ext.id]
if not ext:
continue
existing_ext = next(
(ee for ee in extension_list if ee.id == r.id), None
)
if existing_ext:
existing_ext.check_latest_version(ext.latest_release)
continue
ext.featured = ext.id in manifest.featured
extension_list += [ext]
extension_id_list += [ext.id]
for e in manifest.extensions:
if e.id in extension_id_list:
release = ExtensionRelease.from_explicit_release(url, e)
existing_ext = next(
(ee for ee in extension_list if ee.id == e.id), None
)
if existing_ext:
existing_ext.check_latest_version(release)
continue
ext = InstallableExtension.from_explicit_release(e)
ext.check_latest_version(release)
ext.featured = ext.id in manifest.featured
extension_list += [ext]
extension_id_list += [e.id]
@@ -488,17 +554,7 @@ class InstallableExtension(BaseModel):
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.short_description,
details_html=e.details,
html_url=e.html_url,
icon=e.icon,
)
ExtensionRelease.from_explicit_release(url, e)
]
except Exception as e:
+4
View File
@@ -50,6 +50,9 @@ class ExtensionsSettings(LNbitsSettings):
"https://raw.githubusercontent.com/lnbits/lnbits-extensions/main/extensions.json"
]
)
class ExtensionsInstallSettings(LNbitsSettings):
lnbits_extensions_default_install: List[str] = Field(default=[])
# required due to GitHUb rate-limit
lnbits_ext_github_token: str = Field(default="")
@@ -279,6 +282,7 @@ class TransientSettings(InstalledExtensionsSettings):
class ReadOnlySettings(
EnvSettings,
ExtensionsInstallSettings,
SaaSSettings,
PersistenceSettings,
SuperUserSettings,
+1 -1
View File
File diff suppressed because one or more lines are too long
+9 -9
View File
File diff suppressed because one or more lines are too long
+33
View File
@@ -79,6 +79,39 @@ window.localisation.de = {
extensions: 'Erweiterungen',
no_extensions: 'Du hast noch keine Erweiterungen installiert :(',
created: 'Erstellt',
search_extensions: 'Sucherweiterungen',
warning: 'Warnung',
manage: 'Verwalten',
repository: 'Repository',
confirm_continue: 'Sind Sie sicher, dass Sie fortfahren möchten?',
manage_extension_details: 'Erweiterung installieren/deinstallieren',
install: 'Installieren',
uninstall: 'Deinstallieren',
open: 'Öffnen',
enable: 'Aktivieren',
enable_extension_details: 'Erweiterung für aktuellen Benutzer aktivieren',
disable: 'Deaktivieren',
installed: 'Installiert',
activated: 'Aktiviert',
deactivated: 'Deaktiviert',
release_notes: 'Versionshinweise',
activate_extension_details:
'Erweiterung für Benutzer verfügbar/nicht verfügbar machen',
featured: 'Vorgestellt',
all: 'Alle',
only_admins_can_install:
'(Nur Administratorkonten können Erweiterungen installieren)',
new_version: 'Neue Version',
extension_depends_on: 'Hängt ab von:',
extension_rating_soon: 'Bewertungen kommen bald',
extension_installed_version: 'Installierte Version',
extension_uninstall_warning:
'Sie sind dabei, die Erweiterung für alle Benutzer zu entfernen.',
uninstall_confirm: 'Ja, deinstallieren',
extension_min_lnbits_version:
'Diese Version erfordert mindestens die LNbits-Version',
payment_hash: 'Zahlungs-Hash',
fee: 'Gebühr',
amount: 'Menge',
+30 -1
View File
@@ -71,11 +71,40 @@ window.localisation.en = {
disclaimer_dialog:
'Login functionality to be released in a future update, for now, make sure you bookmark this page for future access to your wallet! This service is in BETA, and we hold no responsibility for people losing access to funds.',
no_transactions: 'No transactions made yet',
manage_extensions: 'Manage Extensions',
manage_server: 'Manage Server',
extensions: 'Extensions',
no_extensions: "You don't have any extensions installed :(",
created: 'Created',
search_extensions: 'Search extensions',
warning: 'Warning',
manage: 'Manage',
repository: 'Repository',
confirm_continue: 'Are you sure you want to continue?',
manage_extension_details: 'Install/uninstall extension',
install: 'Install',
uninstall: 'Uninstall',
open: 'Open',
enable: 'Enable',
enable_extension_details: 'Enable extension for current user',
disable: 'Disable',
installed: 'Installed',
activated: 'Activated',
deactivated: 'Deactivated',
release_notes: 'Release Notes',
activate_extension_details: 'Make extension available/unavailable for users',
featured: 'Featured',
all: 'All',
only_admins_can_install: '(Only admin accounts can install extensions)',
new_version: 'New Version',
extension_depends_on: 'Depends on:',
extension_rating_soon: 'Ratings coming soon',
extension_installed_version: 'Installed version',
extension_uninstall_warning:
'You are about to remove the extension for all users.',
uninstall_confirm: 'Yes, Uninstall',
extension_min_lnbits_version: 'This release requires at least LNbits version',
payment_hash: 'Payment Hash',
fee: 'Fee',
amount: 'Amount',
+33 -1
View File
@@ -72,11 +72,43 @@ window.localisation.es = {
disclaimer_dialog:
'La funcionalidad de inicio de sesión se lanzará en una actualización futura, por ahora, asegúrese de guardar esta página como marcador para acceder a su billetera en el futuro. Este servicio está en BETA y no asumimos ninguna responsabilidad por personas que pierdan el acceso a sus fondos.',
no_transactions: 'No hay transacciones todavía',
manage_extensions: 'Administrar extensiones',
manage_server: 'Administrar servidor',
extensions: 'Extensiones',
no_extensions: 'No tienes extensiones instaladas :(',
created: 'Creado',
search_extensions: 'Extensiones de búsqueda',
warning: 'Advertencia',
manage: 'Administrar',
repository: 'Repositorio',
confirm_continue: '¿Está seguro de que desea continuar?',
manage_extension_details: 'Instalar/desinstalar extensión',
install: 'Instalar',
uninstall: 'Desinstalar',
open: 'Abrir',
enable: 'Habilitar',
enable_extension_details: 'Habilitar extensión para el usuario actual',
disable: 'Deshabilitar',
installed: 'Instalado',
activated: 'Activado',
deactivated: 'Desactivado',
release_notes: 'Notas de la versión',
activate_extension_details:
'Hacer que la extensión esté disponible/no disponible para los usuarios',
featured: 'Destacado',
all: 'Todos',
only_admins_can_install:
'(Solo las cuentas de administrador pueden instalar extensiones)',
new_version: 'Nueva Versión',
extension_depends_on: 'Depende de:',
extension_rating_soon: 'Calificaciones próximamente',
extension_installed_version: 'Versión instalada',
extension_uninstall_warning:
'Está a punto de eliminar la extensión para todos los usuarios.',
uninstall_confirm: 'Sí, desinstalar',
extension_min_lnbits_version:
'Esta versión requiere al menos una versión de LNbits',
payment_hash: 'Hash de pago',
fee: 'Cuota',
amount: 'Cantidad',
+89
View File
@@ -0,0 +1,89 @@
window.localisation.it = {
server: 'Server',
theme: 'Tema',
funding: 'Funding',
users: 'Utenti',
restart: 'Riavvia il server',
save: 'Salva',
save_tooltip: 'Salva le modifiche',
topup: 'Ricarica',
topup_wallet: 'Ricarica un portafoglio',
topup_hint: "Usa l'ID del portafoglio per ricaricare qualsiasi portafoglio",
restart_tooltip: 'Riavvia il server affinché le modifiche abbiano effetto',
add_funds_tooltip: 'Aggiungere fondi a un portafoglio',
reset_defaults: 'Ripristina le impostazioni predefinite',
reset_defaults_tooltip:
'Cancella tutte le impostazioni e ripristina i valori predefiniti',
download_backup: 'Scarica il backup del database',
name_your_wallet: 'Dai un nome al tuo portafoglio %{name}',
paste_invoice_label:
'Incolla una fattura, una richiesta di pagamento o un codice lnurl *',
lnbits_description:
"Leggero e facile da configurare, LNbits può funzionare su qualsiasi fonte di finanziamento lightning-network, attualmente supporta LND, c-lightning, OpenNode, LNPay e persino LNbits stesso! Potete gestire LNbits per conto vostro o offrire facilmente una soluzione di custodia per altri. Ogni portafoglio ha le proprie chiavi API e non c'è limite al numero di portafogli che si possono creare. La possibilità di suddividere i fondi rende LNbits uno strumento utile per la gestione del denaro e come strumento di sviluppo. Le estensioni aggiungono ulteriori funzionalità a LNbits, consentendo di sperimentare una serie di tecnologie all'avanguardia sulla rete Lightning. Abbiamo reso lo sviluppo delle estensioni il più semplice possibile e, in quanto progetto libero e open-source, incoraggiamo le persone a sviluppare e inviare le proprie",
export_to_phone: 'Esportazione su telefono con codice QR',
export_to_phone_desc:
"Questo codice QR contiene l'URL del portafoglio con accesso da amministratore. È possibile scansionarlo dal telefono per aprire il portafoglio da lì.",
wallets: 'Portafogli',
add_wallet: 'Aggiungi un nuovo portafoglio',
delete_wallet: 'Elimina il portafoglio',
delete_wallet_desc:
"L'intero portafoglio sarà cancellato, i fondi saranno irrecuperabili",
rename_wallet: 'Rinomina il portafoglio',
update_name: 'Aggiorna il nome',
press_to_claim: 'Premi per richiedere bitcoin',
donate: 'Donazioni',
view_github: 'Visualizza su GitHub',
voidwallet_active: 'VoidWallet è attivo! Pagamenti disabilitati',
use_with_caution: 'USARE CON CAUTELA - %{nome} portafoglio è ancora in BETA',
toggle_darkmode: 'Attiva la modalità notturna',
view_swagger_docs: "Visualizza i documenti dell'API Swagger di LNbits",
api_docs: 'Documenti API',
commit_version: 'Commit version',
lnbits_version: 'Versione di LNbits',
runs_on: 'Esegue su',
credit_hint: 'Premere Invio per accreditare i fondi',
credit_label: '%{denomination} da accreditare',
paste_request: 'Richiesta di pagamento',
create_invoice: 'Crea fattura',
camera_tooltip: 'Usa la fotocamera per scansionare la fattura/QR',
export_csv: 'Esporta CSV',
transactions: 'Transazioni',
chart_tooltip: 'Mostra grafico',
pending: 'In attesa',
copy_invoice: 'Copia fattura',
close: 'Chiudi',
cancel: 'Annulla',
scan: 'Scansiona',
read: 'Leggi',
pay: 'Paga',
memo: 'Memo',
data: 'Dati',
processing_payment: 'Elaborazione pagamento...',
not_enough_funds: 'Non ci sono abbastanza fondi!',
search_by_tag_memo_amount: 'Cerca per tag, memo, importo...',
invoice_waiting: 'Fattura in attesa di pagamento',
payment_received: 'Pagamento ricevuto',
payment_sent: 'Pagamento inviato',
outgoing_payment_pending: 'Pagamento in uscita in attesa',
drain_funds: 'Fondi di drenaggio',
drain_funds_desc:
'Questo è un codice QR <code>LNURL-withdraw</code> per prelevare tutti i fondi da questo portafoglio. Non condividerlo con nessuno. È compatibile con <code>balanceCheck</code> e <code>balanceNotify</code>, di conseguenza il vostro portafoglio può continuare a prelevare continuamente i fondi da qui dopo il primo prelievo',
i_understand: 'Ho capito',
copy_wallet_url: 'Copia URL portafoglio',
disclaimer_dialog:
"La funzionalità di login sarà rilasciata in un futuro aggiornamento; per ora, assicuratevi di salvare tra i preferiti questa pagina per accedere nuovamente in futuro a questo portafoglio! Questo servizio è in fase BETA e non ci assumiamo alcuna responsabilità per la perdita all'accesso dei fondi",
no_transactions: 'Nessuna transazione effettuata',
manage_extensions: 'Gestisci le estensioni',
manage_server: 'Gestisci server',
estensioni: 'Estensioni',
no_extensions: 'Non ci sono estensioni installate :(',
created: 'Creato',
payment_hash: 'Hash del pagamento',
fee: 'Tariffa',
amount: 'Importo',
unit: 'Unità',
description: 'Descrizione',
expiry: 'Scadenza',
webhook: 'Webhook',
prova_di_pagamento: 'Prova di pagamento'
}
+31 -1
View File
@@ -70,11 +70,41 @@ window.localisation.jp = {
disclaimer_dialog:
'ウォレットを削除すると、ウォレットの秘密鍵が削除され、ウォレットを復元することはできません。ウォレットを削除する前に、ウォレットをエクスポートしてください。',
no_transactions: 'トランザクションはありません',
manage_extensions: '拡張機能を管理する',
manage_server: 'サーバーを管理する',
extensions: '拡張機能',
no_extensions: '拡張機能はありません',
created: '作成済み',
search_extensions: '検索拡張機能',
warning: '警告',
manage: '管理',
repository: 'リポジトリ',
confirm_continue: '続行してもよろしいですか?',
manage_extension_details: '拡張機能のインストール/アンインストール',
install: 'インストール',
uninstall: 'アンインストール',
open: '開く',
enable: '有効',
enable_extension_details: '現在のユーザーの拡張機能を有効にする',
disable: '無効',
installed: 'インストール済み',
activated: '有効化',
deactivated: '無効化',
release_notes: 'リリースノート',
activate_extension_details:
'拡張機能をユーザーが利用できるようにする/利用できないようにする',
featured: '特集',
all: 'すべて',
only_admins_can_install:
'(管理者アカウントのみが拡張機能をインストールできます)',
new_version: '新しいバージョン',
extension_depends_on: '依存先:',
extension_rating_soon: '評価は近日公開',
extension_installed_version: 'インストール済みバージョン',
extension_uninstall_warning:
'すべてのユーザーの拡張機能を削除しようとしています.',
uninstall_confirm: 'はい、アンインストールします',
extension_min_lnbits_version:
'このリリースには少なくとも LNbits バージョンが必要です',
payment_hash: '支払いハッシュ',
fee: '料金',
amount: '量',
+87
View File
@@ -0,0 +1,87 @@
window.localisation.pl = {
server: 'Serwer',
theme: 'Motyw',
funding: 'Finansowanie',
users: 'Użytkownicy',
restart: 'Restart serwera',
save: 'Zapisz',
save_tooltip: 'Zapisz zmiany',
topup: 'Doładowanie',
topup_wallet: 'Doładuj portfel',
topup_hint: 'Użyj ID portfela aby go doładować',
restart_tooltip: 'Zrestartuj serwer aby aktywować zmiany',
add_funds_tooltip: 'Dodaj środki do portfela.',
reset_defaults: 'Powrót do ustawień domyślnych',
reset_defaults_tooltip: 'Wymaż wszystkie ustawienia i ustaw domyślne.',
download_backup: 'Pobierz kopię zapasową bazy danych',
name_your_wallet: 'Nazwij swój portfel %{name}',
paste_invoice_label: 'Wklej fakturę, żądanie zapłaty lub kod lnurl *',
lnbits_description:
'Łatwy i lekki w konfiguracji, LNbits może działać w oparciu o dowolne źródło finansowania w sieci lightning, obecnie wspiera LND, c-lightning, OpenNode, LNPay czy nawet inną instancję LNbits! Możesz uruchomić instancję LNbits dla siebie lub dla innych. Każdy portfel ma swoje klucze API i nie ma ograniczeń jeśli chodzi o ilość portfeli. LNbits umożliwia dzielenie środków w celu zarządzania nimi, jest również dobrym narzędziem deweloperskim. Rozszerzenia zwiększają funkcjonalność LNbits co umożliwia eksperymentowanie z nowym technologiami w sieci lightning. Tworzenie rozszerzeń jest proste dlatego zachęcamy innych deweloperów do tworzenia dodatkowych funkcjonalności i wysyłanie do nas PR',
export_to_phone: 'Eksport kodu QR na telefon',
export_to_phone_desc:
'Ten kod QR zawiera adres URL Twojego portfela z pełnym dostępem do niego. Możesz go zeskanować na swoim telefonie aby otworzyć na nim ten portfel.',
wallets: 'Portfele',
add_wallet: 'Dodaj portfel',
delete_wallet: 'Usuń portfel',
delete_wallet_desc:
'Ten portfel zostanie usunięty, środków na nim zgromadzonych NIE BĘDZIE MOŻNA ODZYSKAĆ.',
rename_wallet: 'Zmień nazwę portfela',
update_name: 'Zaktualizuj nazwę',
press_to_claim: 'Naciśnij aby odebrać Bitcoiny',
donate: 'Podaruj',
view_github: 'Otwórz GitHub',
voidwallet_active: 'VoidWallet jest aktywny! Płatności są niemożliwe',
use_with_caution: 'KORZYSTAJ Z ROZWAGĄ - portfel %{name} jest w wersji BETA',
toggle_darkmode: 'Tryb nocny',
view_swagger_docs: 'Dokumentacja Swagger API',
api_docs: 'Dokumentacja API',
commit_version: 'Commit',
lnbits_version: 'Wersja LNbits',
runs_on: 'Działa na',
credit_hint: 'Naciśnij Enter aby doładować konto',
credit_label: '%{denomination} doładowanie',
paste_request: 'Wklej żądanie',
create_invoice: 'Utwórz fakturę',
camera_tooltip: 'Użyj kamery aby zeskanować fakturę lub kod QR',
export_csv: 'Eksport do CSV',
transactions: 'Transakcje',
chart_tooltip: 'Wykres',
pending: 'W toku',
copy_invoice: 'Skopiuj fakturę',
close: 'Zamknij',
cancel: 'Anuluj',
scan: 'Skanuj',
read: 'Odczytaj',
pay: 'Zapłać',
memo: 'Memo',
date: 'Data',
processing_payment: 'Przetwarzam płatność...',
not_enough_funds: 'Brak wystarczających środków!',
search_by_tag_memo_amount: 'Szukaj po tagu, memo czy wartości',
invoice_waiting: 'Faktura oczekuje na zapłatę',
payment_received: 'Otrzymano płatność',
payment_sent: 'Wysłano płatność',
outgoing_payment_pending: 'Płatność wychodząca w toku',
drain_funds: 'Opróżnij środki',
drain_funds_desc:
'To jest kod QR służący do opróżnienia portfela (LNURL-withdraw). Nie udostępniaj go nikomu. Ten kod jest kompatybilny z funkcjami, które umożliwiają wielokrotne żądania aż do zupełnego opróżnienia portfela.',
i_understand: 'Rozumiem',
copy_wallet_url: 'Skopiuj URL portfela',
disclaimer_dialog:
'Funkcja logowania zostanie uruchomiona w przyszłości. Póki co upewnij się, że zapisałeś adres URL tej strony aby mieć dostęp do tego portfela. Nie udostępniaj adresu tej strony nikomu, kto nie ma mieć do tego portfela dostępu! Ta usługa działa w wersji BETA, nie odpowiadamy za utratę dostępu do środków przez osoby używające LNbits.',
no_transactions: 'Brak transakcji',
manage_extensions: 'Zarządzaj rozszerzeniami',
manage_server: 'Zarządzaj serwerem',
extensions: 'Rozszerzenia',
no_extensions: 'Nie masz zainstalowanych żadnych rozszerzeń :(',
created: 'Utworzono',
payment_hash: 'Hash Płatności',
fee: 'Opłata',
amount: 'Wartość',
unit: 'Jednostka',
description: 'Opis',
expiry: 'Wygasa',
webhook: 'Webhook',
payment_proof: 'Potwierdzenie płatności'
}
+88
View File
@@ -0,0 +1,88 @@
window.localisation.we = {
server: 'Gweinydd',
theme: 'Thema',
funding: 'Arian fyndio',
users: 'Defnyddwyr',
restart: 'Ailgychwyn gweinydd',
save: 'Save',
save_tooltip: 'cadw eich newidiadau',
topup: 'Topup',
topup_wallet: 'Atodi waled',
topup_hint: 'Defnyddiwch ID y waled i ychwanegu at unrhyw waled',
restart_tooltip: 'Ailgychwyn y gweinydd er mwyn i newidiadau ddod i rym',
add_funds_tooltip: 'Ychwanegu arian at waled.',
reset_defaults: 'Ailosod i`r rhagosodiadau',
reset_defaults_tooltip: 'Dileu pob gosodiad ac ailosod i`r rhagosodiadau.',
download_backup: 'Lawrlwytho copi wrth gefn cronfa ddata',
name_your_wallet: 'Enwch eich waled %{name}',
paste_invoice_label: 'Gludwch anfoneb, cais am daliad neu god lnurl *',
lnbits_description:
'Yn hawdd iw sefydlu ac yn ysgafn, gall LNbits redeg ar unrhyw ffynhonnell ariannu rhwydwaith mellt, ar hyn o bryd yn cefnogi LND, c-lightning, OpenNode, LNPay a hyd yn oed LNbits ei hun! Gallwch redeg LNbits i chi`ch hun, neu gynnig datrysiad ceidwad i eraill yn hawdd. Mae gan bob waled ei allweddi API ei hun ac nid oes cyfyngiad ar nifer y waledi y gallwch eu gwneud. Mae gallu rhannu cronfeydd yn gwneud LNbits yn arf defnyddiol ar gyfer rheoli arian ac fel offeryn datblygu. Mae estyniadau yn ychwanegu ymarferoldeb ychwanegol at LNbits fel y gallwch arbrofi gydag ystod o dechnolegau blaengar ar y rhwydwaith mellt. Rydym wedi gwneud datblygu estyniadau mor hawdd â phosibl, ac fel prosiect ffynhonnell agored am ddim, rydym yn annog pobl i ddatblygu a chyflwyno eu rhai eu hunain.',
export_to_phone: 'Allforio i Ffôn gyda chod QR',
export_to_phone_desc:
'Mae`r cod QR hwn yn cynnwys URL eich waled gyda mynediad llawn. Gallwch ei sganio o`ch ffôn i agor eich waled oddi yno.',
waledi: 'Waledi',
add_wallet: 'Ychwanegu waled newydd',
delete_wallet: 'Dileu waled',
delete_wallet_desc:
'Bydd y waled gyfan hon yn cael ei dileu, ni fydd modd adennill yr arian.',
rename_wallet: 'Ailenwi waled',
update_name: 'Diweddaru enw',
press_to_claim: 'Pwyswch i hawlio bitcoin',
Donate: 'Rhoi',
view_github: 'Gweld ar GitHub',
voidwallet_active:
' Mae VoidWallet yn weithredol! Taliadau wedi`u hanalluogi',
use_with_caution: 'DEFNYDDIO GYDA GOFAL - mae waled %{name} yn dal yn BETA',
toggle_darkmode: 'Toglo Modd Tywyll',
view_swagger_docs: 'Gweld dogfennau API LNbits Swagger',
api_docs: 'Api docs',
commit_version: 'fersiwn ymrwymo',
lnbits_version: 'Fersiwn LNbits',
Runs_on: 'Yn rhedeg ymlaen',
credit_hint: 'Pwyswch Enter i gyfrif credyd',
credit_label: '%{enomination} i gredyd',
paste_request: 'Gludo Cais',
create_invoice: 'Creu Anfoneb',
camera_tooltip: 'Defnyddio camera i sganio anfoneb/QR',
export_csv: 'Allforio i CSV',
trafodion: 'Trafodion',
chart_tooltip: 'Dangos siart',
pending: 'yn yr arfaeth',
copy_invoice: 'Copi anfoneb',
Close: 'cau',
cancel: 'Canslo',
scan: 'Sgan',
read: 'Darllen',
talu: 'Pay',
memo: 'Memo',
date: 'Dyddiad',
processing_payment: 'Prosesu taliad...',
not_enough_funds: 'Dim digon o arian!',
search_by_tag_memo_amount: 'Chwilio yn ôl tag, memo, swm',
invoice_waiting: 'Anfoneb yn aros i gael ei thalu',
payment_received: 'Taliad a Dderbyniwyd',
payment_sent: 'Taliad a Anfonwyd',
outgoing_payment_pending: 'Taliad sy`n aros yn yr arfaeth',
drain_funds: 'Cronfeydd Draenio',
drain_funds_desc:
'Cod QR Tynnu`n ôl LNURL yw hwn ar gyfer slurpio popeth o`r waled hon. Peidiwch â rhannu gyda neb. Mae`n gydnaws â balanceCheck a balanceNotify felly efallai y bydd eich waled yn tynnu`r arian yn barhaus o`r fan hon ar ôl y codiad cyntaf.',
i_understand: 'Rwy`n deall',
copy_wallet_url: 'Copi URL waled',
disclaimer_dialog:
'Swyddogaeth mewngofnodi i`w ryddhau mewn diweddariad yn y dyfodol, am y tro, gwnewch yn siŵr eich bod yn rhoi nod tudalen ar y dudalen hon ar gyfer mynediad i`ch waled yn y dyfodol! Mae`r gwasanaeth hwn yn BETA, ac nid ydym yn gyfrifol am bobl sy`n colli mynediad at arian.',
no_transactions: 'Dim trafodion wedi`u gwneud eto',
manage_extensions: 'Rheoli Estyniadau',
manage_server: 'Rheoli Gweinydd',
Extensions: 'Estyniadau',
no_extensions: "Nid oes gennych unrhyw estyniadau wedi'u gosod :(",
create: 'Crëwyd',
payment_hash: 'Hais Taliad',
fee: 'Fee',
amount: 'swm',
unit: 'Uned',
description: 'Disgrifiad',
expiry: 'dod i ben',
webhook: 'bachyn we',
payment_proof: 'prawf taliad'
}
-8
View File
@@ -140,14 +140,6 @@ Vue.component('lnbits-extension-list', {
<q-item-label lines="1" class="text-caption" v-text="$t('extensions')"></q-item-label>
</q-item-section>
</q-item>
<q-item clickable tag="a" :href="['/install?usr=', user.id].join('')">
<q-item-section side>
<q-icon name="playlist_add" color="grey-5" size="md"></q-icon>
</q-item-section>
<q-item-section>
<q-item-label lines="1" class="text-caption" v-text="$t('manage_extensions')"></q-item-label>
</q-item-section>
</q-item>
</q-list>
`,
computed: {
+7 -3
View File
@@ -5,7 +5,7 @@
"/static/vendor/axios.js",
"/static/vendor/vue.js",
"/static/vendor/vue-router.js",
"/static/vendor/vue-qrcode-reader.browser.js",
"/static/vendor/VueQrcodeReader.umd.js",
"/static/vendor/vue-qrcode.js",
"/static/vendor/vuex.js",
"/static/vendor/quasar.ie.polyfills.umd.min.js",
@@ -13,13 +13,18 @@
"/static/vendor/Chart.bundle.js",
"/static/vendor/vue-i18n.js",
"/static/i18n/i18n.js",
"/static/i18n/en.js",
"/static/i18n/de.js",
"/static/i18n/en.js",
"/static/i18n/es.js",
"/static/i18n/fr.js",
"/static/i18n/it.js",
"/static/i18n/jp.js",
"/static/i18n/nl.js",
"/static/i18n/pi.js",
"/static/i18n/pl.js",
"/static/i18n/fr.js",
"/static/i18n/nl.js",
"/static/i18n/we.js",
"/static/js/base.js",
"/static/js/components.js",
"/static/js/bolt11-decoder.js"
@@ -27,7 +32,6 @@
"css": [
"/static/vendor/quasar.css",
"/static/vendor/Chart.css",
"/static/vendor/vue-qrcode-reader.css",
"/static/css/base.css"
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
.wrapper[data-v-1f90552a]{position:relative;z-index:0;width:100%;height:100%}.overlay[data-v-1f90552a],.tracking-layer[data-v-1f90552a]{position:absolute;width:100%;height:100%;top:0;left:0}.camera[data-v-1f90552a],.pause-frame[data-v-1f90552a]{display:block;object-fit:cover;width:100%;height:100%}
+27 -3
View File
@@ -40,15 +40,24 @@
<span><strong>LN</strong>bits</span> {% endif %} {%endif%} {%
endblock %}
</q-btn>
{% block toolbar_subtitle %}{%if user and user.super_user%}
<q-badge align="middle">Super User</q-badge>
{% elif user and user.admin %}
<q-badge align="middle">Admin User</q-badge>
{%endif%}{% endblock %}
</q-toolbar-title>
{% block beta %} {% if VOIDWALLET %}
<q-badge color="red" text-color="black" class="q-mr-md">
{% raw %}{{ $t('voidwallet_active') }}{% endraw %}
</q-badge>
{%endif%}
<q-badge color="yellow" text-color="black" class="q-mr-md">
<q-badge
v-show="$q.screen.gt.sm"
color="yellow"
text-color="black"
class="q-mr-md"
>
<span
v-show="$q.screen.gt.sm"
v-text='$t("use_with_caution", { name: "{{ SITE_TITLE }}" })'
></span>
</q-badge>
@@ -95,11 +104,26 @@
<q-item-label>FR</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="changeLanguage('we')">
<q-item-section>
<q-item-label>WE</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="changeLanguage('it')">
<q-item-section>
<q-item-label>IT</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="changeLanguage('pi')">
<q-item-section>
<q-item-label>PI</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="changeLanguage('pl')">
<q-item-section>
<q-item-label>PL</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="changeLanguage('nl')">
<q-item-section>
<q-item-label>NL</q-item-label>
@@ -306,7 +330,7 @@
<script type="text/javascript">
const themes = {{ LNBITS_THEME_OPTIONS | tojson }}
const LNBITS_DENOMINATION = {{ LNBITS_DENOMINATION | tojson}}
if(themes && themes.length) {
if (themes && themes.length) {
window.allowedThemes = themes.map(str => str.trim())
}
</script>
+21 -44
View File
@@ -14,7 +14,7 @@
"underscore": "^1.13.6",
"vue": "2.6.12",
"vue-i18n": "^8.28.2",
"vue-qrcode-reader": "2.2",
"vue-qrcode-reader": "^2.3.18",
"vue-router": "3.4.3",
"vuex": "3.5.1"
},
@@ -172,16 +172,6 @@
"proxy-from-env": "^1.1.0"
}
},
"node_modules/babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
"license": "MIT",
"dependencies": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
}
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -367,11 +357,14 @@
}
},
"node_modules/core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"version": "3.30.2",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.2.tgz",
"integrity": "sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg==",
"hasInstallScript": true,
"license": "MIT"
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/css-b64-images": {
"version": "0.2.5",
@@ -657,12 +650,6 @@
"integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==",
"dev": true
},
"node_modules/jsqr": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.4.0.tgz",
"integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==",
"license": "Apache-2.0"
},
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -1008,12 +995,6 @@
"node": ">=10"
}
},
"node_modules/regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"license": "MIT"
},
"node_modules/relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
@@ -1042,7 +1023,6 @@
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz",
"integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==",
"license": "BSD-3-Clause",
"dependencies": {
"sdp": "^2.6.0"
},
@@ -1071,8 +1051,7 @@
"node_modules/sdp": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz",
"integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw==",
"license": "MIT"
"integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw=="
},
"node_modules/set-blocking": {
"version": "2.0.0",
@@ -1217,15 +1196,14 @@
"integrity": "sha512-C5GZjs1tYlAqjwymaaCPDjCyGo10ajUphiwA922jKt9n7KPpqR7oM1PCwYzhB/E7+nT3wfdG3oRre5raIT1rKA=="
},
"node_modules/vue-qrcode-reader": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/vue-qrcode-reader/-/vue-qrcode-reader-2.2.0.tgz",
"integrity": "sha512-xsNptrJGTFFjwfksIK6KlbjsyIGBLTFgO9KH3JcmZzABY19c2d0+zG//LXkp8K2Jl+4APidktVtH/aTbmcgX7g==",
"license": "MIT",
"version": "2.3.18",
"resolved": "https://registry.npmjs.org/vue-qrcode-reader/-/vue-qrcode-reader-2.3.18.tgz",
"integrity": "sha512-r+nrIZQ+0helU3nfioh84iZDw39nh0w8NjMtOnWcM/3pqr9MOyUV1dkZWD144N1LB3/z5gUOAWkWHJ+6lq6rUA==",
"dependencies": {
"babel-runtime": "^6.26.0",
"callforth": "^0.3.0",
"jsqr": "^1.2.0",
"webrtc-adapter": "^6.2.1"
"callforth": "^0.3.1",
"core-js": "^3.6.5",
"vue": "^2.6.11",
"webrtc-adapter": "7.7.0"
}
},
"node_modules/vue-router": {
@@ -1242,13 +1220,12 @@
}
},
"node_modules/webrtc-adapter": {
"version": "6.4.8",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-6.4.8.tgz",
"integrity": "sha512-YM8yl545c/JhYcjGHgaCoA7jRK/KZuMwEDFeP2AcP0Auv5awEd+gZE0hXy9z7Ed3p9HvAXp8jdbe+4ESb1zxAw==",
"license": "BSD-3-Clause",
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.0.tgz",
"integrity": "sha512-7Bp9OBnx642oJRkom1tNAbeJjUadAq2rh5xLL9YXPw5hVyt2h4hHr5bcoPYDs1stp/mZHSPSQA34YISdnr0DBQ==",
"dependencies": {
"rtcpeerconnection-shim": "^1.2.14",
"sdp": "^2.9.0"
"rtcpeerconnection-shim": "^1.2.15",
"sdp": "^2.12.0"
},
"engines": {
"node": ">=6.0.0",
+11 -8
View File
@@ -12,9 +12,9 @@
"devDependencies": {
"concat": "^1.0.3",
"minify": "^9.2.0",
"sass": "^1.60.0",
"prettier": "2.8.3",
"pyright": "1.1.289"
"pyright": "1.1.289",
"sass": "^1.60.0"
},
"dependencies": {
"@chenfengyuan/vue-qrcode": "1.0.2",
@@ -25,7 +25,7 @@
"underscore": "^1.13.6",
"vue": "2.6.12",
"vue-i18n": "^8.28.2",
"vue-qrcode-reader": "2.2",
"vue-qrcode-reader": "^2.3.18",
"vue-router": "3.4.3",
"vuex": "3.5.1"
},
@@ -35,7 +35,7 @@
"./node_modules/axios/dist/axios.js",
"./node_modules/vue/dist/vue.js",
"./node_modules/vue-router/dist/vue-router.js",
"./node_modules/vue-qrcode-reader/dist/vue-qrcode-reader.browser.js",
"./node_modules/vue-qrcode-reader/dist/VueQrcodeReader.umd.js",
"./node_modules/@chenfengyuan/vue-qrcode/dist/vue-qrcode.js",
"./node_modules/vuex/dist/vuex.js",
"./node_modules/quasar/dist/quasar.ie.polyfills.umd.min.js",
@@ -43,7 +43,6 @@
"./node_modules/chart.js/dist/Chart.bundle.js",
"./node_modules/quasar/dist/quasar.css",
"./node_modules/chart.js/dist/Chart.css",
"./node_modules/vue-qrcode-reader/dist/vue-qrcode-reader.css",
"./node_modules/vue-i18n/dist/vue-i18n.js"
],
"bundle": {
@@ -53,7 +52,7 @@
"/static/vendor/axios.js",
"/static/vendor/vue.js",
"/static/vendor/vue-router.js",
"/static/vendor/vue-qrcode-reader.browser.js",
"/static/vendor/VueQrcodeReader.umd.js",
"/static/vendor/vue-qrcode.js",
"/static/vendor/vuex.js",
"/static/vendor/quasar.ie.polyfills.umd.min.js",
@@ -61,13 +60,18 @@
"/static/vendor/Chart.bundle.js",
"/static/vendor/vue-i18n.js",
"/static/i18n/i18n.js",
"/static/i18n/en.js",
"/static/i18n/de.js",
"/static/i18n/en.js",
"/static/i18n/es.js",
"/static/i18n/fr.js",
"/static/i18n/it.js",
"/static/i18n/jp.js",
"/static/i18n/nl.js",
"/static/i18n/pi.js",
"/static/i18n/pl.js",
"/static/i18n/fr.js",
"/static/i18n/nl.js",
"/static/i18n/we.js",
"/static/js/base.js",
"/static/js/components.js",
"/static/js/bolt11-decoder.js"
@@ -75,7 +79,6 @@
"css": [
"/static/vendor/quasar.css",
"/static/vendor/Chart.css",
"/static/vendor/vue-qrcode-reader.css",
"/static/css/base.css"
]
}
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "lnbits"
version = "0.10.6"
version = "0.10.7"
description = "LNbits, free and open-source Lightning wallet and accounts system."
authors = ["Alan Bits <alan@lnbits.com>"]