mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-26 15:42:30 +01:00
* feat: add shortcuts for insert_query and update_query into `Database` example: await db.insert("table_name", base_model) * remove where from argument * chore: code clean-up * extension manager * lnbits-qrcode components * parse date from dict * refactor: make `settings` a fixture * chore: remove verbose key names * fix: time column * fix: cast balance to `int` * extension toggle vue3 * vue3 @input migration * fix: payment extra and payment hash * fix dynamic fields and ext db migration * remove shadow on cards in dark theme * screwed up and made more css pushes to this branch * attempt to make chip component in settings dynamic fields * dynamic chips * qrscanner * clean init admin settings * make get_user better * add dbversion model * remove update_payment_status/extra/details * traces for value and assertion errors * refactor services * add PaymentFiatAmount * return Payment on api endpoints * rename to get_user_from_account * refactor: just refactor (#2740) * rc5 * Fix db cache (#2741) * [refactor] split services.py (#2742) * refactor: spit `core.py` (#2743) * refactor: make QR more customizable * fix: print.html * fix: qrcode options * fix: white shadow on dark theme * fix: datetime wasnt parsed in dict_to_model * add timezone for conversion * only parse timestamp for sqlite, postgres does it * log internal payment success * fix: export wallet to phone QR * Adding a customisable border theme, like gradient (#2746) * fixed mobile scan btn * fix test websocket * fix get_payments tests * dict_to_model skip none values * preimage none instead of defaulting to 0000... * fixup test real invoice tests * fixed pheonixd for wss * fix nodemanager test settings * fix lnbits funding * only insert extension when they dont exist --------- Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com> Co-authored-by: Tiago Vasconcelos <talvasconcelos@gmail.com> Co-authored-by: Arc <ben@arc.wales> Co-authored-by: Arc <33088785+arcbtc@users.noreply.github.com>
375 lines
10 KiB
HTML
375 lines
10 KiB
HTML
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
|
|
%} {% block page %}
|
|
|
|
<q-dialog v-model="nodeInfoDialog.show">
|
|
<lnbits-node-qrcode :info="nodeInfoDialog.data"></lnbits-node-qrcode>
|
|
</q-dialog>
|
|
|
|
<div class="row q-col-gutter-md justify-center">
|
|
<div class="col q-gutter-y-md">
|
|
<q-card>
|
|
<div class="q-pa-md">
|
|
<div class="q-gutter-y-md">
|
|
<q-tabs v-model="tab" active-color="primary" align="justify">
|
|
<q-tab
|
|
name="dashboard"
|
|
:label="$t('dashboard')"
|
|
@update="val => tab = val.name"
|
|
></q-tab>
|
|
<q-tab
|
|
name="channels"
|
|
:label="$t('channels')"
|
|
@update="val => tab = val.name"
|
|
></q-tab>
|
|
<q-tab
|
|
name="transactions"
|
|
:label="$t('transactions')"
|
|
@update="val => tab = val.name"
|
|
></q-tab>
|
|
</q-tabs>
|
|
</div>
|
|
</div>
|
|
|
|
<q-form name="settings_form" id="settings_form">
|
|
<q-tab-panels v-model="tab" animated>
|
|
{% include "node/_tab_dashboard.html" %} {% include
|
|
"node/_tab_channels.html" %} {% include "node/_tab_transactions.html"
|
|
%}
|
|
</q-tab-panels>
|
|
</q-form>
|
|
</q-card>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
|
<script>
|
|
window.app = Vue.createApp({
|
|
el: '#vue',
|
|
config: {
|
|
globalProperties: {
|
|
LNbits,
|
|
msg: 'hello'
|
|
}
|
|
},
|
|
mixins: [window.windowMixin],
|
|
data: function () {
|
|
return {
|
|
isSuperUser: false,
|
|
wallet: {},
|
|
tab: 'dashboard',
|
|
payments: 1000,
|
|
info: {},
|
|
channel_stats: {},
|
|
|
|
channels: {
|
|
data: [],
|
|
filter: ''
|
|
},
|
|
|
|
activeBalance: {},
|
|
ranks: {},
|
|
|
|
peers: {
|
|
data: [],
|
|
filter: ''
|
|
},
|
|
|
|
connectPeerDialog: {
|
|
show: false,
|
|
data: {}
|
|
},
|
|
|
|
openChannelDialog: {
|
|
show: false,
|
|
data: {}
|
|
},
|
|
|
|
closeChannelDialog: {
|
|
show: false,
|
|
data: {}
|
|
},
|
|
|
|
nodeInfoDialog: {
|
|
show: false,
|
|
data: {}
|
|
},
|
|
|
|
transactionDetailsDialog: {
|
|
show: false,
|
|
data: {}
|
|
},
|
|
|
|
states: [
|
|
{label: 'Active', value: 'active', color: 'green'},
|
|
{label: 'Pending', value: 'pending', color: 'orange'},
|
|
{label: 'Inactive', value: 'inactive', color: 'grey'},
|
|
{label: 'Closed', value: 'closed', color: 'red'}
|
|
],
|
|
|
|
stateFilters: [
|
|
{label: 'Active', value: 'active'},
|
|
{label: 'Pending', value: 'pending'}
|
|
],
|
|
|
|
paymentsTable: {
|
|
data: [],
|
|
columns: [
|
|
{
|
|
name: 'pending',
|
|
label: ''
|
|
},
|
|
{
|
|
name: 'memo',
|
|
align: 'left',
|
|
label: this.$t('memo'),
|
|
field: 'memo'
|
|
},
|
|
{
|
|
name: 'date',
|
|
align: 'left',
|
|
label: this.$t('date'),
|
|
field: 'date',
|
|
sortable: true
|
|
},
|
|
{
|
|
name: 'sat',
|
|
align: 'right',
|
|
label: this.$t('amount') + ' (' + LNBITS_DENOMINATION + ')',
|
|
field: row => this.formatMsat(row.amount),
|
|
sortable: true
|
|
},
|
|
{
|
|
name: 'fee',
|
|
align: 'right',
|
|
label: this.$t('fee') + ' (m' + LNBITS_DENOMINATION + ')',
|
|
field: 'fee'
|
|
},
|
|
{
|
|
name: 'destination',
|
|
align: 'right',
|
|
label: 'Destination',
|
|
field: 'destination'
|
|
}
|
|
],
|
|
pagination: {
|
|
rowsPerPage: 10,
|
|
page: 1,
|
|
rowsNumber: 10
|
|
},
|
|
filter: null
|
|
},
|
|
invoiceTable: {
|
|
data: [],
|
|
columns: [
|
|
{
|
|
name: 'pending',
|
|
label: ''
|
|
},
|
|
{
|
|
name: 'memo',
|
|
align: 'left',
|
|
label: this.$t('memo'),
|
|
field: 'memo'
|
|
},
|
|
{
|
|
name: 'paid_at',
|
|
field: 'paid_at',
|
|
align: 'right',
|
|
label: 'Paid at',
|
|
sortable: true
|
|
},
|
|
{
|
|
name: 'expiry',
|
|
label: this.$t('expiry'),
|
|
field: 'expiry',
|
|
align: 'right',
|
|
sortable: true
|
|
},
|
|
{
|
|
name: 'amount',
|
|
label: this.$t('amount') + ' (' + LNBITS_DENOMINATION + ')',
|
|
field: row => this.formatMsat(row.amount),
|
|
sortable: true
|
|
}
|
|
],
|
|
pagination: {
|
|
rowsPerPage: 10,
|
|
page: 1,
|
|
rowsNumber: 10
|
|
},
|
|
filter: null
|
|
}
|
|
}
|
|
},
|
|
created: function () {
|
|
this.getInfo()
|
|
this.get1MLStats()
|
|
},
|
|
watch: {
|
|
tab: function (val) {
|
|
if (val === 'transactions' && !this.paymentsTable.data.length) {
|
|
this.getPayments()
|
|
this.getInvoices()
|
|
} else if (val === 'channels' && !this.channels.data.length) {
|
|
this.getChannels()
|
|
this.getPeers()
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
checkChanges() {
|
|
return !_.isEqual(this.settings, this.formData)
|
|
},
|
|
filteredChannels: function () {
|
|
return this.stateFilters
|
|
? this.channels.data.filter(channel => {
|
|
return this.stateFilters.find(({value}) => value == channel.state)
|
|
})
|
|
: this.channels.data
|
|
},
|
|
totalBalance: function () {
|
|
return this.filteredChannels.reduce(
|
|
(balance, channel) => {
|
|
balance.local_msat += channel.balance.local_msat
|
|
balance.remote_msat += channel.balance.remote_msat
|
|
balance.total_msat += channel.balance.total_msat
|
|
return balance
|
|
},
|
|
{local_msat: 0, remote_msat: 0, total_msat: 0}
|
|
)
|
|
}
|
|
},
|
|
methods: {
|
|
formatMsat: function (msat) {
|
|
return LNbits.utils.formatMsat(msat)
|
|
},
|
|
api: function (method, url, options) {
|
|
const params = new URLSearchParams(options?.query)
|
|
return LNbits.api
|
|
.request(method, `/node/api/v1${url}?${params}`, {}, options?.data)
|
|
.catch(error => {
|
|
LNbits.utils.notifyApiError(error)
|
|
})
|
|
},
|
|
getChannels: function () {
|
|
return this.api('GET', '/channels').then(response => {
|
|
this.channels.data = response.data
|
|
})
|
|
},
|
|
getInfo: function () {
|
|
return this.api('GET', '/info').then(response => {
|
|
this.info = response.data
|
|
this.channel_stats = response.data.channel_stats
|
|
})
|
|
},
|
|
get1MLStats: function () {
|
|
return this.api('GET', '/rank').then(response => {
|
|
this.ranks = response.data
|
|
})
|
|
},
|
|
getPayments: function (props) {
|
|
if (props) {
|
|
this.paymentsTable.pagination = props.pagination
|
|
}
|
|
let pagination = this.paymentsTable.pagination
|
|
const query = {
|
|
limit: pagination.rowsPerPage,
|
|
offset: (pagination.page - 1) * pagination.rowsPerPage ?? 0
|
|
}
|
|
return this.api('GET', '/payments', {query}).then(response => {
|
|
this.paymentsTable.data = response.data.data
|
|
this.paymentsTable.pagination.rowsNumber = response.data.total
|
|
})
|
|
},
|
|
getInvoices: function (props) {
|
|
if (props) {
|
|
this.invoiceTable.pagination = props.pagination
|
|
}
|
|
let pagination = this.invoiceTable.pagination
|
|
const query = {
|
|
limit: pagination.rowsPerPage,
|
|
offset: (pagination.page - 1) * pagination.rowsPerPage ?? 0
|
|
}
|
|
return this.api('GET', '/invoices', {query}).then(response => {
|
|
this.invoiceTable.data = response.data.data
|
|
this.invoiceTable.pagination.rowsNumber = response.data.total
|
|
})
|
|
},
|
|
getPeers: function () {
|
|
return this.api('GET', '/peers').then(response => {
|
|
this.peers.data = response.data
|
|
console.log('peers', this.peers)
|
|
})
|
|
},
|
|
connectPeer: function () {
|
|
console.log('peer', this.connectPeerDialog)
|
|
this.api('POST', '/peers', {data: this.connectPeerDialog.data}).then(
|
|
() => {
|
|
this.connectPeerDialog.show = false
|
|
this.getPeers()
|
|
}
|
|
)
|
|
},
|
|
disconnectPeer: function (id) {
|
|
LNbits.utils
|
|
.confirmDialog('Do you really wanna disconnect this peer?')
|
|
.onOk(() => {
|
|
this.api('DELETE', `/peers/${id}`).then(response => {
|
|
Quasar.Notify.create({
|
|
message: 'Disconnected',
|
|
icon: null
|
|
})
|
|
this.needsRestart = true
|
|
this.getPeers()
|
|
})
|
|
})
|
|
},
|
|
openChannel: function () {
|
|
this.api('POST', '/channels', {data: this.openChannelDialog.data})
|
|
.then(response => {
|
|
this.openChannelDialog.show = false
|
|
this.getChannels()
|
|
})
|
|
.catch(error => {
|
|
console.log(error)
|
|
})
|
|
},
|
|
showCloseChannelDialog: function (channel) {
|
|
this.closeChannelDialog.show = true
|
|
this.closeChannelDialog.data = {
|
|
force: false,
|
|
short_id: channel.short_id,
|
|
...channel.point
|
|
}
|
|
},
|
|
closeChannel: function () {
|
|
this.api('DELETE', '/channels', {
|
|
query: this.closeChannelDialog.data
|
|
}).then(response => {
|
|
this.closeChannelDialog.show = false
|
|
this.getChannels()
|
|
})
|
|
},
|
|
showOpenChannelDialog: function (peer_id) {
|
|
this.openChannelDialog.show = true
|
|
this.openChannelDialog.data = {peer_id, funding_amount: 0}
|
|
},
|
|
showNodeInfoDialog: function (node) {
|
|
this.nodeInfoDialog.show = true
|
|
this.nodeInfoDialog.data = node
|
|
},
|
|
showTransactionDetailsDialog: function (details) {
|
|
this.transactionDetailsDialog.show = true
|
|
this.transactionDetailsDialog.data = details
|
|
console.log('details', details)
|
|
},
|
|
shortenNodeId(nodeId) {
|
|
return nodeId
|
|
? nodeId.substring(0, 5) + '...' + nodeId.substring(nodeId.length - 5)
|
|
: '...'
|
|
}
|
|
}
|
|
})
|
|
</script>
|
|
<script src="{{ static_url_for('static', 'js/node.js') }}"></script>
|
|
{% endblock %}
|