mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2024-11-19 01:43:42 +01:00
feat: improve on admin ui funding tab (#1989)
* feat: improve on admin ui funding tab * now only shows settings of selected fundingsource. * refactor into vue component * use camel case * refactor: move admin js into .js file (#1990) * updateFundingData is redundent now --------- Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
This commit is contained in:
parent
c2a11fa0bb
commit
80529dee4b
@ -1,6 +1,6 @@
|
||||
// update cache version every time there is a new deployment
|
||||
// so the service worker reinitializes the cache
|
||||
const CACHE_VERSION = 61
|
||||
const CACHE_VERSION = 62
|
||||
const CURRENT_CACHE = `lnbits-${CACHE_VERSION}-`
|
||||
|
||||
const getApiKey = request => {
|
||||
|
@ -25,117 +25,71 @@
|
||||
</ul>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div v-if="'{{LNBITS_NODE_UI_AVAILABLE}}' === 'True'">
|
||||
<div>Node Management</div>
|
||||
<q-toggle
|
||||
label="Node UI"
|
||||
v-model="formData.lnbits_node_ui"
|
||||
></q-toggle>
|
||||
<q-toggle
|
||||
v-if="formData.lnbits_node_ui"
|
||||
label="Public node UI"
|
||||
v-model="formData.lnbits_public_node_ui"
|
||||
></q-toggle>
|
||||
<q-toggle
|
||||
v-if="formData.lnbits_node_ui"
|
||||
label="Transactions Tab (Disable on large CLN nodes)"
|
||||
v-model="formData.lnbits_node_ui_transactions"
|
||||
></q-toggle>
|
||||
<br />
|
||||
</div>
|
||||
<p v-else>Node Management not supported by active funding source</p>
|
||||
{% if LNBITS_NODE_UI_AVAILABLE %}
|
||||
<p>Node Management</p>
|
||||
<q-toggle
|
||||
label="Node UI"
|
||||
v-model="formData.lnbits_node_ui"
|
||||
></q-toggle>
|
||||
<q-toggle
|
||||
v-if="formData.lnbits_node_ui"
|
||||
label="Public node UI"
|
||||
v-model="formData.lnbits_public_node_ui"
|
||||
></q-toggle>
|
||||
<br />
|
||||
<q-toggle
|
||||
v-if="formData.lnbits_node_ui"
|
||||
label="Transactions Tab (Disable on large CLN nodes)"
|
||||
v-model="formData.lnbits_node_ui_transactions"
|
||||
></q-toggle>
|
||||
{% else %}
|
||||
<p>Node Management not supported by active funding source</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row q-col-gutter-md">
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p>Active Funding<small> (Requires server restart)</small></p>
|
||||
<q-select
|
||||
:disable="!isSuperUser"
|
||||
filled
|
||||
v-model="formData.lnbits_backend_wallet_class"
|
||||
hint="Select the active funding wallet"
|
||||
:options="settings.lnbits_allowed_funding_sources"
|
||||
></q-select>
|
||||
</div>
|
||||
</div>
|
||||
<p>Invoice Expiry</p>
|
||||
<q-input
|
||||
filled
|
||||
v-model.number="formData.lightning_invoice_expiry"
|
||||
type="number"
|
||||
label="Invoice expiry (seconds)"
|
||||
mask="#######"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12 col-md-8">
|
||||
<p>Fee reserve</p>
|
||||
<div class="row q-col-gutter-md">
|
||||
<div class="col-12 col-md-4">
|
||||
<p>Invoice Expiry</p>
|
||||
<div class="col-6">
|
||||
<q-input
|
||||
filled
|
||||
v-model.number="formData.lightning_invoice_expiry"
|
||||
type="number"
|
||||
label="Invoice expiry (seconds)"
|
||||
mask="#######"
|
||||
filled
|
||||
v-model="formData.lnbits_reserve_fee_min"
|
||||
label="Reserve fee in msats"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12 col-md-8">
|
||||
<p>Fee reserve</p>
|
||||
<div class="row q-col-gutter-md">
|
||||
<div class="col-6">
|
||||
<q-input
|
||||
type="number"
|
||||
filled
|
||||
v-model="formData.lnbits_reserve_fee_min"
|
||||
label="Reserve fee in msats"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<q-input
|
||||
type="number"
|
||||
filled
|
||||
name="lnbits_reserve_fee_percent"
|
||||
v-model="formData.lnbits_reserve_fee_percent"
|
||||
label="Reserve fee in percent"
|
||||
step="0.1"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<q-input
|
||||
type="number"
|
||||
filled
|
||||
name="lnbits_reserve_fee_percent"
|
||||
v-model="formData.lnbits_reserve_fee_percent"
|
||||
label="Reserve fee in percent"
|
||||
step="0.1"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isSuperUser">
|
||||
<p class="q-my-md">
|
||||
Funding Sources<small> (Requires server restart)</small>
|
||||
</p>
|
||||
<q-list
|
||||
v-for="(fund, idx) in settings.lnbits_allowed_funding_sources"
|
||||
:key="idx"
|
||||
>
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
icon="payments"
|
||||
:label="prettyFunding.get(fund) || fund"
|
||||
v-if="funding_sources.get(fund)"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section
|
||||
v-for="([key, prop], i) in Object.entries(funding_sources.get(fund))"
|
||||
:key="i"
|
||||
>
|
||||
<q-input
|
||||
dense
|
||||
filled
|
||||
type="text"
|
||||
v-model="formData[key]"
|
||||
:label="prop.label"
|
||||
class="q-pr-md"
|
||||
:hint="prop.hint"
|
||||
></q-input>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
</q-list>
|
||||
<lnbits-funding-sources
|
||||
:form-data="formData"
|
||||
:allowed-funding-sources="settings.lnbits_allowed_funding_sources"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
@ -164,537 +164,5 @@
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#vue',
|
||||
mixins: [windowMixin],
|
||||
data: function () {
|
||||
return {
|
||||
settings: {},
|
||||
logs: [],
|
||||
serverlogEnabled: false,
|
||||
lnbits_theme_options: [
|
||||
'classic',
|
||||
'bitcoin',
|
||||
'flamingo',
|
||||
'cyber',
|
||||
'freedom',
|
||||
'mint',
|
||||
'autumn',
|
||||
'monochrome',
|
||||
'salvador'
|
||||
],
|
||||
auditData: {},
|
||||
statusData: {},
|
||||
statusDataTable: {
|
||||
columns: [
|
||||
{
|
||||
name: 'date',
|
||||
align: 'left',
|
||||
label: this.$t('date'),
|
||||
field: 'date'
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
align: 'left',
|
||||
label: this.$t('memo'),
|
||||
field: 'message'
|
||||
}
|
||||
]
|
||||
},
|
||||
formData: {},
|
||||
formAddAdmin: '',
|
||||
formAddUser: '',
|
||||
formAddExtensionsManifest: '',
|
||||
formAllowedIPs: '',
|
||||
formBlockedIPs: '',
|
||||
isSuperUser: false,
|
||||
wallet: {},
|
||||
cancel: {},
|
||||
topUpDialog: {
|
||||
show: false
|
||||
},
|
||||
tab: 'funding',
|
||||
needsRestart: false,
|
||||
prettyFunding: new Map([
|
||||
['VoidWallet', 'Void Wallet'],
|
||||
['FakeWallet', 'Fake Wallet'],
|
||||
['CoreLightningWallet', 'Core Lightning'],
|
||||
['CoreLightningRestWallet', 'Core Lightning'],
|
||||
['LndRestWallet', 'Lightning Network Daemon (LND Rest)'],
|
||||
['LndWallet', 'Lightning Network Daemon (LND)'],
|
||||
['LnTipsWallet', 'LN.Tips'],
|
||||
['LNPayWallet', 'LN Pay'],
|
||||
['EclairWallet', 'Eclair (ACINQ)'],
|
||||
['LNbitsWallet', 'LNBits'],
|
||||
['OpenNodeWallet', 'OpenNode'],
|
||||
['ClicheWallet', 'Cliche (NBD)'],
|
||||
['SparkWallet', 'Spark']
|
||||
]),
|
||||
funding_sources: new Map([
|
||||
['VoidWallet', null],
|
||||
[
|
||||
'FakeWallet',
|
||||
{
|
||||
fake_wallet_secret: {
|
||||
value: null,
|
||||
label: 'Secret'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'CoreLightningWallet',
|
||||
{
|
||||
corelightning_rpc: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'CoreLightningRestWallet',
|
||||
{
|
||||
corelightning_rest_url: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
},
|
||||
corelightning_rest_cert: {
|
||||
value: null,
|
||||
label: 'Certificate'
|
||||
},
|
||||
corelightning_rest_macaroon: {
|
||||
value: null,
|
||||
label: 'Macaroon'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'LndRestWallet',
|
||||
{
|
||||
lnd_rest_endpoint: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
},
|
||||
lnd_rest_cert: {
|
||||
value: null,
|
||||
label: 'Certificate'
|
||||
},
|
||||
lnd_rest_macaroon: {
|
||||
value: null,
|
||||
label: 'Macaroon'
|
||||
},
|
||||
lnd_rest_macaroon_encrypted: {
|
||||
value: null,
|
||||
label: 'Encrypted Macaroon'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'LndWallet',
|
||||
{
|
||||
lnd_grpc_endpoint: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
},
|
||||
lnd_grpc_cert: {
|
||||
value: null,
|
||||
label: 'Certificate'
|
||||
},
|
||||
lnd_grpc_port: {
|
||||
value: null,
|
||||
label: 'Port'
|
||||
},
|
||||
lnd_grpc_admin_macaroon: {
|
||||
value: null,
|
||||
label: 'Admin Macaroon'
|
||||
},
|
||||
lnd_grpc_macaroon_encrypted: {
|
||||
value: null,
|
||||
label: 'Encrypted Macaroon'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'LnTipsWallet',
|
||||
{
|
||||
lntips_api_endpoint: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
},
|
||||
lntips_api_key: {
|
||||
value: null,
|
||||
label: 'API Key'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'LNPayWallet',
|
||||
{
|
||||
lnpay_api_endpoint: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
},
|
||||
lnpay_api_key: {
|
||||
value: null,
|
||||
label: 'API Key'
|
||||
},
|
||||
lnpay_wallet_key: {
|
||||
value: null,
|
||||
label: 'Wallet Key'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'EclairWallet',
|
||||
{
|
||||
eclair_url: {
|
||||
value: null,
|
||||
label: 'URL'
|
||||
},
|
||||
eclair_pass: {
|
||||
value: null,
|
||||
label: 'Password'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'LNbitsWallet',
|
||||
{
|
||||
lnbits_endpoint: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
},
|
||||
lnbits_key: {
|
||||
value: null,
|
||||
label: 'Admin Key'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'OpenNodeWallet',
|
||||
{
|
||||
opennode_api_endpoint: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
},
|
||||
opennode_key: {
|
||||
value: null,
|
||||
label: 'Key'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'ClicheWallet',
|
||||
{
|
||||
cliche_endpoint: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'SparkWallet',
|
||||
{
|
||||
spark_url: {
|
||||
value: null,
|
||||
label: 'Endpoint'
|
||||
},
|
||||
spark_token: {
|
||||
value: null,
|
||||
label: 'Token'
|
||||
}
|
||||
}
|
||||
]
|
||||
])
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getSettings()
|
||||
this.getAudit()
|
||||
this.balance = +'{{ balance|safe }}'
|
||||
},
|
||||
computed: {
|
||||
lnbitsVersion() {
|
||||
return LNBITS_VERSION
|
||||
},
|
||||
checkChanges() {
|
||||
return !_.isEqual(this.settings, this.formData)
|
||||
},
|
||||
updateAvailable() {
|
||||
return LNBITS_VERSION !== this.statusData.version
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addAdminUser() {
|
||||
let addUser = this.formAddAdmin
|
||||
let admin_users = this.formData.lnbits_admin_users
|
||||
if (addUser && addUser.length && !admin_users.includes(addUser)) {
|
||||
//admin_users = [...admin_users, addUser]
|
||||
this.formData.lnbits_admin_users = [...admin_users, addUser]
|
||||
this.formAddAdmin = ''
|
||||
}
|
||||
},
|
||||
removeAdminUser(user) {
|
||||
let admin_users = this.formData.lnbits_admin_users
|
||||
this.formData.lnbits_admin_users = admin_users.filter(u => u !== user)
|
||||
},
|
||||
addAllowedUser() {
|
||||
let addUser = this.formAddUser
|
||||
let allowed_users = this.formData.lnbits_allowed_users
|
||||
if (addUser && addUser.length && !allowed_users.includes(addUser)) {
|
||||
this.formData.lnbits_allowed_users = [...allowed_users, addUser]
|
||||
this.formAddUser = ''
|
||||
}
|
||||
},
|
||||
removeAllowedUser(user) {
|
||||
let allowed_users = this.formData.lnbits_allowed_users
|
||||
this.formData.lnbits_allowed_users = allowed_users.filter(
|
||||
u => u !== user
|
||||
)
|
||||
},
|
||||
addExtensionsManifest() {
|
||||
const addManifest = this.formAddExtensionsManifest.trim()
|
||||
const manifests = this.formData.lnbits_extensions_manifests
|
||||
if (
|
||||
addManifest &&
|
||||
addManifest.length &&
|
||||
!manifests.includes(addManifest)
|
||||
) {
|
||||
this.formData.lnbits_extensions_manifests = [
|
||||
...manifests,
|
||||
addManifest
|
||||
]
|
||||
this.formAddExtensionsManifest = ''
|
||||
}
|
||||
},
|
||||
removeExtensionsManifest(manifest) {
|
||||
const manifests = this.formData.lnbits_extensions_manifests
|
||||
this.formData.lnbits_extensions_manifests = manifests.filter(
|
||||
m => m !== manifest
|
||||
)
|
||||
},
|
||||
async toggleServerLog() {
|
||||
this.serverlogEnabled = !this.serverlogEnabled
|
||||
if (this.serverlogEnabled) {
|
||||
const wsProto = location.protocol !== 'http:' ? 'wss://' : 'ws://'
|
||||
const digestHex = await LNbits.utils.digestMessage(this.g.user.id)
|
||||
const localUrl =
|
||||
wsProto +
|
||||
document.domain +
|
||||
':' +
|
||||
location.port +
|
||||
'/api/v1/ws/' +
|
||||
digestHex
|
||||
this.ws = new WebSocket(localUrl)
|
||||
this.ws.addEventListener('message', async ({data}) => {
|
||||
this.logs.push(data.toString())
|
||||
const scrollArea = this.$refs.logScroll
|
||||
if (scrollArea) {
|
||||
const scrollTarget = scrollArea.getScrollTarget()
|
||||
const duration = 0
|
||||
scrollArea.setScrollPosition(scrollTarget.scrollHeight, duration)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.ws.close()
|
||||
}
|
||||
},
|
||||
addAllowedIPs() {
|
||||
const allowedIPs = this.formAllowedIPs.trim()
|
||||
const allowed_ips = this.formData.lnbits_allowed_ips
|
||||
if (
|
||||
allowedIPs &&
|
||||
allowedIPs.length &&
|
||||
!allowed_ips.includes(allowedIPs)
|
||||
) {
|
||||
this.formData.lnbits_allowed_ips = [...allowed_ips, allowedIPs]
|
||||
this.formAllowedIPs = ''
|
||||
}
|
||||
},
|
||||
removeAllowedIPs(allowed_ip) {
|
||||
const allowed_ips = this.formData.lnbits_allowed_ips
|
||||
this.formData.lnbits_allowed_ips = allowed_ips.filter(
|
||||
a => a !== allowed_ip
|
||||
)
|
||||
},
|
||||
addBlockedIPs() {
|
||||
const blockedIPs = this.formBlockedIPs.trim()
|
||||
const blocked_ips = this.formData.lnbits_blocked_ips
|
||||
if (
|
||||
blockedIPs &&
|
||||
blockedIPs.length &&
|
||||
!blocked_ips.includes(blockedIPs)
|
||||
) {
|
||||
this.formData.lnbits_blocked_ips = [...blocked_ips, blockedIPs]
|
||||
this.formBlockedIPs = ''
|
||||
}
|
||||
},
|
||||
removeBlockedIPs(blocked_ip) {
|
||||
const blocked_ips = this.formData.lnbits_blocked_ips
|
||||
this.formData.lnbits_blocked_ips = blocked_ips.filter(
|
||||
b => b !== blocked_ip
|
||||
)
|
||||
},
|
||||
restartServer() {
|
||||
LNbits.api
|
||||
.request('GET', '/admin/api/v1/restart/?usr=' + this.g.user.id)
|
||||
.then(response => {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Success! Restarted Server',
|
||||
icon: null
|
||||
})
|
||||
this.needsRestart = false
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
topupWallet() {
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
'/admin/api/v1/topup/?usr=' + this.g.user.id,
|
||||
this.g.user.wallets[0].adminkey,
|
||||
this.wallet
|
||||
)
|
||||
.then(response => {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message:
|
||||
'Success! Added ' +
|
||||
this.wallet.amount +
|
||||
' to ' +
|
||||
this.wallet.id,
|
||||
icon: null
|
||||
})
|
||||
this.wallet = {}
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
updateFundingData() {
|
||||
this.settings.lnbits_allowed_funding_sources.map(f => {
|
||||
let opts = this.funding_sources.get(f)
|
||||
if (!opts) return
|
||||
Object.keys(opts).forEach(e => {
|
||||
opts[e].value = this.settings[e]
|
||||
})
|
||||
})
|
||||
},
|
||||
formatDate(date) {
|
||||
return moment(date * 1000).fromNow()
|
||||
},
|
||||
getNotifications() {
|
||||
if (this.settings.lnbits_notifications) {
|
||||
axios
|
||||
.get(this.settings.lnbits_status_manifest)
|
||||
.then(response => {
|
||||
this.statusData = response.data
|
||||
})
|
||||
.catch(error => {
|
||||
this.formData.lnbits_notifications = false
|
||||
error.response.data = {}
|
||||
error.response.data.message = 'Could not fetch status manifest.'
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
}
|
||||
},
|
||||
getAudit() {
|
||||
LNbits.api
|
||||
.request(
|
||||
'GET',
|
||||
'/admin/api/v1/audit/?usr=' + this.g.user.id,
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
.then(response => {
|
||||
this.auditData = response.data
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
getSettings() {
|
||||
LNbits.api
|
||||
.request(
|
||||
'GET',
|
||||
'/admin/api/v1/settings/?usr=' + this.g.user.id,
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
.then(response => {
|
||||
this.isSuperUser = response.data.is_super_user || false
|
||||
this.settings = response.data
|
||||
this.formData = {...this.settings}
|
||||
this.updateFundingData()
|
||||
this.getNotifications()
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
updateSettings() {
|
||||
let data = _.omit(this.formData, [
|
||||
'is_super_user',
|
||||
'lnbits_allowed_funding_sources'
|
||||
])
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
'/admin/api/v1/settings/?usr=' + this.g.user.id,
|
||||
this.g.user.wallets[0].adminkey,
|
||||
data
|
||||
)
|
||||
.then(response => {
|
||||
this.needsRestart =
|
||||
this.settings.lnbits_backend_wallet_class !==
|
||||
this.formData.lnbits_backend_wallet_class ||
|
||||
this.settings.lnbits_killswitch !==
|
||||
this.formData.lnbits_killswitch
|
||||
this.settings = this.formData
|
||||
this.formData = _.clone(this.settings)
|
||||
this.updateFundingData()
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: `Success! Settings changed! ${
|
||||
this.needsRestart ? 'Restart required!' : ''
|
||||
}`,
|
||||
icon: null
|
||||
})
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
deleteSettings() {
|
||||
LNbits.utils
|
||||
.confirmDialog(
|
||||
'Are you sure you want to restore settings to default?'
|
||||
)
|
||||
.onOk(() => {
|
||||
LNbits.api
|
||||
.request(
|
||||
'DELETE',
|
||||
'/admin/api/v1/settings/?usr=' + this.g.user.id
|
||||
)
|
||||
.then(response => {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message:
|
||||
'Success! Restored settings to defaults, restart required!',
|
||||
icon: null
|
||||
})
|
||||
this.needsRestart = true
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
downloadBackup() {
|
||||
window.open('/admin/api/v1/backup/?usr=' + this.g.user.id, '_blank')
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<script src="/static/js/admin.js"></script>
|
||||
{% endblock %}
|
||||
|
20
lnbits/static/bundle.min.js
vendored
20
lnbits/static/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
316
lnbits/static/js/admin.js
Normal file
316
lnbits/static/js/admin.js
Normal file
@ -0,0 +1,316 @@
|
||||
new Vue({
|
||||
el: '#vue',
|
||||
mixins: [windowMixin],
|
||||
data: function () {
|
||||
return {
|
||||
settings: {},
|
||||
logs: [],
|
||||
serverlogEnabled: false,
|
||||
lnbits_theme_options: [
|
||||
'classic',
|
||||
'bitcoin',
|
||||
'flamingo',
|
||||
'cyber',
|
||||
'freedom',
|
||||
'mint',
|
||||
'autumn',
|
||||
'monochrome',
|
||||
'salvador'
|
||||
],
|
||||
auditData: {},
|
||||
statusData: {},
|
||||
statusDataTable: {
|
||||
columns: [
|
||||
{
|
||||
name: 'date',
|
||||
align: 'left',
|
||||
label: this.$t('date'),
|
||||
field: 'date'
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
align: 'left',
|
||||
label: this.$t('memo'),
|
||||
field: 'message'
|
||||
}
|
||||
]
|
||||
},
|
||||
formData: {},
|
||||
formAddAdmin: '',
|
||||
formAddUser: '',
|
||||
formAddExtensionsManifest: '',
|
||||
formAllowedIPs: '',
|
||||
formBlockedIPs: '',
|
||||
isSuperUser: false,
|
||||
wallet: {},
|
||||
cancel: {},
|
||||
topUpDialog: {
|
||||
show: false
|
||||
},
|
||||
tab: 'funding',
|
||||
needsRestart: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getSettings()
|
||||
this.getAudit()
|
||||
this.balance = +'{{ balance|safe }}'
|
||||
},
|
||||
computed: {
|
||||
lnbitsVersion() {
|
||||
return LNBITS_VERSION
|
||||
},
|
||||
checkChanges() {
|
||||
return !_.isEqual(this.settings, this.formData)
|
||||
},
|
||||
updateAvailable() {
|
||||
return LNBITS_VERSION !== this.statusData.version
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addAdminUser() {
|
||||
let addUser = this.formAddAdmin
|
||||
let admin_users = this.formData.lnbits_admin_users
|
||||
if (addUser && addUser.length && !admin_users.includes(addUser)) {
|
||||
//admin_users = [...admin_users, addUser]
|
||||
this.formData.lnbits_admin_users = [...admin_users, addUser]
|
||||
this.formAddAdmin = ''
|
||||
}
|
||||
},
|
||||
removeAdminUser(user) {
|
||||
let admin_users = this.formData.lnbits_admin_users
|
||||
this.formData.lnbits_admin_users = admin_users.filter(u => u !== user)
|
||||
},
|
||||
addAllowedUser() {
|
||||
let addUser = this.formAddUser
|
||||
let allowed_users = this.formData.lnbits_allowed_users
|
||||
if (addUser && addUser.length && !allowed_users.includes(addUser)) {
|
||||
this.formData.lnbits_allowed_users = [...allowed_users, addUser]
|
||||
this.formAddUser = ''
|
||||
}
|
||||
},
|
||||
removeAllowedUser(user) {
|
||||
let allowed_users = this.formData.lnbits_allowed_users
|
||||
this.formData.lnbits_allowed_users = allowed_users.filter(u => u !== user)
|
||||
},
|
||||
addExtensionsManifest() {
|
||||
const addManifest = this.formAddExtensionsManifest.trim()
|
||||
const manifests = this.formData.lnbits_extensions_manifests
|
||||
if (
|
||||
addManifest &&
|
||||
addManifest.length &&
|
||||
!manifests.includes(addManifest)
|
||||
) {
|
||||
this.formData.lnbits_extensions_manifests = [...manifests, addManifest]
|
||||
this.formAddExtensionsManifest = ''
|
||||
}
|
||||
},
|
||||
removeExtensionsManifest(manifest) {
|
||||
const manifests = this.formData.lnbits_extensions_manifests
|
||||
this.formData.lnbits_extensions_manifests = manifests.filter(
|
||||
m => m !== manifest
|
||||
)
|
||||
},
|
||||
async toggleServerLog() {
|
||||
this.serverlogEnabled = !this.serverlogEnabled
|
||||
if (this.serverlogEnabled) {
|
||||
const wsProto = location.protocol !== 'http:' ? 'wss://' : 'ws://'
|
||||
const digestHex = await LNbits.utils.digestMessage(this.g.user.id)
|
||||
const localUrl =
|
||||
wsProto +
|
||||
document.domain +
|
||||
':' +
|
||||
location.port +
|
||||
'/api/v1/ws/' +
|
||||
digestHex
|
||||
this.ws = new WebSocket(localUrl)
|
||||
this.ws.addEventListener('message', async ({data}) => {
|
||||
this.logs.push(data.toString())
|
||||
const scrollArea = this.$refs.logScroll
|
||||
if (scrollArea) {
|
||||
const scrollTarget = scrollArea.getScrollTarget()
|
||||
const duration = 0
|
||||
scrollArea.setScrollPosition(scrollTarget.scrollHeight, duration)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.ws.close()
|
||||
}
|
||||
},
|
||||
addAllowedIPs() {
|
||||
const allowedIPs = this.formAllowedIPs.trim()
|
||||
const allowed_ips = this.formData.lnbits_allowed_ips
|
||||
if (
|
||||
allowedIPs &&
|
||||
allowedIPs.length &&
|
||||
!allowed_ips.includes(allowedIPs)
|
||||
) {
|
||||
this.formData.lnbits_allowed_ips = [...allowed_ips, allowedIPs]
|
||||
this.formAllowedIPs = ''
|
||||
}
|
||||
},
|
||||
removeAllowedIPs(allowed_ip) {
|
||||
const allowed_ips = this.formData.lnbits_allowed_ips
|
||||
this.formData.lnbits_allowed_ips = allowed_ips.filter(
|
||||
a => a !== allowed_ip
|
||||
)
|
||||
},
|
||||
addBlockedIPs() {
|
||||
const blockedIPs = this.formBlockedIPs.trim()
|
||||
const blocked_ips = this.formData.lnbits_blocked_ips
|
||||
if (
|
||||
blockedIPs &&
|
||||
blockedIPs.length &&
|
||||
!blocked_ips.includes(blockedIPs)
|
||||
) {
|
||||
this.formData.lnbits_blocked_ips = [...blocked_ips, blockedIPs]
|
||||
this.formBlockedIPs = ''
|
||||
}
|
||||
},
|
||||
removeBlockedIPs(blocked_ip) {
|
||||
const blocked_ips = this.formData.lnbits_blocked_ips
|
||||
this.formData.lnbits_blocked_ips = blocked_ips.filter(
|
||||
b => b !== blocked_ip
|
||||
)
|
||||
},
|
||||
restartServer() {
|
||||
LNbits.api
|
||||
.request('GET', '/admin/api/v1/restart/?usr=' + this.g.user.id)
|
||||
.then(response => {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Success! Restarted Server',
|
||||
icon: null
|
||||
})
|
||||
this.needsRestart = false
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
topupWallet() {
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
'/admin/api/v1/topup/?usr=' + this.g.user.id,
|
||||
this.g.user.wallets[0].adminkey,
|
||||
this.wallet
|
||||
)
|
||||
.then(response => {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message:
|
||||
'Success! Added ' + this.wallet.amount + ' to ' + this.wallet.id,
|
||||
icon: null
|
||||
})
|
||||
this.wallet = {}
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
formatDate(date) {
|
||||
return moment(date * 1000).fromNow()
|
||||
},
|
||||
getNotifications() {
|
||||
if (this.settings.lnbits_notifications) {
|
||||
axios
|
||||
.get(this.settings.lnbits_status_manifest)
|
||||
.then(response => {
|
||||
this.statusData = response.data
|
||||
})
|
||||
.catch(error => {
|
||||
this.formData.lnbits_notifications = false
|
||||
error.response.data = {}
|
||||
error.response.data.message = 'Could not fetch status manifest.'
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
}
|
||||
},
|
||||
getAudit() {
|
||||
LNbits.api
|
||||
.request(
|
||||
'GET',
|
||||
'/admin/api/v1/audit/?usr=' + this.g.user.id,
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
.then(response => {
|
||||
this.auditData = response.data
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
getSettings() {
|
||||
LNbits.api
|
||||
.request(
|
||||
'GET',
|
||||
'/admin/api/v1/settings/?usr=' + this.g.user.id,
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
.then(response => {
|
||||
this.isSuperUser = response.data.is_super_user || false
|
||||
this.settings = response.data
|
||||
this.formData = {...this.settings}
|
||||
this.getNotifications()
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
updateSettings() {
|
||||
let data = _.omit(this.formData, [
|
||||
'is_super_user',
|
||||
'lnbits_allowed_funding_sources'
|
||||
])
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
'/admin/api/v1/settings/?usr=' + this.g.user.id,
|
||||
this.g.user.wallets[0].adminkey,
|
||||
data
|
||||
)
|
||||
.then(response => {
|
||||
this.needsRestart =
|
||||
this.settings.lnbits_backend_wallet_class !==
|
||||
this.formData.lnbits_backend_wallet_class ||
|
||||
this.settings.lnbits_killswitch !== this.formData.lnbits_killswitch
|
||||
this.settings = this.formData
|
||||
this.formData = _.clone(this.settings)
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: `Success! Settings changed! ${
|
||||
this.needsRestart ? 'Restart required!' : ''
|
||||
}`,
|
||||
icon: null
|
||||
})
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
deleteSettings() {
|
||||
LNbits.utils
|
||||
.confirmDialog('Are you sure you want to restore settings to default?')
|
||||
.onOk(() => {
|
||||
LNbits.api
|
||||
.request('DELETE', '/admin/api/v1/settings/?usr=' + this.g.user.id)
|
||||
.then(response => {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message:
|
||||
'Success! Restored settings to defaults, restart required!',
|
||||
icon: null
|
||||
})
|
||||
this.needsRestart = true
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
downloadBackup() {
|
||||
window.open('/admin/api/v1/backup/?usr=' + this.g.user.id, '_blank')
|
||||
}
|
||||
}
|
||||
})
|
165
lnbits/static/js/components/lnbits-funding-sources.js
Normal file
165
lnbits/static/js/components/lnbits-funding-sources.js
Normal file
@ -0,0 +1,165 @@
|
||||
Vue.component('lnbits-funding-sources', {
|
||||
mixins: [windowMixin],
|
||||
props: ['form-data', 'allowed-funding-sources'],
|
||||
computed: {
|
||||
fundingSources() {
|
||||
let tmp = []
|
||||
for (const [key, _, obj] of this.rawFundingSources) {
|
||||
const tmpObj = {}
|
||||
if (obj !== null) {
|
||||
for (let [k, v] of Object.entries(obj)) {
|
||||
tmpObj[k] = {label: v, value: null}
|
||||
}
|
||||
}
|
||||
tmp.push([key, tmpObj])
|
||||
}
|
||||
return new Map(tmp)
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rawFundingSources: [
|
||||
['VoidWallet', 'Void Wallet', null],
|
||||
[
|
||||
'FakeWallet',
|
||||
'Fake Wallet',
|
||||
{
|
||||
fake_wallet_secret: 'Secret'
|
||||
}
|
||||
],
|
||||
[
|
||||
'CoreLightningWallet',
|
||||
'Core Lightning',
|
||||
{
|
||||
corelightning_rpc: 'Endpoint'
|
||||
}
|
||||
],
|
||||
[
|
||||
'CoreLightningRestWallet',
|
||||
'Core Lightning Rest',
|
||||
{
|
||||
corelightning_rest_url: 'Endpoint',
|
||||
corelightning_rest_cert: 'Certificate',
|
||||
corelightning_rest_macaroon: 'Macaroon'
|
||||
}
|
||||
],
|
||||
[
|
||||
'LndRestWallet',
|
||||
'Lightning Network Daemon (LND Rest)',
|
||||
{
|
||||
lnd_rest_endpoint: 'Endpoint',
|
||||
lnd_rest_cert: 'Certificate',
|
||||
lnd_rest_macaroon: 'Macaroon',
|
||||
lnd_rest_macaroon_encrypted: 'Encrypted Macaroon'
|
||||
}
|
||||
],
|
||||
[
|
||||
'LndWallet',
|
||||
'Lightning Network Daemon (LND)',
|
||||
{
|
||||
lnd_grpc_endpoint: 'Endpoint',
|
||||
lnd_grpc_cert: 'Certificate',
|
||||
lnd_grpc_port: 'Port',
|
||||
lnd_grpc_admin_macaroon: 'Admin Macaroon',
|
||||
lnd_grpc_macaroon_encrypted: 'Encrypted Macaroon'
|
||||
}
|
||||
],
|
||||
[
|
||||
'LnTipsWallet',
|
||||
'LN.Tips',
|
||||
{
|
||||
lntips_api_endpoint: 'Endpoint',
|
||||
lntips_api_key: 'API Key'
|
||||
}
|
||||
],
|
||||
[
|
||||
'LNPayWallet',
|
||||
'LN Pay',
|
||||
{
|
||||
lnpay_api_endpoint: 'Endpoint',
|
||||
lnpay_api_key: 'API Key',
|
||||
lnpay_wallet_key: 'Wallet Key'
|
||||
}
|
||||
],
|
||||
[
|
||||
'EclairWallet',
|
||||
'Eclair (ACINQ)',
|
||||
{
|
||||
eclair_url: 'URL',
|
||||
eclair_pass: 'Password'
|
||||
}
|
||||
],
|
||||
[
|
||||
'LNbitsWallet',
|
||||
'LNBits',
|
||||
{
|
||||
lnbits_endpoint: 'Endpoint',
|
||||
lnbits_key: 'Admin Key'
|
||||
}
|
||||
],
|
||||
[
|
||||
'OpenNodeWallet',
|
||||
'OpenNode',
|
||||
{
|
||||
opennode_api_endpoint: 'Endpoint',
|
||||
opennode_key: 'Key'
|
||||
}
|
||||
],
|
||||
[
|
||||
'ClicheWallet',
|
||||
'Cliche (NBD)',
|
||||
{
|
||||
cliche_endpoint: 'Endpoint'
|
||||
}
|
||||
],
|
||||
[
|
||||
'SparkWallet',
|
||||
'Spark',
|
||||
{
|
||||
spark_url: 'Endpoint',
|
||||
spark_token: 'Token'
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="funding-sources">
|
||||
<h6 class="q-mt-xl q-mb-md">Funding Sources</h6>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p>Active Funding<small> (Requires server restart)</small></p>
|
||||
<q-select
|
||||
filled
|
||||
v-model="formData.lnbits_backend_wallet_class"
|
||||
hint="Select the active funding wallet"
|
||||
:options="allowedFundingSources"
|
||||
></q-select>
|
||||
</div>
|
||||
</div>
|
||||
<q-list
|
||||
class="q-mt-md"
|
||||
v-for="(fund, idx) in allowedFundingSources"
|
||||
:key="idx"
|
||||
>
|
||||
<div v-if="fundingSources.get(fund) && fund === formData.lnbits_backend_wallet_class">
|
||||
<div class="row"
|
||||
v-for="([key, prop], i) in Object.entries(fundingSources.get(fund))"
|
||||
:key="i"
|
||||
>
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
filled
|
||||
type="text"
|
||||
class="q-mt-sm"
|
||||
v-model="formData[key]"
|
||||
:label="prop.label"
|
||||
:hint="prop.hint"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-list>
|
||||
</div>
|
||||
`
|
||||
})
|
@ -31,6 +31,7 @@
|
||||
"/static/i18n/br.js",
|
||||
"/static/js/base.js",
|
||||
"/static/js/components.js",
|
||||
"/static/js/components/lnbits-funding-sources.js",
|
||||
"/static/js/bolt11-decoder.js"
|
||||
],
|
||||
"css": [
|
||||
|
@ -80,6 +80,7 @@
|
||||
"/static/i18n/br.js",
|
||||
"/static/js/base.js",
|
||||
"/static/js/components.js",
|
||||
"/static/js/components/lnbits-funding-sources.js",
|
||||
"/static/js/bolt11-decoder.js"
|
||||
],
|
||||
"css": [
|
||||
|
Loading…
Reference in New Issue
Block a user