Load more for pay/send

This commit is contained in:
cryptosharks131 2023-08-15 16:14:44 -04:00
parent ef765af5fe
commit 94de58a7bb
No known key found for this signature in database
GPG key ID: 0A50748567ADEB28
5 changed files with 175 additions and 85 deletions

View file

@ -5,12 +5,14 @@ from .models import LocalSettings, Payments, PaymentHops, Invoices, Forwards, Ch
##FUTURE UPDATE 'exclude' TO 'fields'
class PaymentSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.IntegerField(source='index')
payment_hash = serializers.ReadOnlyField()
class Meta:
model = Payments
exclude = []
class InvoiceSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.IntegerField(source='index')
r_hash = serializers.ReadOnlyField()
creation_date = serializers.ReadOnlyField()
settle_date = serializers.ReadOnlyField()

View file

@ -30,6 +30,32 @@
"fee": f => ({innerHTML: parseFloat(f.fee.toFixed(3)).toLocaleString()}),
"ppm": f => ({innerHTML: parseInt(f.ppm.toFixed(0)).toLocaleString()}),
}
let {fee, ppm} = routed_template
const payments_template = Object.assign({}, {
"creation_date": p => ({innerHTML: formatDate(p.creation_date), title: adjustTZ(p.creation_date) }),
"value": p => ({innerHTML: p.value.intcomma()}),
"fee": p => ({innerHTML: parseFloat(p.fee.toFixed(3)).toLocaleString()})
}, {ppm}, {
"status": p => ({innerHTML: ['Unknown', 'In-Flight', 'Succeeded', 'Failed'][p.status]}),
"chan_out_alias": p => ({innerHTML: p.chan_out_alias||'---'}),
"chan_out": p => ({innerHTML: p.status == 2 && p.chan_out !== 'MPP' ? `<a href="/channel?=${p.chan_out}" target="_blank">${(BigInt(p.chan_out)>>40n)+'x'+(BigInt(p.chan_out)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(p.chan_out) & BigInt('0xFFFF'))}` : p.chan_out || '---' }),
"payment_hash": p => ({innerHTML: `<a href="/route?=${p.payment_hash}" target="_blank">${p.payment_hash.substring(0,7)}</a>`}),
"rebal_chan": p => ({innerHTML: (p.rebal_chan || '').length > 0 ? 'Yes' : 'No'}),
"keysend_preimage": p => ({innerHTML: p.keysend_preimage ? 'Yes' : 'No'})
}
)
let {creation_date, value, keysend_preimage} = payments_template
const invoices_template = Object.assign({}, {creation_date}, {
"settle_date": i => ({innerHTML: formatDate(i.settle_date), title: adjustTZ(i.settle_date)})
}, {value}, {
"amt_paid": i => ({innerHTML: i.amt_paid.intcomma()}),
"state": i => ({innerHTML: ['Open', 'Settled', 'Cancelled', 'Accepted'][i.state]}),
"chan_in_alias": i => ({innerHTML: i.chan_in_alias||'---'}),
"chan_in": i => ({innerHTML: i.chan_in? `<a href="/channel?=${i.chan_in}" target="_blank">${(BigInt(i.chan_in)>>40n)+'x'+(BigInt(i.chan_in)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(i.chan_in) & BigInt('0xFFFF'))}</a>` : '---'}),
"r_hash": i => ({innerHTML: `<a href="/route?=${i.r_hash}" target="_blank">${i.r_hash.substring(0,7)}</a>`}),
"keysend_preimage": i => ({innerHTML: keysend_preimage(i).innerHTML, title: i.message }),
}
)
const wire_failures = "0|1|2|3|4|5|6|Expiry Too Soon|8|9|10|Amount Below Minimum|Fee Insufficient|13|14|Temporary Channel Failure|16|17|Unknown Next Peer|19|20|21|Expiry Too Far".split('|')
const failure_details = "0|---|2|3|4|HTLC Exceeds Max|Insufficient Balance|7|8|9|10|11|12|Invoice Not Open|14|15|16|17|18|19|Invalid Keysend|21|Circular Route".split('|')
const failedHTLCs_template = {

View file

@ -336,7 +336,7 @@
</tr>
<tfoot id="loadMoreForwards">
<tr style="background-color:transparent;cursor:pointer;">
<td colspan="9">
<td colspan="11">
<a onclick="loadForwards()">Load More</a>
</td>
</tr>
@ -346,43 +346,33 @@
<div id="rebalancerDiv">
{% include 'rebalances_table.html' with count=5 last_hop_pubkey=channel.remote_pubkey load_count=5 title='<a href="/rebalancing" target="_blank">Rebalance Requests</a>' %}
</div>
{% if payments %}
<div class="w3-container w3-padding-small">
<div id="paymentsDiv" class="w3-container w3-padding-small">
<h2>Last 5 <a href="/payments" target="_blank">Payments Sent</a></h2>
<table class="w3-table-all w3-centered w3-hoverable">
<table id="paymentsTable" class="w3-table-all w3-centered w3-hoverable">
<tr>
<th>Timestamp</th>
<th width=28%>Hash</th>
<th width=6%>Value</th>
<th width=6%>Fee Paid</th>
<th width=6%>PPM Paid</th>
<th width=6%>Status</th>
<th width=12%>Chan Out Alias</th>
<th width=12%>Chan Out ID</th>
<th width=4%>Route</th>
<th width=6%>Keysend</th>
<th>Value</th>
<th>Fee Paid</th>
<th>PPM Paid</th>
<th>Status</th>
<th>Chan Out Alias</th>
<th>Chan Out ID</th>
<th>Hash</th>
<th>Rebalance</th>
<th>Keysend</th>
</tr>
{% for payment in payments %}
<tr>
<td title="{{ payment.creation_date }}">{{ payment.creation_date|naturaltime }}</td>
<td><a href="/route?={{ payment.payment_hash }}" target="_blank">{{ payment.payment_hash }}</td>
<td>{{ payment.value|add:"0"|intcomma }}</td>
<td>{{ payment.fee|intcomma }}</td>
<td>{{ payment.ppm|intcomma }}</td>
<td>{% if payment.status == 1 %}In-Flight{% elif payment.status == 2 %}Succeeded{% elif payment.status == 3 %}Failed{% else %}{{ payment.status }}{% endif %}</td>
<td>{% if payment.status == 2 %}{% if payment.chan_out_alias == '' %}---{% else %}{{ payment.chan_out_alias }}{% endif %}{% else %}---{% endif %}</td>
<td>{% if payment.status == 2 %}{{ payment.chan_out }}{% else %}---{% endif %}</td>
<td>{% if payment.status == 2 %}<a href="/route?={{ payment.payment_hash }}" target="_blank">Open</a>{% else %}---{% endif %}</td>
<td title="{{ payment.message }}">{% if payment.keysend_preimage != None %}Yes{% else %}No{% endif %}</td>
</tr>
{% endfor %}
<tfoot id="loadMorePayments">
<tr style="background-color:transparent;cursor:pointer;">
<td colspan="10">
<a onclick="loadPayments()">Load More</a>
</td>
</tr>
</tfoot>
</table>
</div>
{% endif %}
{% if invoices %}
<div class="w3-container w3-padding-small">
<div id="invoicesDiv" class="w3-container w3-padding-small">
<h2>Last 5 <a href="/invoices" target="_blank">Payments Received</a></h2>
<table class="w3-table-all w3-centered w3-hoverable">
<table id="invoicesTable" class="w3-table-all w3-centered w3-hoverable">
<tr>
<th>Created</th>
<th>Settled</th>
@ -394,22 +384,15 @@
<th width=10%>Channel In</th>
<th><a href="/keysends" target="_blank">Keysend</a></th>
</tr>
{% for invoice in invoices %}
<tr>
<td title="{{ invoice.creation_date }}">{{ invoice.creation_date|naturaltime }}</td>
<td title="{{ invoice.settle_date }}">{% if invoice.state == 1 %}{{ invoice.settle_date|naturaltime }}{% else %}---{% endif %}</td>
<td><a href="/route?={{ invoice.r_hash }}" target="_blank">{{ invoice.r_hash }}</td>
<td>{{ invoice.value|add:"0"|intcomma }}</td>
<td>{% if invoice.state == 1 %}{{ invoice.amt_paid|intcomma }}{% else %}---{% endif %}</td>
<td>{% if invoice.state == 0 %}Open{% elif invoice.state == 1 %}Settled{% elif invoice.state == 2 %}Canceled{% else %}{{ invoice.state }}{% endif %}</td>
<td>{% if invoice.state == 1 %}{% if invoice.chan_in_alias == '' %}---{% else %}{{ invoice.chan_in_alias }}{% endif %}{% else %}---{% endif %}</td>
<td>{% if invoice.state == 1 and invoice.chan_in != None %}<a href="/channel?={{ invoice.chan_in }}" target="_blank">{{ invoice.chan_in }}</a>{% else %}---{% endif %}</td>
<td title="{{ invoice.message }}">{% if invoice.keysend_preimage != None %}Yes{% else %}No{% endif %}</td>
</tr>
{% endfor %}
<tfoot id="loadMoreInvoices">
<tr style="background-color:transparent;cursor:pointer;">
<td colspan="9">
<a onclick="loadInvoices()">Load More</a>
</td>
</tr>
</tfoot>
</table>
</div>
{% endif %}
<div id="failedhtlcsDiv" class="w3-container w3-padding-small">
<h2>Last <a href="/failed_htlcs?={{chan_id}}_O" target="_blank">Failed HTLCs</a></h2>
<table id="failedhtlcsTable" class="w3-table-all w3-centered w3-hoverable">
@ -427,7 +410,7 @@
</tr>
<tfoot id="loadMoreFailedHTLCs">
<tr style="background-color:transparent;cursor:pointer;">
<td colspan="9">
<td colspan="12">
<a onclick="loadFailedHTLCs()">Load More</a>
</td>
</tr>
@ -455,7 +438,7 @@
</tr>
<tfoot id="loadMorePeerEvents">
<tr style="background-color:transparent;cursor:pointer;">
<td colspan="9">
<td colspan="8">
<a onclick="loadPeerEvents()">Load More</a>
</td>
</tr>
@ -475,14 +458,20 @@
<script>
document.addEventListener('DOMContentLoaded', async () =>{
const forwardsTable = byId('forwardsTable')
const paymentsTable = byId('paymentsTable')
const invoicesTable = byId('invoicesTable')
const failedhtlcsTable = byId('failedhtlcsTable')
const peerEventsTable = byId('peerEventsTable')
const forwards_task = GET('forwards', {data: {chan_in_or_out: "{{ chan_id }}", limit: 5} })
const payments_task = GET('payments', {data: {chan_out: "{{ chan_id }}", status__lt: 3, limit: 5} })
const invoices_task = GET('invoices', {data: {chan_in: "{{ chan_id }}", state__lt: 2, limit: 5} })
const failedhtlcs_task = GET('failedhtlcs', {data: {chan_in_or_out: "{{ chan_id}}", wire_failure__lt: 99, limit: 5} })
const peerEvents_task = GET('peerevents', {data: {chan_id: "{{ chan_id}}", limit: 5} })
let failed_htlcs = (await failedhtlcs_task).results
let peer_events = (await peerEvents_task).results
let forwards = (await forwards_task).results
let payments = (await payments_task).results
let invoices = (await invoices_task).results
if(byId('rebalsTable_')==null){
byId("rebalancerDiv").style.display = "none"
}
@ -501,6 +490,16 @@
}else{
build_routed(forwards)
}
if(payments.length==0){
byId("paymentsDiv").style.display = "none"
}else{
build_payments(payments)
}
if(invoices.length==0){
byId("invoicesDiv").style.display = "none"
}else{
build_invoices(invoices)
}
})
async function loadForwards(){
const lastId = forwardsTable.tBodies[0].lastChild.objId
@ -509,8 +508,10 @@
build_routed(forwards)
}
async function build_routed(next_forwards){
if(next_forwards.length==0){
if(next_forwards.length<5){
byId("loadMoreForwards").style.display = "none"
}
if(next_forwards.length==0){
return
}
const tableBody = forwardsTable.querySelector("tbody")
@ -521,12 +522,51 @@
tableBody.appendChild(use(routed_template).render(f))
}
}
async function loadPayments(){
const lastId = paymentsTable.tBodies[0].lastChild.objId
const payments_task = GET('payments', {data: {chan_out: "{{ chan_id }}", index__lt: lastId, status__lt: 3, limit: 5} })
let next_payments = (await payments_task).results
build_payments(next_payments)
}
async function build_payments(payments){
if(payments.length<5){
byId("loadMorePayments").style.display = "none"
}
if(payments.length==0){
return
}
const tableBody = paymentsTable.querySelector("tbody")
for (p of payments){
p.ppm = p.fee*1000000/p.value
tableBody.appendChild(use(payments_template).render(p))
}
}
async function loadInvoices(){
const lastId = invoicesTable.tBodies[0].lastChild.objId
const invoices_task = GET('invoices', {data: {chan_out: "{{ chan_id }}", index__lt: lastId, state__lt: 2, limit: 5} })
let next_invoices = (await invoices_task).results
build_invoices(next_invoices)
}
async function build_invoices(invoices){
if(invoices.length<5){
byId("loadMoreInvoices").style.display = "none"
}
if(invoices.length==0){
return
}
const tableBody = invoicesTable.querySelector("tbody")
for (inv of invoices){
tableBody.appendChild(use(invoices_template).render(inv))
}
}
async function loadFailedHTLCs(){
const lastId = failedhtlcsTable.tBodies[0].lastChild.objId
const failedhtlcs_task = GET('failedhtlcs', {data: {chan_in_or_out: "{{ chan_id}}", wire_failure__lt: 99, id__lt: lastId, limit: 5} })
let next_failed_htlcs = (await failedhtlcs_task).results
if(next_failed_htlcs.length==0){
if(next_failed_htlcs.length<5){
byId("loadMoreFailedHTLCs").style.display = "none"
}
if(next_failed_htlcs.length==0){
return
}
build_failedhtlcs(next_failed_htlcs)
@ -541,8 +581,10 @@
const lastId = peerEventsTable.tBodies[0].lastChild.objId
const peerEvents_task = GET('peerevents', {data: {chan_id: "{{ chan_id}}", id__lt: lastId, limit: 5} })
let next_peer_events = (await peerEvents_task).results
if(next_peer_events.length==0){
if(next_peer_events.length<5){
byId("loadMorePeerEvents").style.display = "none"
}
if(next_peer_events.length==0){
return
}
build_peerEvents(next_peer_events)

View file

@ -317,7 +317,7 @@
</tbody>
<tfoot id="loadMoreForwards">
<tr style="background-color:transparent;cursor:pointer;">
<td colspan="9">
<td colspan="11">
<a onclick="loadForwards()">Load More</a>
</td>
</tr>
@ -326,7 +326,7 @@
</div>
{% include 'rebalances_table.html' with count=10 load_count=10 title='<a href="/rebalances" target="_blank">Rebalance Requests</a>' %}
<div id="payments_container" style="display:none" class="w3-container w3-padding-small">
<table class="w3-table-all w3-centered w3-hoverable">
<table id="paymentsTable" class="w3-table-all w3-centered w3-hoverable">
<thead>
<tr>
<th>Timestamp</th>
@ -342,10 +342,17 @@
</tr>
</thead>
<tbody id="payments"></tbody>
<tfoot id="loadMorePayments">
<tr style="background-color:transparent;cursor:pointer;">
<td colspan="10">
<a onclick="loadPayments()">Load More</a>
</td>
</tr>
</tfoot>
</table>
</div>
<div id="invoices_container" style="display:none" class="w3-container w3-padding-small">
<table class="w3-table-all w3-centered w3-hoverable">
<table id="invoicesTable" class="w3-table-all w3-centered w3-hoverable">
<thead>
<tr>
<th>Created</th>
@ -360,6 +367,13 @@
</tr>
</thead>
<tbody id="invoices"></tbody>
<tfoot id="loadMoreInvoices">
<tr style="background-color:transparent;cursor:pointer;">
<td colspan="9">
<a onclick="loadInvoices()">Load More</a>
</td>
</tr>
</tfoot>
</table>
</div>
<div id="failed_htlcs_container" style="display:none" class="w3-container w3-padding-small">
@ -381,7 +395,7 @@
<tbody id="failed_htlcs"></tbody>
<tfoot id="loadMoreFailedHTLCs">
<tr style="background-color:transparent;cursor:pointer;">
<td colspan="9">
<td colspan="12">
<a onclick="loadFailedHTLCs()">Load More</a>
</td>
</tr>
@ -492,33 +506,6 @@
}, {local_commit, local_chan_reserve, initiator}, {
"is_active": ch => ({innerHTML: ch.is_active, title: (ch.is_active ? 'Up' : 'Down') + 'time: ' + formatDate(ch.last_update).replace(" ago", ""), style: {backgroundColor: ch.is_active ? green : red} })
})
let {fee, ppm} = routed_template
const payments_template = Object.assign({}, {
"creation_date": p => ({innerHTML: formatDate(p.creation_date), title: adjustTZ(p.creation_date) }),
"value": p => ({innerHTML: p.value.intcomma()}),
"fee": p => ({innerHTML: parseFloat(p.fee.toFixed(3)).toLocaleString()})
}, {ppm}, {
"status": p => ({innerHTML: ['Unknown', 'In-Flight', 'Succeeded', 'Failed'][p.status]}),
"chan_out_alias": p => ({innerHTML: p.chan_out_alias||'---'}),
"chan_out": p => ({innerHTML: p.status == 2 && p.chan_out !== 'MPP' ? `<a href="/channel?=${p.chan_out}" target="_blank">${(BigInt(p.chan_out)>>40n)+'x'+(BigInt(p.chan_out)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(p.chan_out) & BigInt('0xFFFF'))}` : p.chan_out || '---' }),
"payment_hash": p => ({innerHTML: `<a href="/route?=${p.payment_hash}" target="_blank">${p.payment_hash.substring(0,7)}</a>`}),
"rebal_chan": p => ({innerHTML: (p.rebal_chan || '').length > 0 ? 'Yes' : 'No'}),
"keysend_preimage": p => ({innerHTML: p.keysend_preimage ? 'Yes' : 'No'})
}
)
let {creation_date, value, keysend_preimage} = payments_template
const invoices_template = Object.assign({}, {creation_date}, {
"settle_date": i => ({innerHTML: formatDate(i.settle_date), title: adjustTZ(i.settle_date)})
}, {value}, {
"amt_paid": i => ({innerHTML: i.amt_paid.intcomma()}),
"state": i => ({innerHTML: ['Open', 'Settled', 'Cancelled', 'Accepted'][i.state]}),
"chan_in_alias": i => ({innerHTML: i.chan_in_alias||'---'}),
"chan_in": i => ({innerHTML: i.chan_in? `<a href="/channel?=${i.chan_in}" target="_blank">${(BigInt(i.chan_in)>>40n)+'x'+(BigInt(i.chan_in)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(i.chan_in) & BigInt('0xFFFF'))}</a>` : '---'}),
"r_hash": i => ({innerHTML: `<a href="/route?=${i.r_hash}" target="_blank">${i.r_hash.substring(0,7)}</a>`}),
"keysend_preimage": i => ({innerHTML: keysend_preimage(i).innerHTML, title: i.message }),
}
)
const pending_open_template = {
"alias": ch => ({innerHTML: `<a href="{{graph_links}}/{{network}}node/${ch.remote_node_pub}" target="_blank">${ch.alias == '' ? ch.remote_node_pub.substr(0,12) : ch.alias }</a>`, title: ch.remote_node_pub}),
"channel_point": ch => ({innerHTML: `<a href='{{ network_links }}/{{ network }}tx/${ch.funding_txid}' target="_blank">${ch.channel_point}</a>`}),
@ -811,6 +798,23 @@
table.append(template.render(f))
}
}
async function loadPayments(){
const lastId = paymentsTable.tBodies[0].lastChild.objId
const payments_task = GET('payments', {data: {status__lt: 3, index__lt: lastId, limit: 10} })
let next_payments = (await payments_task).results
if(next_payments.length==0){
byId("loadMorePayments").style.display = "none"
return
}
build_next_payments(next_payments)
}
async function build_next_payments(next_payments){
const tableBody = paymentsTable.querySelector("tbody")
for (p of next_payments){
p.ppm = p.fee*1000000/p.value
tableBody.appendChild(use(payments_template).render(p))
}
}
async function build_payments(payments7d){
let remaining = 10-payments7d.length, template = use(payments_template), payments = payments7d
if(remaining > 0) payments = [...payments, ...(await GET('payments', {data: {creation_date__lt: _7days_ago, status__lt:3, limit: remaining} })).results]
@ -823,6 +827,22 @@
table.append(template.render(p))
}
}
async function loadInvoices(){
const lastId = invoicesTable.tBodies[0].lastChild.objId
const invoices_task = GET('invoices', {data: {state__lt: 2, index__lt: lastId, limit: 10} })
let next_invoices = (await invoices_task).results
if(next_invoices.length==0){
byId("loadMoreInvoices").style.display = "none"
return
}
build_next_invoices(next_invoices)
}
async function build_next_invoices(next_invoices){
const tableBody = invoicesTable.querySelector("tbody")
for (inv of next_invoices){
tableBody.appendChild(use(invoices_template).render(inv))
}
}
function build_invoices(invoices){
if(!addTitle('Invoices', invoices, `Last 10 <a href="/invoices" target="_blank">Invoices</a>`)) return
const table = byId('invoices'), template = use(invoices_template)
@ -917,10 +937,10 @@
}
async function buildMain() {
const node_info = await GET('node_info', {data: {limit:1}})
const inv_rev_task = GET('invoices', {data: {state: 1, is_revenue: true, settle_date__gte: _7days_ago}})
const inv_rev_task = GET('invoices', {data: {state__lt: 2, is_revenue: true, settle_date__gte: _7days_ago}})
const onchain_task = GET('onchain', {data: {time_stamp__gte: _7days_ago} })
const forwards7d_task = GET('forwards', {data: {forward_date__gte: _7days_ago} })
const payments_task = GET('payments', {data: {creation_date__gte: _7days_ago, status: 2} })
const payments_task = GET('payments', {data: {creation_date__gte: _7days_ago, status__lt: 3} })
buildMainStats(node_info)
const closures_task = GET('closures', {data: {close_height__gte: (node_info.block.height - 1008) } })

View file

@ -2037,7 +2037,7 @@ class PaymentsViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [IsAuthenticated] if settings.LOGIN_REQUIRED else []
queryset = Payments.objects.all().order_by('-creation_date')
serializer_class = PaymentSerializer
filterset_fields = {'status':['exact','lt','gt'], 'creation_date':['lte','gte']}
filterset_fields = {'status':['exact','lt','gt'], 'creation_date':['lte','gte'], 'chan_out': ['exact'], 'index': ['lt']}
class PaymentHopsViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [IsAuthenticated] if settings.LOGIN_REQUIRED else []
@ -2048,7 +2048,7 @@ class InvoicesViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [IsAuthenticated] if settings.LOGIN_REQUIRED else []
queryset = Invoices.objects.all().order_by('-creation_date')
serializer_class = InvoiceSerializer
filterset_fields = {'state': ['exact','lt', 'gt'], 'is_revenue': ['exact'], 'settle_date': ['gte']}
filterset_fields = {'state': ['exact','lt', 'gt'], 'is_revenue': ['exact'], 'settle_date': ['gte'], 'chan_in': ['exact'], 'index': ['lt']}
def update(self, request, pk=None):
setting = get_object_or_404(Invoices.objects.all(), pk=pk)