refactor: move /node into vue component (#3460)
This commit is contained in:
parent
d142d76148
commit
8506199c2f
17 changed files with 1349 additions and 1256 deletions
3
lnbits/core/templates/empty_public.html
Normal file
3
lnbits/core/templates/empty_public.html
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{% extends "public.html" %} {% from "macros.jinja" import window_vars with
|
||||||
|
context %} {% block scripts %} {{ window_vars() }} {% endblock %} {% block page
|
||||||
|
%} {% endblock %}
|
||||||
|
|
@ -1,379 +0,0 @@
|
||||||
<q-tab-panel name="channels">
|
|
||||||
<q-dialog v-model="connectPeerDialog.show" position="top">
|
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
||||||
<q-form class="q-gutter-md">
|
|
||||||
<q-input
|
|
||||||
dense
|
|
||||||
type="text"
|
|
||||||
filled
|
|
||||||
v-model="connectPeerDialog.data.uri"
|
|
||||||
label="Node URI"
|
|
||||||
hint="pubkey@host:port"
|
|
||||||
></q-input>
|
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
:label="$t('connect')"
|
|
||||||
color="primary"
|
|
||||||
@click="connectPeer"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('cancel')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-form>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-dialog v-model="setFeeDialog.show">
|
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
||||||
<label class="text-h6">Set Channel Fee</label>
|
|
||||||
<p class="text-caption" v-text="setFeeDialog.channel_id"></p>
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-form class="q-gutter-md">
|
|
||||||
<q-input
|
|
||||||
dense
|
|
||||||
type="number"
|
|
||||||
filled
|
|
||||||
v-model.number="setFeeDialog.data.fee_ppm"
|
|
||||||
label="Fee Rate PPM"
|
|
||||||
></q-input>
|
|
||||||
<q-input
|
|
||||||
dense
|
|
||||||
type="number"
|
|
||||||
filled
|
|
||||||
v-model.number="setFeeDialog.data.fee_base_msat"
|
|
||||||
label="Fee Base msat"
|
|
||||||
></q-input>
|
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
:label="$t('set')"
|
|
||||||
color="primary"
|
|
||||||
@click="setChannelFee(setFeeDialog.channel_id)"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('cancel')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-form>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-dialog v-model="openChannelDialog.show">
|
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
||||||
<q-form class="q-gutter-md">
|
|
||||||
<q-input
|
|
||||||
dense
|
|
||||||
type="text"
|
|
||||||
filled
|
|
||||||
v-model="openChannelDialog.data.peer_id"
|
|
||||||
label="Peer ID"
|
|
||||||
></q-input>
|
|
||||||
<q-input
|
|
||||||
dense
|
|
||||||
type="number"
|
|
||||||
filled
|
|
||||||
v-model.number="openChannelDialog.data.funding_amount"
|
|
||||||
label="Funding Amount"
|
|
||||||
></q-input>
|
|
||||||
<q-expansion-item icon="warning" label="Advanced">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="column q-gutter-md">
|
|
||||||
<q-input
|
|
||||||
dense
|
|
||||||
type="number"
|
|
||||||
filled
|
|
||||||
v-model.number="openChannelDialog.data.push_amount"
|
|
||||||
label="Push Amount"
|
|
||||||
hint="This gifts sats to the other side!"
|
|
||||||
></q-input>
|
|
||||||
|
|
||||||
<q-input
|
|
||||||
dense
|
|
||||||
type="number"
|
|
||||||
filled
|
|
||||||
v-model.number="openChannelDialog.data.fee_rate"
|
|
||||||
label="Fee Rate"
|
|
||||||
></q-input>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
:label="$t('open')"
|
|
||||||
color="primary"
|
|
||||||
@click="openChannel"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('cancel')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-form>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-dialog v-model="closeChannelDialog.show" position="top">
|
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
||||||
<q-form class="q-gutter-md">
|
|
||||||
<div>
|
|
||||||
<q-checkbox v-model="closeChannelDialog.data.force" label="Force" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
:label="$t('close')"
|
|
||||||
color="primary"
|
|
||||||
@click="closeChannel"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('cancel')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-form>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<div class="row q-col-gutter-lg">
|
|
||||||
<div class="col-12 col-xl-6">
|
|
||||||
<q-card class="full-height">
|
|
||||||
<q-card-section class="q-gutter-y-sm">
|
|
||||||
<div class="row items-center q-mt-none q-gutter-x-sm no-wrap">
|
|
||||||
<div class="col-grow text-h6 q-my-none col-grow">Channels</div>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
clearable
|
|
||||||
v-model="channels.filter"
|
|
||||||
placeholder="Search..."
|
|
||||||
class="col-auto"
|
|
||||||
></q-input>
|
|
||||||
<q-select
|
|
||||||
dense
|
|
||||||
size="sm"
|
|
||||||
style="min-width: 200px"
|
|
||||||
filled
|
|
||||||
multiple
|
|
||||||
clearable
|
|
||||||
v-model="stateFilters"
|
|
||||||
:options="this.states"
|
|
||||||
class="col-auto"
|
|
||||||
></q-select>
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
size="md"
|
|
||||||
class="col-auto"
|
|
||||||
@click="showOpenChannelDialog()"
|
|
||||||
>
|
|
||||||
Open channel
|
|
||||||
</q-btn>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="text-subtitle1 col-grow">Total</div>
|
|
||||||
<lnbits-channel-balance
|
|
||||||
:balance="this.totalBalance"
|
|
||||||
></lnbits-channel-balance>
|
|
||||||
</div>
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-table
|
|
||||||
dense
|
|
||||||
flat
|
|
||||||
:rows="this.filteredChannels"
|
|
||||||
:filter="channels.filter"
|
|
||||||
no-data-label="No channels opened"
|
|
||||||
>
|
|
||||||
<template v-slot:header="props">
|
|
||||||
<q-tr :props="props" style="height: 0"> </q-tr>
|
|
||||||
</template>
|
|
||||||
<template v-slot:body="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<div class="q-pb-sm">
|
|
||||||
<div class="row items-center q-gutter-sm">
|
|
||||||
<div class="text-subtitle1" v-text="props.row.name"></div>
|
|
||||||
<div class="text-caption" v-if="props.row.peer_id">
|
|
||||||
<span>Peer ID</span>
|
|
||||||
<q-btn
|
|
||||||
size="xs"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="content_paste"
|
|
||||||
@click="copyText(props.row.peer_id)"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
<div class="text-caption col-grow">
|
|
||||||
<span>Fees</span>
|
|
||||||
<q-btn
|
|
||||||
size="xs"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="settings"
|
|
||||||
@click="showSetFeeDialog(props.row.id)"
|
|
||||||
></q-btn>
|
|
||||||
<span v-if="props.row.fee_ppm">
|
|
||||||
<span v-text="props.row.fee_ppm"></span> ppm,
|
|
||||||
<span v-text="props.row.fee_base_msat"></span> msat
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="text-caption" v-if="props.row.id">
|
|
||||||
<span>Channel ID</span>
|
|
||||||
<q-btn
|
|
||||||
size="xs"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="content_paste"
|
|
||||||
@click="copyText(props.row.id)"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
<div class="text-caption" v-if="props.row.short_id">
|
|
||||||
<span v-text="props.row.short_id"></span>
|
|
||||||
<q-btn
|
|
||||||
size="xs"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="content_paste"
|
|
||||||
@click="copyText(props.row.short_id)"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
<q-badge
|
|
||||||
rounded
|
|
||||||
:color="states.find(s => s.value == props.row.state)?.color"
|
|
||||||
v-text="states.find(s => s.value == props.row.state)?.label"
|
|
||||||
>
|
|
||||||
</q-badge>
|
|
||||||
<q-btn
|
|
||||||
:disable='props.row.state !== "active"'
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
size="md"
|
|
||||||
@click="showCloseChannelDialog(props.row)"
|
|
||||||
icon="cancel"
|
|
||||||
color="pink"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<lnbits-channel-balance
|
|
||||||
:balance="props.row.balance"
|
|
||||||
:color="props.row.color"
|
|
||||||
></lnbits-channel-balance>
|
|
||||||
</div>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
</q-table>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-xl-6">
|
|
||||||
<q-card class="full-height">
|
|
||||||
<q-card-section class="column q-gutter-y-sm">
|
|
||||||
<div
|
|
||||||
class="row items-center q-mt-none justify-between q-gutter-x-md no-wrap"
|
|
||||||
>
|
|
||||||
<div class="col-grow text-h6 q-my-none">Peers</div>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
clearable
|
|
||||||
v-model="peers.filter"
|
|
||||||
placeholder="Search..."
|
|
||||||
class="col-auto"
|
|
||||||
></q-input>
|
|
||||||
<q-btn
|
|
||||||
class="col-auto"
|
|
||||||
color="primary"
|
|
||||||
@click="connectPeerDialog.show = true"
|
|
||||||
>
|
|
||||||
Connect Peer
|
|
||||||
</q-btn>
|
|
||||||
</div>
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-table
|
|
||||||
dense
|
|
||||||
flat
|
|
||||||
:rows="peers.data"
|
|
||||||
:filter="peers.filter"
|
|
||||||
no-data-label="No transactions made yet"
|
|
||||||
>
|
|
||||||
<template v-slot:header="props">
|
|
||||||
<q-tr :props="props" style="height: 0"> </q-tr>
|
|
||||||
</template>
|
|
||||||
<template v-slot:body="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<div class="row no-wrap items-center q-gutter-sm">
|
|
||||||
<div class="q-my-sm col-grow">
|
|
||||||
<div
|
|
||||||
class="text-subtitle1 text-bold"
|
|
||||||
v-text="props.row.alias"
|
|
||||||
></div>
|
|
||||||
<div class="row items-center q-gutter-sm">
|
|
||||||
<q-badge
|
|
||||||
:style="`background-color: #${props.row.color}`"
|
|
||||||
class="text-bold"
|
|
||||||
v-text="'#'+props.row.color"
|
|
||||||
>
|
|
||||||
</q-badge>
|
|
||||||
<div
|
|
||||||
class="text-bold"
|
|
||||||
v-text="shortenNodeId(props.row.id)"
|
|
||||||
></div>
|
|
||||||
<q-btn
|
|
||||||
size="xs"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="content_paste"
|
|
||||||
@click="copyText(props.row.id)"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
size="xs"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="qr_code"
|
|
||||||
@click="showNodeInfoDialog(props.row)"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
@click="showOpenChannelDialog(props.row.id)"
|
|
||||||
>
|
|
||||||
Open channel
|
|
||||||
</q-btn>
|
|
||||||
<q-btn
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
size="md"
|
|
||||||
@click="disconnectPeer(props.row.id)"
|
|
||||||
icon="cancel"
|
|
||||||
color="pink"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
</q-table>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
<q-tab-panel name="dashboard">
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<lnbits-node-info :info="this.info"></lnbits-node-info>
|
|
||||||
<div class="row q-col-gutter-lg q-mt-sm">
|
|
||||||
<div class="col-12 col-md-8 q-gutter-y-md">
|
|
||||||
<div class="row q-col-gutter-md q-pb-lg">
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
:title="$t('total_capacity')"
|
|
||||||
:msat="this.channel_stats.total_capacity"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
|
||||||
<lnbits-stat title="Balance" :msat="this.info.balance_msat" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
title="Fees collected"
|
|
||||||
:msat="this.info.fees?.total_msat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
title="Onchain Balance"
|
|
||||||
:btc="this.info.onchain_balance_sat / 100000000"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
title="Onchain Confirmed"
|
|
||||||
:btc="this.info.onchain_confirmed_sat / 100000000"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
|
||||||
<lnbits-stat title="Peers" :amount="this.info.num_peers" />
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
:title="$t('avg_channel_size')"
|
|
||||||
:msat="this.channel_stats.avg_size"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
:title="$t('biggest_channel_size')"
|
|
||||||
:msat="this.channel_stats.biggest_size"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
:title="$t('smallest_channel_size')"
|
|
||||||
:msat="this.channel_stats.smallest_size"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="column col-12 col-md-4 q-gutter-y-md">
|
|
||||||
<lnbits-node-ranks :ranks="this.ranks"></lnbits-node-ranks>
|
|
||||||
<lnbits-channel-stats
|
|
||||||
:stats="this.channel_stats"
|
|
||||||
></lnbits-channel-stats>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
@ -1,314 +0,0 @@
|
||||||
<q-tab-panel name="transactions">
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<q-dialog v-model="transactionDetailsDialog.show" position="top">
|
|
||||||
<q-card class="my-card">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-center q-mb-lg">
|
|
||||||
<div
|
|
||||||
v-if="transactionDetailsDialog.data.isIn && transactionDetailsDialog.data.pending"
|
|
||||||
>
|
|
||||||
<q-icon
|
|
||||||
size="18px"
|
|
||||||
:name="'call_received'"
|
|
||||||
:color="'green'"
|
|
||||||
></q-icon>
|
|
||||||
<span v-text="$t('payment_received')"></span>
|
|
||||||
</div>
|
|
||||||
<div class="row q-my-md">
|
|
||||||
<div class="col-3"><b v-text="$t('payment_hash')"></b>:</div>
|
|
||||||
<div class="col-9 text-wrap mono">
|
|
||||||
<span
|
|
||||||
v-text="transactionDetailsDialog.data.payment_hash"
|
|
||||||
></span>
|
|
||||||
<q-icon
|
|
||||||
name="content_copy"
|
|
||||||
@click="copyText(transactionDetailsDialog.data.payment_hash)"
|
|
||||||
size="1em"
|
|
||||||
color="grey"
|
|
||||||
class="q-mb-xs cursor-pointer"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
v-if="transactionDetailsDialog.data.preimage && !transactionDetailsDialog.data.pending"
|
|
||||||
>
|
|
||||||
<div class="col-3"><b v-text="$t('payment_proof')"></b>:</div>
|
|
||||||
<div class="col-9 text-wrap mono">
|
|
||||||
<span v-text="transactionDetailsDialog.data.preimage"></span>
|
|
||||||
<q-icon
|
|
||||||
name="content_copy"
|
|
||||||
@click="copyText(transactionDetailsDialog.data.preimage)"
|
|
||||||
size="1em"
|
|
||||||
color="grey"
|
|
||||||
class="q-mb-xs cursor-pointer"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="transactionDetailsDialog.data.bolt11"
|
|
||||||
class="text-center q-mb-lg"
|
|
||||||
>
|
|
||||||
<a :href="'lightning:' + transactionDetailsDialog.data.bolt11">
|
|
||||||
<q-responsive :ratio="1" class="q-mx-xl">
|
|
||||||
<qrcode-vue
|
|
||||||
:value="'lightning:' + transactionDetailsDialog.data.bolt11.toUpperCase()"
|
|
||||||
:options="{width: 340}"
|
|
||||||
class="rounded-borders"
|
|
||||||
></qrcode-vue>
|
|
||||||
</q-responsive>
|
|
||||||
</a>
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
@click="copyText(transactionDetailsDialog.data.bolt11)"
|
|
||||||
:label="$t('copy_invoice')"
|
|
||||||
class="q-mt-sm"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<div class="row q-col-gutter-md q-pb-lg"></div>
|
|
||||||
|
|
||||||
<div class="row q-col-gutter-lg">
|
|
||||||
<div class="col-12 col-lg-6 q-gutter-y-md">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="row items-center no-wrap q-mb-sm">
|
|
||||||
<div class="col text-h6 q-my-none">Payments</div>
|
|
||||||
<q-input
|
|
||||||
v-if="payments.length > 10"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
clearable
|
|
||||||
v-model="paymentsTable.filter"
|
|
||||||
debounce="300"
|
|
||||||
placeholder="Search by tag, memo, amount"
|
|
||||||
class="q-mb-md"
|
|
||||||
>
|
|
||||||
</q-input>
|
|
||||||
</div>
|
|
||||||
<q-table
|
|
||||||
dense
|
|
||||||
flat
|
|
||||||
:rows="paymentsTable.data"
|
|
||||||
:columns="paymentsTable.columns"
|
|
||||||
v-model:pagination="paymentsTable.pagination"
|
|
||||||
row-key="payment_hash"
|
|
||||||
no-data-label="No transactions made yet"
|
|
||||||
:filter="paymentsTable.filter"
|
|
||||||
@request="getPayments"
|
|
||||||
>
|
|
||||||
<template v-slot:body-cell-pending="props">
|
|
||||||
<q-td auto-width class="text-center">
|
|
||||||
<q-icon
|
|
||||||
v-if="!props.row.pending"
|
|
||||||
size="xs"
|
|
||||||
name="call_made"
|
|
||||||
color="green"
|
|
||||||
@click="showTransactionDetailsDialog(props.row)"
|
|
||||||
></q-icon>
|
|
||||||
<q-icon
|
|
||||||
v-else
|
|
||||||
size="xs"
|
|
||||||
name="settings_ethernet"
|
|
||||||
color="grey"
|
|
||||||
@click="showTransactionDetailsDialog(props.row)"
|
|
||||||
>
|
|
||||||
<q-tooltip>Pending</q-tooltip>
|
|
||||||
</q-icon>
|
|
||||||
<q-dialog
|
|
||||||
v-model="props.row.expand"
|
|
||||||
:props="props"
|
|
||||||
position="top"
|
|
||||||
>
|
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
||||||
<div class="text-center q-mb-lg">
|
|
||||||
<div v-if="props.row.isIn && props.row.pending">
|
|
||||||
<q-icon
|
|
||||||
name="settings_ethernet"
|
|
||||||
color="grey"
|
|
||||||
></q-icon>
|
|
||||||
<span v-text="$t('invoice_waiting')"></span>
|
|
||||||
<lnbits-payment-details
|
|
||||||
:payment="props.row"
|
|
||||||
></lnbits-payment-details>
|
|
||||||
<div
|
|
||||||
v-if="props.row.bolt11"
|
|
||||||
class="text-center q-mb-lg"
|
|
||||||
>
|
|
||||||
<a :href="'lightning:' + props.row.bolt11">
|
|
||||||
<q-responsive :ratio="1" class="q-mx-xl">
|
|
||||||
<qrcode-vue
|
|
||||||
:value="'lightning:' + props.row.bolt11.toUpperCase()"
|
|
||||||
:options="{width: 340}"
|
|
||||||
class="rounded-borders"
|
|
||||||
></qrcode-vue>
|
|
||||||
</q-responsive>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
@click="copyText(props.row.bolt11)"
|
|
||||||
:label="$t('copy_invoice')"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('close')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="props.row.isPaid && props.row.isIn">
|
|
||||||
<q-icon
|
|
||||||
size="18px"
|
|
||||||
:name="'call_received'"
|
|
||||||
:color="'green'"
|
|
||||||
></q-icon>
|
|
||||||
<span v-text="$t('payment_received')"></span>
|
|
||||||
<lnbits-payment-details
|
|
||||||
:payment="props.row"
|
|
||||||
></lnbits-payment-details>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="props.row.isPaid && props.row.isOut">
|
|
||||||
<q-icon
|
|
||||||
size="18px"
|
|
||||||
:name="'call_made'"
|
|
||||||
:color="'pink'"
|
|
||||||
></q-icon>
|
|
||||||
<span v-text="$t('payment_sent')"></span>
|
|
||||||
<lnbits-payment-details
|
|
||||||
:payment="props.row"
|
|
||||||
></lnbits-payment-details>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="props.row.isOut && props.row.pending">
|
|
||||||
<q-icon
|
|
||||||
name="settings_ethernet"
|
|
||||||
color="grey"
|
|
||||||
></q-icon>
|
|
||||||
<span v-text="$t('outgoing_payment_pending')"></span>
|
|
||||||
<lnbits-payment-details
|
|
||||||
:payment="props.row"
|
|
||||||
></lnbits-payment-details>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
</q-td>
|
|
||||||
</template>
|
|
||||||
<template v-slot:body-cell-date="props">
|
|
||||||
<q-td auto-width key="date" :props="props">
|
|
||||||
<lnbits-date :ts="props.row.time"></lnbits-date>
|
|
||||||
</q-td>
|
|
||||||
</template>
|
|
||||||
<template v-slot:body-cell-destination="props">
|
|
||||||
<q-td auto-width key="destination">
|
|
||||||
<div class="row items-center justify-between no-wrap">
|
|
||||||
<q-badge
|
|
||||||
:style="`background-color: #${props.row.destination?.color}`"
|
|
||||||
class="text-bold"
|
|
||||||
v-text="props.row.destination?.alias"
|
|
||||||
></q-badge>
|
|
||||||
<div>
|
|
||||||
<q-btn
|
|
||||||
size="xs"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="content_paste"
|
|
||||||
@click="copyText(info.id)"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
size="xs"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="qr_code"
|
|
||||||
@click="showNodeInfoDialog(props.row.destination)"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-td>
|
|
||||||
</template>
|
|
||||||
</q-table>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-lg-6 q-gutter-y-md">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="row items-center no-wrap q-mb-sm">
|
|
||||||
<div class="col text-h6 q-my-none">Invoices</div>
|
|
||||||
<q-input
|
|
||||||
v-if="payments.length > 10"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
clearable
|
|
||||||
v-model="paymentsTable.filter"
|
|
||||||
debounce="300"
|
|
||||||
placeholder="Search by tag, memo, amount"
|
|
||||||
class="q-mb-md"
|
|
||||||
>
|
|
||||||
</q-input>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<q-table
|
|
||||||
dense
|
|
||||||
flat
|
|
||||||
:rows="invoiceTable.data"
|
|
||||||
:columns="invoiceTable.columns"
|
|
||||||
v-model:pagination="invoiceTable.pagination"
|
|
||||||
no-data-label="No transactions made yet"
|
|
||||||
:filter="invoiceTable.filter"
|
|
||||||
@request="getInvoices"
|
|
||||||
>
|
|
||||||
<template v-slot:body-cell-pending="props">
|
|
||||||
<q-td auto-width class="text-center">
|
|
||||||
<q-icon
|
|
||||||
v-if="!props.row.pending"
|
|
||||||
size="xs"
|
|
||||||
name="call_received"
|
|
||||||
color="green"
|
|
||||||
@click="showTransactionDetailsDialog(props.row)"
|
|
||||||
></q-icon>
|
|
||||||
<q-icon
|
|
||||||
v-else
|
|
||||||
size="xs"
|
|
||||||
name="settings_ethernet"
|
|
||||||
color="grey"
|
|
||||||
@click="showTransactionDetailsDialog(props.row)"
|
|
||||||
>
|
|
||||||
<q-tooltip>Pending</q-tooltip>
|
|
||||||
</q-icon>
|
|
||||||
</q-td>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:body-cell-paid_at="props">
|
|
||||||
<q-td auto-width :props="props">
|
|
||||||
<lnbits-date
|
|
||||||
v-if="props.row.paid_at"
|
|
||||||
:ts="props.row.paid_at"
|
|
||||||
></lnbits-date>
|
|
||||||
</q-td>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:body-cell-expiry="props">
|
|
||||||
<q-td auto-width :props="props">
|
|
||||||
<lnbits-date
|
|
||||||
v-if="props.row.expiry"
|
|
||||||
:ts="props.row.expiry"
|
|
||||||
></lnbits-date>
|
|
||||||
</q-td>
|
|
||||||
</template>
|
|
||||||
</q-table>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
{% if not ajax %} {% extends "base.html" %} {% endif %}
|
|
||||||
<!---->
|
|
||||||
{% from "macros.jinja" import window_vars with context %}
|
|
||||||
<!---->
|
|
||||||
{% block scripts %} {{ window_vars(user) }}{% endblock %} {% block page %}
|
|
||||||
|
|
||||||
<q-dialog v-model="nodeInfoDialog.show" position="top">
|
|
||||||
<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 %}
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
{% extends "public.html" %} {% from "macros.jinja" import window_vars with
|
|
||||||
context %} {% block page %}
|
|
||||||
|
|
||||||
<div class="q-ma-lg-xl q-mx-auto q-ma-xl" style="max-width: 1048px">
|
|
||||||
<lnbits-node-info :info="this.info"></lnbits-node-info>
|
|
||||||
|
|
||||||
<div class="row q-col-gutter-lg q-mt-sm">
|
|
||||||
<div class="col-12 col-md-8 q-gutter-y-md">
|
|
||||||
<div class="row q-col-gutter-md q-pb-lg">
|
|
||||||
<div class="col-12 col-md-6 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
:title="$t('total_capacity')"
|
|
||||||
:msat="this.channel_stats.total_capacity"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-md-6 q-gutter-y-md">
|
|
||||||
<lnbits-stat title="Peers" :amount="this.info.num_peers" />
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
:title="$t('avg_channel_size')"
|
|
||||||
:msat="this.channel_stats.avg_size"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
:title="$t('biggest_channel_size')"
|
|
||||||
:msat="this.channel_stats.biggest_size"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
:title="$t('smallest_channel_size')"
|
|
||||||
:msat="this.channel_stats.smallest_size"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6 q-gutter-y-md">
|
|
||||||
<lnbits-stat
|
|
||||||
:title="$t('smallest_channel_size')"
|
|
||||||
:msat="this.channel_stats.smallest_size"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="column col-12 col-md-4 q-gutter-y-md">
|
|
||||||
<lnbits-node-ranks :ranks="this.ranks"></lnbits-node-ranks>
|
|
||||||
<lnbits-channel-stats :stats="this.channel_stats"></lnbits-channel-stats>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
|
||||||
<script src="{{ static_url_for('static', 'js/node.js') }}"></script>
|
|
||||||
<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: [],
|
|
||||||
activeBalance: {},
|
|
||||||
ranks: {},
|
|
||||||
|
|
||||||
peers: [],
|
|
||||||
|
|
||||||
connectPeerDialog: {
|
|
||||||
show: false,
|
|
||||||
data: {}
|
|
||||||
},
|
|
||||||
|
|
||||||
openChannelDialog: {
|
|
||||||
show: false,
|
|
||||||
data: {}
|
|
||||||
},
|
|
||||||
|
|
||||||
closeChannelDialog: {
|
|
||||||
show: false,
|
|
||||||
data: {}
|
|
||||||
},
|
|
||||||
|
|
||||||
nodeInfoDialog: {
|
|
||||||
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'}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created: function () {
|
|
||||||
this.getInfo()
|
|
||||||
this.get1MLStats()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
formatMsat: function (msat) {
|
|
||||||
return LNbits.utils.formatMsat(msat)
|
|
||||||
},
|
|
||||||
api: function (method, url, data) {
|
|
||||||
return LNbits.api.request(method, '/node/public/api/v1' + url, {}, data)
|
|
||||||
},
|
|
||||||
getInfo: function () {
|
|
||||||
this.api('GET', '/info', {}).then(response => {
|
|
||||||
this.info = response.data
|
|
||||||
this.channel_stats = response.data.channel_stats
|
|
||||||
})
|
|
||||||
},
|
|
||||||
get1MLStats: function () {
|
|
||||||
this.api('GET', '/rank', {}).then(response => {
|
|
||||||
this.ranks = response.data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
@ -426,43 +426,6 @@ async def manifest(request: Request, usr: str):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get("/node", response_class=HTMLResponse)
|
|
||||||
async def node(request: Request, user: User = Depends(check_admin)):
|
|
||||||
if not settings.lnbits_node_ui:
|
|
||||||
raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE)
|
|
||||||
|
|
||||||
funding_source = get_funding_source()
|
|
||||||
_, balance = await funding_source.status()
|
|
||||||
|
|
||||||
return template_renderer().TemplateResponse(
|
|
||||||
request,
|
|
||||||
"node/index.html",
|
|
||||||
{
|
|
||||||
"user": user.json(),
|
|
||||||
"balance": balance,
|
|
||||||
"wallets": user.wallets[0].json(),
|
|
||||||
"ajax": _is_ajax_request(request),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get("/node/public", response_class=HTMLResponse)
|
|
||||||
async def node_public(request: Request):
|
|
||||||
if not settings.lnbits_public_node_ui:
|
|
||||||
raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE)
|
|
||||||
|
|
||||||
funding_source = get_funding_source()
|
|
||||||
_, balance = await funding_source.status()
|
|
||||||
|
|
||||||
return template_renderer().TemplateResponse(
|
|
||||||
request,
|
|
||||||
"node/public.html",
|
|
||||||
{
|
|
||||||
"balance": balance,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get("/admin", response_class=HTMLResponse)
|
@generic_router.get("/admin", response_class=HTMLResponse)
|
||||||
async def admin_index(request: Request, user: User = Depends(check_admin)):
|
async def admin_index(request: Request, user: User = Depends(check_admin)):
|
||||||
if not settings.lnbits_admin_ui:
|
if not settings.lnbits_admin_ui:
|
||||||
|
|
@ -517,14 +480,30 @@ async def audit_index(request: Request, user: User = Depends(check_admin)):
|
||||||
@generic_router.get("/payments", response_class=HTMLResponse)
|
@generic_router.get("/payments", response_class=HTMLResponse)
|
||||||
async def empty_index(request: Request, user: User = Depends(check_user_exists)):
|
async def empty_index(request: Request, user: User = Depends(check_user_exists)):
|
||||||
return template_renderer().TemplateResponse(
|
return template_renderer().TemplateResponse(
|
||||||
|
request,
|
||||||
"empty.html",
|
"empty.html",
|
||||||
{
|
{
|
||||||
"request": request,
|
|
||||||
"user": user.json(),
|
"user": user.json(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@generic_router.get("/node", response_class=HTMLResponse)
|
||||||
|
async def empty_admin_index(request: Request, admin: User = Depends(check_admin)):
|
||||||
|
return template_renderer().TemplateResponse(
|
||||||
|
request,
|
||||||
|
"empty.html",
|
||||||
|
{
|
||||||
|
"user": admin.json(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@generic_router.get("/node/public", response_class=HTMLResponse)
|
||||||
|
async def node_public(request: Request):
|
||||||
|
return template_renderer().TemplateResponse(request, "empty_public.html")
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get("/uuidv4/{hex_value}")
|
@generic_router.get("/uuidv4/{hex_value}")
|
||||||
async def hex_to_uuid4(hex_value: str):
|
async def hex_to_uuid4(hex_value: str):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
2
lnbits/static/bundle-components.min.js
vendored
2
lnbits/static/bundle-components.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -656,3 +656,261 @@ window.app.component('separator-text', {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
window.app.component('lnbits-node-ranks', {
|
||||||
|
props: ['ranks'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
stats: [
|
||||||
|
{label: 'Capacity', key: 'capacity'},
|
||||||
|
{label: 'Channels', key: 'channelcount'},
|
||||||
|
{label: 'Age', key: 'age'},
|
||||||
|
{label: 'Growth', key: 'growth'},
|
||||||
|
{label: 'Availability', key: 'availability'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<q-card class='q-my-none'>
|
||||||
|
<div class='column q-ma-md'>
|
||||||
|
<h5 class='text-subtitle1 text-bold q-my-none'>1ml Node Rank</h5>
|
||||||
|
<div v-for='stat in stats' class='q-gutter-sm'>
|
||||||
|
<div class='row items-center'>
|
||||||
|
<div class='col-9'>{{ stat.label }}</div>
|
||||||
|
<div class='col-3 text-subtitle1 text-bold'>
|
||||||
|
{{ (ranks && ranks[stat.key]) ?? '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app.component('lnbits-channel-stats', {
|
||||||
|
props: ['stats'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
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'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<q-card>
|
||||||
|
<div class='column q-ma-md'>
|
||||||
|
<h5 class='text-subtitle1 text-bold q-my-none'>Channels</h5>
|
||||||
|
<div v-for='state in states' class='q-gutter-sm'>
|
||||||
|
<div class='row'>
|
||||||
|
<div class='col-9'>
|
||||||
|
<q-badge rounded size='md' :color='state.color'>{{ state.label }}</q-badge>
|
||||||
|
</div>
|
||||||
|
<div class='col-3 text-subtitle1 text-bold'>
|
||||||
|
{{ (stats?.counts && stats.counts[state.value]) ?? "-" }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app.component('lnbits-stat', {
|
||||||
|
props: ['title', 'amount', 'msat', 'btc'],
|
||||||
|
computed: {
|
||||||
|
value() {
|
||||||
|
return (
|
||||||
|
this.amount ??
|
||||||
|
(this.btc
|
||||||
|
? LNbits.utils.formatSat(this.btc)
|
||||||
|
: LNbits.utils.formatMsat(this.msat))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div class='text-overline text-primary'>
|
||||||
|
{{ title }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class='text-h4 text-bold q-my-none'>{{ value }}</span>
|
||||||
|
<span class='text-h5' v-if='msat != undefined'>sats</span>
|
||||||
|
<span class='text-h5' v-if='btc != undefined'>BTC</span>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app.component('lnbits-node-qrcode', {
|
||||||
|
props: ['info'],
|
||||||
|
mixins: [window.windowMixin],
|
||||||
|
template: `
|
||||||
|
<q-card class="my-card">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">
|
||||||
|
<div style="text-align: center">
|
||||||
|
<vue-qrcode
|
||||||
|
:value="info.addresses[0]"
|
||||||
|
:options="{width: 250}"
|
||||||
|
v-if='info.addresses[0]'
|
||||||
|
class="rounded-borders"
|
||||||
|
></vue-qrcode>
|
||||||
|
<div v-else class='text-subtitle1'>
|
||||||
|
No addresses available
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-actions vertical>
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
unelevated
|
||||||
|
size="md"
|
||||||
|
@click="copyText(info.id)"
|
||||||
|
>Public Key<q-tooltip> Click to copy </q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app.component('lnbits-channel-balance', {
|
||||||
|
props: ['balance', 'color'],
|
||||||
|
methods: {
|
||||||
|
formatMsat(msat) {
|
||||||
|
return LNbits.utils.formatMsat(msat)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<div class="row items-center justify-between">
|
||||||
|
<span class="text-weight-thin">
|
||||||
|
Local: {{ formatMsat(balance.local_msat) }}
|
||||||
|
sats
|
||||||
|
</span>
|
||||||
|
<span class="text-weight-thin">
|
||||||
|
Remote: {{ formatMsat(balance.remote_msat) }}
|
||||||
|
sats
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-linear-progress
|
||||||
|
rounded
|
||||||
|
size="25px"
|
||||||
|
:value="balance.local_msat / balance.total_msat"
|
||||||
|
:color="color"
|
||||||
|
:style="\`color: #\${this.color}\`"
|
||||||
|
>
|
||||||
|
<div class="absolute-full flex flex-center">
|
||||||
|
<q-badge
|
||||||
|
color="white"
|
||||||
|
text-color="accent"
|
||||||
|
:label="formatMsat(balance.total_msat) + ' sats'"
|
||||||
|
>
|
||||||
|
{{ balance.alias }}
|
||||||
|
</q-badge>
|
||||||
|
</div>
|
||||||
|
</q-linear-progress>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app.component('lnbits-date', {
|
||||||
|
props: ['ts'],
|
||||||
|
computed: {
|
||||||
|
date() {
|
||||||
|
return LNbits.utils.formatDate(this.ts)
|
||||||
|
},
|
||||||
|
dateFrom() {
|
||||||
|
return moment.utc(this.date).local().fromNow()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<q-tooltip>{{ this.date }}</q-tooltip>
|
||||||
|
{{ this.dateFrom }}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app.component('lnbits-node-info', {
|
||||||
|
props: ['info'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showDialog: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mixins: [window.windowMixin],
|
||||||
|
methods: {
|
||||||
|
shortenNodeId(nodeId) {
|
||||||
|
return nodeId
|
||||||
|
? nodeId.substring(0, 5) + '...' + nodeId.substring(nodeId.length - 5)
|
||||||
|
: '...'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class='row items-baseline q-gutter-x-sm'>
|
||||||
|
<div class='text-h4 text-bold'>{{ this.info.alias }}</div>
|
||||||
|
<div class='row items-center q-gutter-sm'>
|
||||||
|
<div class='text-subtitle1 text-light'>{{ this.info.backend_name }}</div>
|
||||||
|
<q-badge
|
||||||
|
:style='\`background-color: #\${this.info.color}\`'
|
||||||
|
class='text-bold'
|
||||||
|
>
|
||||||
|
#{{ this.info.color }}
|
||||||
|
</q-badge>
|
||||||
|
<div class='text-bold'>{{ shortenNodeId(this.info.id) }}</div>
|
||||||
|
<q-btn
|
||||||
|
size='xs'
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon='content_paste'
|
||||||
|
@click='copyText(info.id)'
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
size='xs'
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon='qr_code'
|
||||||
|
@click='showDialog = true'
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<q-dialog v-model="showDialog">
|
||||||
|
<lnbits-node-qrcode :info='info'></lnbits-node-qrcode>
|
||||||
|
</q-dialog>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app.component('lnbits-stat', {
|
||||||
|
props: ['title', 'amount', 'msat', 'btc'],
|
||||||
|
computed: {
|
||||||
|
value() {
|
||||||
|
return (
|
||||||
|
this.amount ??
|
||||||
|
(this.btc
|
||||||
|
? LNbits.utils.formatSat(this.btc)
|
||||||
|
: LNbits.utils.formatMsat(this.msat))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div class='text-overline text-primary'>
|
||||||
|
{{ title }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class='text-h4 text-bold q-my-none'>{{ value }}</span>
|
||||||
|
<span class='text-h5' v-if='msat != undefined'>sats</span>
|
||||||
|
<span class='text-h5' v-if='btc != undefined'>BTC</span>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -205,16 +205,12 @@ const routes = [
|
||||||
{
|
{
|
||||||
path: '/node',
|
path: '/node',
|
||||||
name: 'Node',
|
name: 'Node',
|
||||||
component: DynamicComponent,
|
component: PageNode
|
||||||
props: {
|
|
||||||
fetchUrl: '/node',
|
|
||||||
scripts: ['/static/js/node.js']
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/node',
|
path: '/node/public',
|
||||||
name: 'Node',
|
name: 'NodePublic',
|
||||||
component: DynamicComponent
|
component: PageNodePublic
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/payments',
|
path: '/payments',
|
||||||
|
|
|
||||||
74
lnbits/static/js/pages/node-public.js
Normal file
74
lnbits/static/js/pages/node-public.js
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
window.PageNodePublic = {
|
||||||
|
template: '#page-node-public',
|
||||||
|
mixins: [window.windowMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
enabled: false,
|
||||||
|
isSuperUser: false,
|
||||||
|
wallet: {},
|
||||||
|
tab: 'dashboard',
|
||||||
|
payments: 1000,
|
||||||
|
info: {},
|
||||||
|
channel_stats: {},
|
||||||
|
channels: [],
|
||||||
|
activeBalance: {},
|
||||||
|
ranks: {},
|
||||||
|
peers: [],
|
||||||
|
connectPeerDialog: {
|
||||||
|
show: false,
|
||||||
|
data: {}
|
||||||
|
},
|
||||||
|
openChannelDialog: {
|
||||||
|
show: false,
|
||||||
|
data: {}
|
||||||
|
},
|
||||||
|
closeChannelDialog: {
|
||||||
|
show: false,
|
||||||
|
data: {}
|
||||||
|
},
|
||||||
|
nodeInfoDialog: {
|
||||||
|
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'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getInfo()
|
||||||
|
this.get1MLStats()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatMsat(msat) {
|
||||||
|
return LNbits.utils.formatMsat(msat)
|
||||||
|
},
|
||||||
|
api(method, url, data) {
|
||||||
|
return LNbits.api.request(method, '/node/public/api/v1' + url, {}, data)
|
||||||
|
},
|
||||||
|
getInfo() {
|
||||||
|
this.api('GET', '/info', {})
|
||||||
|
.then(response => {
|
||||||
|
this.info = response.data
|
||||||
|
this.channel_stats = response.data.channel_stats
|
||||||
|
this.enabled = true
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.info = {}
|
||||||
|
this.channel_stats = {}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
get1MLStats() {
|
||||||
|
this.api('GET', '/rank', {})
|
||||||
|
.then(response => {
|
||||||
|
this.ranks = response.data
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.ranks = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
window.NodePageLogic = {
|
window.PageNode = {
|
||||||
mixins: [window.windowMixin],
|
mixins: [window.windowMixin],
|
||||||
|
template: '#page-node',
|
||||||
config: {
|
config: {
|
||||||
globalProperties: {
|
globalProperties: {
|
||||||
LNbits,
|
LNbits,
|
||||||
|
|
@ -225,15 +226,24 @@ window.NodePageLogic = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getInfo() {
|
getInfo() {
|
||||||
return this.api('GET', '/info').then(response => {
|
return this.api('GET', '/info')
|
||||||
|
.then(response => {
|
||||||
this.info = response.data
|
this.info = response.data
|
||||||
this.channel_stats = response.data.channel_stats
|
this.channel_stats = response.data.channel_stats
|
||||||
})
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.info = {}
|
||||||
|
this.channel_stats = {}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
get1MLStats() {
|
get1MLStats() {
|
||||||
return this.api('GET', '/rank').then(response => {
|
return this.api('GET', '/rank')
|
||||||
|
.then(response => {
|
||||||
this.ranks = response.data
|
this.ranks = response.data
|
||||||
})
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.ranks = {}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
getPayments(props) {
|
getPayments(props) {
|
||||||
if (props) {
|
if (props) {
|
||||||
|
|
@ -266,7 +276,6 @@ window.NodePageLogic = {
|
||||||
getPeers() {
|
getPeers() {
|
||||||
return this.api('GET', '/peers').then(response => {
|
return this.api('GET', '/peers').then(response => {
|
||||||
this.peers.data = response.data
|
this.peers.data = response.data
|
||||||
console.log('peers', this.peers)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
connectPeer() {
|
connectPeer() {
|
||||||
|
|
@ -343,7 +352,6 @@ window.NodePageLogic = {
|
||||||
showTransactionDetailsDialog(details) {
|
showTransactionDetailsDialog(details) {
|
||||||
this.transactionDetailsDialog.show = true
|
this.transactionDetailsDialog.show = true
|
||||||
this.transactionDetailsDialog.data = details
|
this.transactionDetailsDialog.data = details
|
||||||
console.log('details', details)
|
|
||||||
},
|
},
|
||||||
shortenNodeId(nodeId) {
|
shortenNodeId(nodeId) {
|
||||||
return nodeId
|
return nodeId
|
||||||
|
|
@ -352,261 +360,3 @@ window.NodePageLogic = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.app.component('lnbits-node-ranks', {
|
|
||||||
props: ['ranks'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
stats: [
|
|
||||||
{label: 'Capacity', key: 'capacity'},
|
|
||||||
{label: 'Channels', key: 'channelcount'},
|
|
||||||
{label: 'Age', key: 'age'},
|
|
||||||
{label: 'Growth', key: 'growth'},
|
|
||||||
{label: 'Availability', key: 'availability'}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<q-card class='q-my-none'>
|
|
||||||
<div class='column q-ma-md'>
|
|
||||||
<h5 class='text-subtitle1 text-bold q-my-none'>1ml Node Rank</h5>
|
|
||||||
<div v-for='stat in stats' class='q-gutter-sm'>
|
|
||||||
<div class='row items-center'>
|
|
||||||
<div class='col-9'>{{ stat.label }}</div>
|
|
||||||
<div class='col-3 text-subtitle1 text-bold'>
|
|
||||||
{{ (ranks && ranks[stat.key]) ?? '-' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
|
|
||||||
window.app.component('lnbits-channel-stats', {
|
|
||||||
props: ['stats'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
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'}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<q-card>
|
|
||||||
<div class='column q-ma-md'>
|
|
||||||
<h5 class='text-subtitle1 text-bold q-my-none'>Channels</h5>
|
|
||||||
<div v-for='state in states' class='q-gutter-sm'>
|
|
||||||
<div class='row'>
|
|
||||||
<div class='col-9'>
|
|
||||||
<q-badge rounded size='md' :color='state.color'>{{ state.label }}</q-badge>
|
|
||||||
</div>
|
|
||||||
<div class='col-3 text-subtitle1 text-bold'>
|
|
||||||
{{ (stats?.counts && stats.counts[state.value]) ?? "-" }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
|
|
||||||
window.app.component('lnbits-stat', {
|
|
||||||
props: ['title', 'amount', 'msat', 'btc'],
|
|
||||||
computed: {
|
|
||||||
value() {
|
|
||||||
return (
|
|
||||||
this.amount ??
|
|
||||||
(this.btc
|
|
||||||
? LNbits.utils.formatSat(this.btc)
|
|
||||||
: LNbits.utils.formatMsat(this.msat))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class='text-overline text-primary'>
|
|
||||||
{{ title }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class='text-h4 text-bold q-my-none'>{{ value }}</span>
|
|
||||||
<span class='text-h5' v-if='msat != undefined'>sats</span>
|
|
||||||
<span class='text-h5' v-if='btc != undefined'>BTC</span>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
|
|
||||||
window.app.component('lnbits-node-qrcode', {
|
|
||||||
props: ['info'],
|
|
||||||
mixins: [window.windowMixin],
|
|
||||||
template: `
|
|
||||||
<q-card class="my-card">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">
|
|
||||||
<div style="text-align: center">
|
|
||||||
<vue-qrcode
|
|
||||||
:value="info.addresses[0]"
|
|
||||||
:options="{width: 250}"
|
|
||||||
v-if='info.addresses[0]'
|
|
||||||
class="rounded-borders"
|
|
||||||
></vue-qrcode>
|
|
||||||
<div v-else class='text-subtitle1'>
|
|
||||||
No addresses available
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-actions vertical>
|
|
||||||
<q-btn
|
|
||||||
dense
|
|
||||||
unelevated
|
|
||||||
size="md"
|
|
||||||
@click="copyText(info.id)"
|
|
||||||
>Public Key<q-tooltip> Click to copy </q-tooltip>
|
|
||||||
</q-btn>
|
|
||||||
</q-card-actions>
|
|
||||||
</q-card>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
|
|
||||||
window.app.component('lnbits-node-info', {
|
|
||||||
props: ['info'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showDialog: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [window.windowMixin],
|
|
||||||
methods: {
|
|
||||||
shortenNodeId(nodeId) {
|
|
||||||
return nodeId
|
|
||||||
? nodeId.substring(0, 5) + '...' + nodeId.substring(nodeId.length - 5)
|
|
||||||
: '...'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<div class='row items-baseline q-gutter-x-sm'>
|
|
||||||
<div class='text-h4 text-bold'>{{ this.info.alias }}</div>
|
|
||||||
<div class='row items-center q-gutter-sm'>
|
|
||||||
<div class='text-subtitle1 text-light'>{{ this.info.backend_name }}</div>
|
|
||||||
<q-badge
|
|
||||||
:style='\`background-color: #\${this.info.color}\`'
|
|
||||||
class='text-bold'
|
|
||||||
>
|
|
||||||
#{{ this.info.color }}
|
|
||||||
</q-badge>
|
|
||||||
<div class='text-bold'>{{ shortenNodeId(this.info.id) }}</div>
|
|
||||||
<q-btn
|
|
||||||
size='xs'
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon='content_paste'
|
|
||||||
@click='copyText(info.id)'
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
size='xs'
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon='qr_code'
|
|
||||||
@click='showDialog = true'
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
<q-dialog v-model="showDialog">
|
|
||||||
<lnbits-node-qrcode :info='info'></lnbits-node-qrcode>
|
|
||||||
</q-dialog>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
|
|
||||||
window.app.component('lnbits-stat', {
|
|
||||||
props: ['title', 'amount', 'msat', 'btc'],
|
|
||||||
computed: {
|
|
||||||
value() {
|
|
||||||
return (
|
|
||||||
this.amount ??
|
|
||||||
(this.btc
|
|
||||||
? LNbits.utils.formatSat(this.btc)
|
|
||||||
: LNbits.utils.formatMsat(this.msat))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class='text-overline text-primary'>
|
|
||||||
{{ title }}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class='text-h4 text-bold q-my-none'>{{ value }}</span>
|
|
||||||
<span class='text-h5' v-if='msat != undefined'>sats</span>
|
|
||||||
<span class='text-h5' v-if='btc != undefined'>BTC</span>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
|
|
||||||
window.app.component('lnbits-channel-balance', {
|
|
||||||
props: ['balance', 'color'],
|
|
||||||
methods: {
|
|
||||||
formatMsat(msat) {
|
|
||||||
return LNbits.utils.formatMsat(msat)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<div>
|
|
||||||
<div class="row items-center justify-between">
|
|
||||||
<span class="text-weight-thin">
|
|
||||||
Local: {{ formatMsat(balance.local_msat) }}
|
|
||||||
sats
|
|
||||||
</span>
|
|
||||||
<span class="text-weight-thin">
|
|
||||||
Remote: {{ formatMsat(balance.remote_msat) }}
|
|
||||||
sats
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<q-linear-progress
|
|
||||||
rounded
|
|
||||||
size="25px"
|
|
||||||
:value="balance.local_msat / balance.total_msat"
|
|
||||||
:color="color"
|
|
||||||
:style="\`color: #\${this.color}\`"
|
|
||||||
>
|
|
||||||
<div class="absolute-full flex flex-center">
|
|
||||||
<q-badge
|
|
||||||
color="white"
|
|
||||||
text-color="accent"
|
|
||||||
:label="formatMsat(balance.total_msat) + ' sats'"
|
|
||||||
>
|
|
||||||
{{ balance.alias }}
|
|
||||||
</q-badge>
|
|
||||||
</div>
|
|
||||||
</q-linear-progress>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
|
|
||||||
window.app.component('lnbits-date', {
|
|
||||||
props: ['ts'],
|
|
||||||
computed: {
|
|
||||||
date() {
|
|
||||||
return LNbits.utils.formatDate(this.ts)
|
|
||||||
},
|
|
||||||
dateFrom() {
|
|
||||||
return moment.utc(this.date).local().fromNow()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<div>
|
|
||||||
<q-tooltip>{{ this.date }}</q-tooltip>
|
|
||||||
{{ this.dateFrom }}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
|
|
@ -44,6 +44,8 @@
|
||||||
],
|
],
|
||||||
"components": [
|
"components": [
|
||||||
"js/pages/payments.js",
|
"js/pages/payments.js",
|
||||||
|
"js/pages/node.js",
|
||||||
|
"js/pages/node-public.js",
|
||||||
"js/components/lnbits-qrcode.js",
|
"js/components/lnbits-qrcode.js",
|
||||||
"js/components/lnbits-qrcode-lnurl.js",
|
"js/components/lnbits-qrcode-lnurl.js",
|
||||||
"js/components/lnbits-funding-sources.js",
|
"js/components/lnbits-funding-sources.js",
|
||||||
|
|
|
||||||
|
|
@ -483,7 +483,7 @@
|
||||||
window[key] = WINDOW_SETTINGS[key]
|
window[key] = WINDOW_SETTINGS[key]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% include('components.vue') %} {% include('pages/payments.vue') %} {% block
|
{% include('components.vue') %} {% include('pages.vue') %} {% block
|
||||||
vue_templates %}{% endblock %} {% for url in INCLUDED_JS %}
|
vue_templates %}{% endblock %} {% for url in INCLUDED_JS %}
|
||||||
<script src="{{ static_url_for('static', url) }}"></script>
|
<script src="{{ static_url_for('static', url) }}"></script>
|
||||||
{% endfor %} {% block scripts %}{% endblock %} {% for url in
|
{% endfor %} {% block scripts %}{% endblock %} {% for url in
|
||||||
|
|
|
||||||
1
lnbits/templates/pages.vue
Normal file
1
lnbits/templates/pages.vue
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{% include('pages/payments.vue') %} {% include('pages/node.vue') %}
|
||||||
968
lnbits/templates/pages/node.vue
Normal file
968
lnbits/templates/pages/node.vue
Normal file
|
|
@ -0,0 +1,968 @@
|
||||||
|
<template id="page-node">
|
||||||
|
<q-dialog v-model="nodeInfoDialog.show" position="top">
|
||||||
|
<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>
|
||||||
|
<q-tab-panel name="dashboard">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<lnbits-node-info :info="this.info"></lnbits-node-info>
|
||||||
|
<div class="row q-col-gutter-lg q-mt-sm">
|
||||||
|
<div class="col-12 col-md-8 q-gutter-y-md">
|
||||||
|
<div class="row q-col-gutter-md q-pb-lg">
|
||||||
|
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
:title="$t('total_capacity')"
|
||||||
|
:msat="this.channel_stats.total_capacity"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
title="Balance"
|
||||||
|
:msat="this.info.balance_msat"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
title="Fees collected"
|
||||||
|
:msat="this.info.fees?.total_msat"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
title="Onchain Balance"
|
||||||
|
:btc="this.info.onchain_balance_sat / 100000000"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
title="Onchain Confirmed"
|
||||||
|
:btc="this.info.onchain_confirmed_sat / 100000000"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
title="Peers"
|
||||||
|
:amount="this.info.num_peers"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
:title="$t('avg_channel_size')"
|
||||||
|
:msat="this.channel_stats.avg_size"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
:title="$t('biggest_channel_size')"
|
||||||
|
:msat="this.channel_stats.biggest_size"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-xl-4 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
:title="$t('smallest_channel_size')"
|
||||||
|
:msat="this.channel_stats.smallest_size"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-12 col-md-4 q-gutter-y-md">
|
||||||
|
<lnbits-node-ranks :ranks="this.ranks"></lnbits-node-ranks>
|
||||||
|
<lnbits-channel-stats
|
||||||
|
:stats="this.channel_stats"
|
||||||
|
></lnbits-channel-stats>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-tab-panel>
|
||||||
|
<q-tab-panel name="channels">
|
||||||
|
<q-dialog v-model="connectPeerDialog.show" position="top">
|
||||||
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
|
<q-form class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="text"
|
||||||
|
filled
|
||||||
|
v-model="connectPeerDialog.data.uri"
|
||||||
|
label="Node URI"
|
||||||
|
hint="pubkey@host:port"
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
:label="$t('connect')"
|
||||||
|
color="primary"
|
||||||
|
@click="connectPeer"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('cancel')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="setFeeDialog.show">
|
||||||
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
|
<label class="text-h6">Set Channel Fee</label>
|
||||||
|
<p class="text-caption" v-text="setFeeDialog.channel_id"></p>
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<q-form class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="number"
|
||||||
|
filled
|
||||||
|
v-model.number="setFeeDialog.data.fee_ppm"
|
||||||
|
label="Fee Rate PPM"
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="number"
|
||||||
|
filled
|
||||||
|
v-model.number="setFeeDialog.data.fee_base_msat"
|
||||||
|
label="Fee Base msat"
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
:label="$t('set')"
|
||||||
|
color="primary"
|
||||||
|
@click="setChannelFee(setFeeDialog.channel_id)"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('cancel')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="openChannelDialog.show">
|
||||||
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
|
<q-form class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="text"
|
||||||
|
filled
|
||||||
|
v-model="openChannelDialog.data.peer_id"
|
||||||
|
label="Peer ID"
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="number"
|
||||||
|
filled
|
||||||
|
v-model.number="openChannelDialog.data.funding_amount"
|
||||||
|
label="Funding Amount"
|
||||||
|
></q-input>
|
||||||
|
<q-expansion-item icon="warning" label="Advanced">
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="column q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="number"
|
||||||
|
filled
|
||||||
|
v-model.number="
|
||||||
|
openChannelDialog.data.push_amount
|
||||||
|
"
|
||||||
|
label="Push Amount"
|
||||||
|
hint="This gifts sats to the other side!"
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="number"
|
||||||
|
filled
|
||||||
|
v-model.number="openChannelDialog.data.fee_rate"
|
||||||
|
label="Fee Rate"
|
||||||
|
></q-input>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-expansion-item>
|
||||||
|
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
:label="$t('open')"
|
||||||
|
color="primary"
|
||||||
|
@click="openChannel"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('cancel')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="closeChannelDialog.show" position="top">
|
||||||
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
|
<q-form class="q-gutter-md">
|
||||||
|
<div>
|
||||||
|
<q-checkbox
|
||||||
|
v-model="closeChannelDialog.data.force"
|
||||||
|
label="Force"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
:label="$t('close')"
|
||||||
|
color="primary"
|
||||||
|
@click="closeChannel"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('cancel')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<div class="row q-col-gutter-lg">
|
||||||
|
<div class="col-12 col-xl-6">
|
||||||
|
<q-card class="full-height">
|
||||||
|
<q-card-section class="q-gutter-y-sm">
|
||||||
|
<div
|
||||||
|
class="row items-center q-mt-none q-gutter-x-sm no-wrap"
|
||||||
|
>
|
||||||
|
<div class="col-grow text-h6 q-my-none col-grow">
|
||||||
|
Channels
|
||||||
|
</div>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
|
v-model="channels.filter"
|
||||||
|
placeholder="Search..."
|
||||||
|
class="col-auto"
|
||||||
|
></q-input>
|
||||||
|
<q-select
|
||||||
|
dense
|
||||||
|
size="sm"
|
||||||
|
style="min-width: 200px"
|
||||||
|
filled
|
||||||
|
multiple
|
||||||
|
clearable
|
||||||
|
v-model="stateFilters"
|
||||||
|
:options="this.states"
|
||||||
|
class="col-auto"
|
||||||
|
></q-select>
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
size="md"
|
||||||
|
class="col-auto"
|
||||||
|
@click="showOpenChannelDialog()"
|
||||||
|
>
|
||||||
|
Open channel
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-subtitle1 col-grow">Total</div>
|
||||||
|
<lnbits-channel-balance
|
||||||
|
:balance="this.totalBalance"
|
||||||
|
></lnbits-channel-balance>
|
||||||
|
</div>
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<q-table
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
:rows="this.filteredChannels"
|
||||||
|
:filter="channels.filter"
|
||||||
|
no-data-label="No channels opened"
|
||||||
|
>
|
||||||
|
<template v-slot:header="props">
|
||||||
|
<q-tr :props="props" style="height: 0"> </q-tr>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body="props">
|
||||||
|
<q-tr :props="props">
|
||||||
|
<div class="q-pb-sm">
|
||||||
|
<div class="row items-center q-gutter-sm">
|
||||||
|
<div
|
||||||
|
class="text-subtitle1"
|
||||||
|
v-text="props.row.name"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="text-caption"
|
||||||
|
v-if="props.row.peer_id"
|
||||||
|
>
|
||||||
|
<span>Peer ID</span>
|
||||||
|
<q-btn
|
||||||
|
size="xs"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="content_paste"
|
||||||
|
@click="copyText(props.row.peer_id)"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<div class="text-caption col-grow">
|
||||||
|
<span>Fees</span>
|
||||||
|
<q-btn
|
||||||
|
size="xs"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="settings"
|
||||||
|
@click="showSetFeeDialog(props.row.id)"
|
||||||
|
></q-btn>
|
||||||
|
<span v-if="props.row.fee_ppm">
|
||||||
|
<span v-text="props.row.fee_ppm"></span>
|
||||||
|
ppm,
|
||||||
|
<span
|
||||||
|
v-text="props.row.fee_base_msat"
|
||||||
|
></span>
|
||||||
|
msat
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-caption" v-if="props.row.id">
|
||||||
|
<span>Channel ID</span>
|
||||||
|
<q-btn
|
||||||
|
size="xs"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="content_paste"
|
||||||
|
@click="copyText(props.row.id)"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-caption"
|
||||||
|
v-if="props.row.short_id"
|
||||||
|
>
|
||||||
|
<span v-text="props.row.short_id"></span>
|
||||||
|
<q-btn
|
||||||
|
size="xs"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="content_paste"
|
||||||
|
@click="copyText(props.row.short_id)"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<q-badge
|
||||||
|
rounded
|
||||||
|
:color="
|
||||||
|
states.find(
|
||||||
|
s => s.value == props.row.state
|
||||||
|
)?.color
|
||||||
|
"
|
||||||
|
v-text="
|
||||||
|
states.find(
|
||||||
|
s => s.value == props.row.state
|
||||||
|
)?.label
|
||||||
|
"
|
||||||
|
>
|
||||||
|
</q-badge>
|
||||||
|
<q-btn
|
||||||
|
:disable="props.row.state !== 'active'"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
size="md"
|
||||||
|
@click="showCloseChannelDialog(props.row)"
|
||||||
|
icon="cancel"
|
||||||
|
color="pink"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<lnbits-channel-balance
|
||||||
|
:balance="props.row.balance"
|
||||||
|
:color="props.row.color"
|
||||||
|
></lnbits-channel-balance>
|
||||||
|
</div>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-xl-6">
|
||||||
|
<q-card class="full-height">
|
||||||
|
<q-card-section class="column q-gutter-y-sm">
|
||||||
|
<div
|
||||||
|
class="row items-center q-mt-none justify-between q-gutter-x-md no-wrap"
|
||||||
|
>
|
||||||
|
<div class="col-grow text-h6 q-my-none">Peers</div>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
|
v-model="peers.filter"
|
||||||
|
placeholder="Search..."
|
||||||
|
class="col-auto"
|
||||||
|
></q-input>
|
||||||
|
<q-btn
|
||||||
|
class="col-auto"
|
||||||
|
color="primary"
|
||||||
|
@click="connectPeerDialog.show = true"
|
||||||
|
>
|
||||||
|
Connect Peer
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<q-table
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
:rows="peers.data"
|
||||||
|
:filter="peers.filter"
|
||||||
|
no-data-label="No transactions made yet"
|
||||||
|
>
|
||||||
|
<template v-slot:header="props">
|
||||||
|
<q-tr :props="props" style="height: 0"> </q-tr>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body="props">
|
||||||
|
<q-tr :props="props">
|
||||||
|
<div class="row no-wrap items-center q-gutter-sm">
|
||||||
|
<div class="q-my-sm col-grow">
|
||||||
|
<div
|
||||||
|
class="text-subtitle1 text-bold"
|
||||||
|
v-text="props.row.alias"
|
||||||
|
></div>
|
||||||
|
<div class="row items-center q-gutter-sm">
|
||||||
|
<q-badge
|
||||||
|
:style="`background-color: #${props.row.color}`"
|
||||||
|
class="text-bold"
|
||||||
|
v-text="'#' + props.row.color"
|
||||||
|
>
|
||||||
|
</q-badge>
|
||||||
|
<div
|
||||||
|
class="text-bold"
|
||||||
|
v-text="shortenNodeId(props.row.id)"
|
||||||
|
></div>
|
||||||
|
<q-btn
|
||||||
|
size="xs"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="content_paste"
|
||||||
|
@click="copyText(props.row.id)"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
size="xs"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="qr_code"
|
||||||
|
@click="showNodeInfoDialog(props.row)"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
@click="showOpenChannelDialog(props.row.id)"
|
||||||
|
>
|
||||||
|
Open channel
|
||||||
|
</q-btn>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
size="md"
|
||||||
|
@click="disconnectPeer(props.row.id)"
|
||||||
|
icon="cancel"
|
||||||
|
color="pink"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-tab-panel>
|
||||||
|
<q-tab-panel name="transactions">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<q-dialog
|
||||||
|
v-model="transactionDetailsDialog.show"
|
||||||
|
position="top"
|
||||||
|
>
|
||||||
|
<q-card class="my-card">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-center q-mb-lg">
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
transactionDetailsDialog.data.isIn &&
|
||||||
|
transactionDetailsDialog.data.pending
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
size="18px"
|
||||||
|
:name="'call_received'"
|
||||||
|
:color="'green'"
|
||||||
|
></q-icon>
|
||||||
|
<span v-text="$t('payment_received')"></span>
|
||||||
|
</div>
|
||||||
|
<div class="row q-my-md">
|
||||||
|
<div class="col-3">
|
||||||
|
<b v-text="$t('payment_hash')"></b>:
|
||||||
|
</div>
|
||||||
|
<div class="col-9 text-wrap mono">
|
||||||
|
<span
|
||||||
|
v-text="
|
||||||
|
transactionDetailsDialog.data.payment_hash
|
||||||
|
"
|
||||||
|
></span>
|
||||||
|
<q-icon
|
||||||
|
name="content_copy"
|
||||||
|
@click="
|
||||||
|
copyText(
|
||||||
|
transactionDetailsDialog.data.payment_hash
|
||||||
|
)
|
||||||
|
"
|
||||||
|
size="1em"
|
||||||
|
color="grey"
|
||||||
|
class="q-mb-xs cursor-pointer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="row"
|
||||||
|
v-if="
|
||||||
|
transactionDetailsDialog.data.preimage &&
|
||||||
|
!transactionDetailsDialog.data.pending
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="col-3">
|
||||||
|
<b v-text="$t('payment_proof')"></b>:
|
||||||
|
</div>
|
||||||
|
<div class="col-9 text-wrap mono">
|
||||||
|
<span
|
||||||
|
v-text="transactionDetailsDialog.data.preimage"
|
||||||
|
></span>
|
||||||
|
<q-icon
|
||||||
|
name="content_copy"
|
||||||
|
@click="
|
||||||
|
copyText(
|
||||||
|
transactionDetailsDialog.data.preimage
|
||||||
|
)
|
||||||
|
"
|
||||||
|
size="1em"
|
||||||
|
color="grey"
|
||||||
|
class="q-mb-xs cursor-pointer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="transactionDetailsDialog.data.bolt11"
|
||||||
|
class="text-center q-mb-lg"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
:href="
|
||||||
|
'lightning:' +
|
||||||
|
transactionDetailsDialog.data.bolt11
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-responsive :ratio="1" class="q-mx-xl">
|
||||||
|
<qrcode-vue
|
||||||
|
:value="
|
||||||
|
'lightning:' +
|
||||||
|
transactionDetailsDialog.data.bolt11.toUpperCase()
|
||||||
|
"
|
||||||
|
:options="{width: 340}"
|
||||||
|
class="rounded-borders"
|
||||||
|
></qrcode-vue>
|
||||||
|
</q-responsive>
|
||||||
|
</a>
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
color="grey"
|
||||||
|
@click="
|
||||||
|
copyText(transactionDetailsDialog.data.bolt11)
|
||||||
|
"
|
||||||
|
:label="$t('copy_invoice')"
|
||||||
|
class="q-mt-sm"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<div class="row q-col-gutter-md q-pb-lg"></div>
|
||||||
|
|
||||||
|
<div class="row q-col-gutter-lg">
|
||||||
|
<div class="col-12 col-lg-6 q-gutter-y-md">
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row items-center no-wrap q-mb-sm">
|
||||||
|
<div class="col text-h6 q-my-none">Payments</div>
|
||||||
|
<q-input
|
||||||
|
v-if="payments.length > 10"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
|
v-model="paymentsTable.filter"
|
||||||
|
debounce="300"
|
||||||
|
placeholder="Search by tag, memo, amount"
|
||||||
|
class="q-mb-md"
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
<q-table
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
:rows="paymentsTable.data"
|
||||||
|
:columns="paymentsTable.columns"
|
||||||
|
v-model:pagination="paymentsTable.pagination"
|
||||||
|
row-key="payment_hash"
|
||||||
|
no-data-label="No transactions made yet"
|
||||||
|
:filter="paymentsTable.filter"
|
||||||
|
@request="getPayments"
|
||||||
|
>
|
||||||
|
<template v-slot:body-cell-pending="props">
|
||||||
|
<q-td auto-width class="text-center">
|
||||||
|
<q-icon
|
||||||
|
v-if="!props.row.pending"
|
||||||
|
size="xs"
|
||||||
|
name="call_made"
|
||||||
|
color="green"
|
||||||
|
@click="showTransactionDetailsDialog(props.row)"
|
||||||
|
></q-icon>
|
||||||
|
<q-icon
|
||||||
|
v-else
|
||||||
|
size="xs"
|
||||||
|
name="settings_ethernet"
|
||||||
|
color="grey"
|
||||||
|
@click="showTransactionDetailsDialog(props.row)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Pending</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
<q-dialog
|
||||||
|
v-model="props.row.expand"
|
||||||
|
:props="props"
|
||||||
|
position="top"
|
||||||
|
>
|
||||||
|
<q-card
|
||||||
|
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
||||||
|
>
|
||||||
|
<div class="text-center q-mb-lg">
|
||||||
|
<div
|
||||||
|
v-if="props.row.isIn && props.row.pending"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
name="settings_ethernet"
|
||||||
|
color="grey"
|
||||||
|
></q-icon>
|
||||||
|
<span
|
||||||
|
v-text="$t('invoice_waiting')"
|
||||||
|
></span>
|
||||||
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
|
<div
|
||||||
|
v-if="props.row.bolt11"
|
||||||
|
class="text-center q-mb-lg"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
:href="
|
||||||
|
'lightning:' + props.row.bolt11
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-responsive
|
||||||
|
:ratio="1"
|
||||||
|
class="q-mx-xl"
|
||||||
|
>
|
||||||
|
<qrcode-vue
|
||||||
|
:value="
|
||||||
|
'lightning:' +
|
||||||
|
props.row.bolt11.toUpperCase()
|
||||||
|
"
|
||||||
|
:options="{width: 340}"
|
||||||
|
class="rounded-borders"
|
||||||
|
></qrcode-vue>
|
||||||
|
</q-responsive>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
color="grey"
|
||||||
|
@click="copyText(props.row.bolt11)"
|
||||||
|
:label="$t('copy_invoice')"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('close')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="
|
||||||
|
props.row.isPaid && props.row.isIn
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
size="18px"
|
||||||
|
:name="'call_received'"
|
||||||
|
:color="'green'"
|
||||||
|
></q-icon>
|
||||||
|
<span
|
||||||
|
v-text="$t('payment_received')"
|
||||||
|
></span>
|
||||||
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="
|
||||||
|
props.row.isPaid && props.row.isOut
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
size="18px"
|
||||||
|
:name="'call_made'"
|
||||||
|
:color="'pink'"
|
||||||
|
></q-icon>
|
||||||
|
<span v-text="$t('payment_sent')"></span>
|
||||||
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="
|
||||||
|
props.row.isOut && props.row.pending
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
name="settings_ethernet"
|
||||||
|
color="grey"
|
||||||
|
></q-icon>
|
||||||
|
<span
|
||||||
|
v-text="$t('outgoing_payment_pending')"
|
||||||
|
></span>
|
||||||
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body-cell-date="props">
|
||||||
|
<q-td auto-width key="date" :props="props">
|
||||||
|
<lnbits-date :ts="props.row.time"></lnbits-date>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body-cell-destination="props">
|
||||||
|
<q-td auto-width key="destination">
|
||||||
|
<div
|
||||||
|
class="row items-center justify-between no-wrap"
|
||||||
|
>
|
||||||
|
<q-badge
|
||||||
|
:style="`background-color: #${props.row.destination?.color}`"
|
||||||
|
class="text-bold"
|
||||||
|
v-text="props.row.destination?.alias"
|
||||||
|
></q-badge>
|
||||||
|
<div>
|
||||||
|
<q-btn
|
||||||
|
size="xs"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="content_paste"
|
||||||
|
@click="copyText(info.id)"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
size="xs"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="qr_code"
|
||||||
|
@click="
|
||||||
|
showNodeInfoDialog(props.row.destination)
|
||||||
|
"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-6 q-gutter-y-md">
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row items-center no-wrap q-mb-sm">
|
||||||
|
<div class="col text-h6 q-my-none">Invoices</div>
|
||||||
|
<q-input
|
||||||
|
v-if="payments.length > 10"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
|
v-model="paymentsTable.filter"
|
||||||
|
debounce="300"
|
||||||
|
placeholder="Search by tag, memo, amount"
|
||||||
|
class="q-mb-md"
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-table
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
:rows="invoiceTable.data"
|
||||||
|
:columns="invoiceTable.columns"
|
||||||
|
v-model:pagination="invoiceTable.pagination"
|
||||||
|
no-data-label="No transactions made yet"
|
||||||
|
:filter="invoiceTable.filter"
|
||||||
|
@request="getInvoices"
|
||||||
|
>
|
||||||
|
<template v-slot:body-cell-pending="props">
|
||||||
|
<q-td auto-width class="text-center">
|
||||||
|
<q-icon
|
||||||
|
v-if="!props.row.pending"
|
||||||
|
size="xs"
|
||||||
|
name="call_received"
|
||||||
|
color="green"
|
||||||
|
@click="showTransactionDetailsDialog(props.row)"
|
||||||
|
></q-icon>
|
||||||
|
<q-icon
|
||||||
|
v-else
|
||||||
|
size="xs"
|
||||||
|
name="settings_ethernet"
|
||||||
|
color="grey"
|
||||||
|
@click="showTransactionDetailsDialog(props.row)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Pending</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:body-cell-paid_at="props">
|
||||||
|
<q-td auto-width :props="props">
|
||||||
|
<lnbits-date
|
||||||
|
v-if="props.row.paid_at"
|
||||||
|
:ts="props.row.paid_at"
|
||||||
|
></lnbits-date>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:body-cell-expiry="props">
|
||||||
|
<q-td auto-width :props="props">
|
||||||
|
<lnbits-date
|
||||||
|
v-if="props.row.expiry"
|
||||||
|
:ts="props.row.expiry"
|
||||||
|
></lnbits-date>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-tab-panel>
|
||||||
|
</q-tab-panels>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="page-node-public">
|
||||||
|
<div
|
||||||
|
v-if="!enabled"
|
||||||
|
class="q-ma-lg-xl q-mx-auto q-ma-xl"
|
||||||
|
style="max-width: 1048px"
|
||||||
|
>
|
||||||
|
<h2>Node public page not enabled.</h2>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="enabled"
|
||||||
|
class="q-ma-lg-xl q-mx-auto q-ma-xl"
|
||||||
|
style="max-width: 1048px"
|
||||||
|
>
|
||||||
|
<lnbits-node-info :info="this.info"></lnbits-node-info>
|
||||||
|
|
||||||
|
<div class="row q-col-gutter-lg q-mt-sm">
|
||||||
|
<div class="col-12 col-md-8 q-gutter-y-md">
|
||||||
|
<div class="row q-col-gutter-md q-pb-lg">
|
||||||
|
<div class="col-12 col-md-6 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
:title="$t('total_capacity')"
|
||||||
|
:msat="this.channel_stats.total_capacity"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6 q-gutter-y-md">
|
||||||
|
<lnbits-stat title="Peers" :amount="this.info.num_peers" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
:title="$t('avg_channel_size')"
|
||||||
|
:msat="this.channel_stats.avg_size"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
:title="$t('biggest_channel_size')"
|
||||||
|
:msat="this.channel_stats.biggest_size"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
:title="$t('smallest_channel_size')"
|
||||||
|
:msat="this.channel_stats.smallest_size"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 q-gutter-y-md">
|
||||||
|
<lnbits-stat
|
||||||
|
:title="$t('smallest_channel_size')"
|
||||||
|
:msat="this.channel_stats.smallest_size"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-12 col-md-4 q-gutter-y-md">
|
||||||
|
<lnbits-node-ranks :ranks="this.ranks"></lnbits-node-ranks>
|
||||||
|
<lnbits-channel-stats
|
||||||
|
:stats="this.channel_stats"
|
||||||
|
></lnbits-channel-stats>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -96,6 +96,8 @@
|
||||||
],
|
],
|
||||||
"components": [
|
"components": [
|
||||||
"js/pages/payments.js",
|
"js/pages/payments.js",
|
||||||
|
"js/pages/node.js",
|
||||||
|
"js/pages/node-public.js",
|
||||||
"js/components/lnbits-qrcode.js",
|
"js/components/lnbits-qrcode.js",
|
||||||
"js/components/lnbits-qrcode-lnurl.js",
|
"js/components/lnbits-qrcode-lnurl.js",
|
||||||
"js/components/lnbits-funding-sources.js",
|
"js/components/lnbits-funding-sources.js",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue