Watchonly statspay basic gui working

This commit is contained in:
benarc
2021-02-22 00:45:52 +00:00
parent ecfcc167f0
commit 385406a78a
6 changed files with 84 additions and 458 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "SatsPay Server",
"short_description": "Create onchain and LN charges",
"icon": "visibility",
"icon": "payment",
"contributors": [
"arcbtc"
]

View File

@@ -6,20 +6,7 @@ from .models import Charges
from lnbits.helpers import urlsafe_short_hash
from embit import bip32
from embit import ec
from embit.networks import NETWORKS
from embit import base58
from embit.util import hashlib
import io
from embit.util import secp256k1
from embit import hashes
from binascii import hexlify
from quart import jsonify
from embit import script
from embit import ec
from embit.networks import NETWORKS
from binascii import unhexlify, hexlify, a2b_base64, b2a_base64
import httpx

View File

@@ -5,36 +5,8 @@
<q-card>
<q-card-section>
{% raw %}
<q-btn unelevated color="deep-purple" @click="formDialog.show = true"
>New wallet </q-btn
>
<q-btn unelevated color="deep-purple"
icon="edit">
<div class="cursor-pointer">
<q-tooltip>
Point to another Mempool
</q-tooltip>
{{ this.mempool.endpoint }}
<q-popup-edit v-model="mempool.endpoint">
<q-input color="accent" v-model="mempool.endpoint">
</q-input>
<center><q-btn
flat
dense
@click="updateMempool()"
v-close-popup
>set</q-btn>
<q-btn
flat
dense
v-close-popup
>cancel</q-btn>
</center>
</q-popup-edit>
</div>
</q-btn
<q-btn unelevated color="deep-purple" @click="formDialogCharge.show = true"
>New charge </q-btn
>
</q-card-section>
</q-card>
@@ -43,113 +15,7 @@
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">Wallets</h5>
</div>
<div class="col-auto">
<q-input borderless dense debounce="300" v-model="filter" placeholder="Search">
<template v-slot:append>
<q-icon name="search"></q-icon>
</template>
</q-input>
</div>
</div>
<q-table
flat
dense
:data="walletLinks"
row-key="id"
:columns="WalletsTable.columns"
:pagination.sync="WalletsTable.pagination"
:filter="filter"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props" auto-width>
<div v-if="col.name == 'id'"></div>
<div v-else>
{{ col.label }}
</div>
</q-th>
<q-th auto-width></q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td auto-width>
<q-btn
unelevated
dense
size="xs"
icon="toll"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
@click="formDialogCharge.show = true, formDialogCharge.data.walletid = props.row.id"
>
<q-tooltip>
Charge link
</q-tooltip>
</q-btn>
<q-btn
unelevated
dense
size="xs"
icon="dns"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
@click="openQrCodeDialog(props.row.id)"
>
<q-tooltip>
Adresses
</q-tooltip>
</q-btn>
<q-btn
flat
dense
size="xs"
@click="openUpdateDialog(props.row.id)"
icon="edit"
color="light-blue"
></q-btn>
<q-btn
flat
dense
size="xs"
@click="deleteWalletLink(props.row.id)"
icon="cancel"
color="pink"
></q-btn>
</q-td>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props" auto-width>
<div v-if="col.name == 'id'"></div>
<div v-else>
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</q-table>
</q-card-section>
</q-card>
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">Paylinks</h5>
<h5 class="text-subtitle1 q-my-none">Charges</h5>
</div>
<div class="col-auto">
<q-input borderless dense debounce="300" v-model="filter" placeholder="Search">
@@ -276,54 +142,6 @@
</q-card-section>
</q-card>
</div>
<q-dialog v-model="formDialog.show" position="top" @hide="closeFormDialog">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form @submit="sendFormData" class="q-gutter-md">
<q-input
filled
dense
v-model.trim="formDialog.data.title"
type="text"
label="Title"
></q-input>
<q-input
filled
type="textarea"
v-model="formDialog.data.masterpub"
height="50px"
autogrow
label="Master Public Key, either xpub, ypub, zpub"
></q-input>
<div class="row q-mt-lg">
<q-btn
v-if="formDialog.data.id"
unelevated
color="deep-purple"
type="submit"
>Update Watch-only Wallet</q-btn
>
<q-btn
v-else
unelevated
color="deep-purple"
:disable="
formDialog.data.masterpub == null ||
formDialog.data.title == null"
type="submit"
>Create Watch-only Wallet</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
>Cancel</q-btn
>
</div>
</q-form>
</q-card>
</q-dialog>
<q-dialog v-model="formDialogCharge.show" position="top" @hide="closeFormDialog">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form @submit="sendFormDataCharge" class="q-gutter-md">
@@ -350,8 +168,48 @@
v-model.trim="formDialogCharge.data.time"
type="number"
label="Time (secs)"
> </q-input>
> </q-input>
<div class="row">
<div class="col">
<div v-if="walletLinks.length > 0">
<q-checkbox v-model="formDialogCharge.data.onchain" label="Onchain" />
</div>
<div v-else>
<q-checkbox :value=false label="Onchain" disabled>
<q-tooltip>
Watch-Only extension MUST be activated and have a wallet
</q-tooltip>
</q-checkbox>
</div>
</div>
<div class="col">
<div>
<q-checkbox v-model="formDialogCharge.data.lnbits" label="LNbits wallet" />
</div>
</div>
</div>
<div v-if="formDialogCharge.data.onchain">
<q-select filled dense emit-value v-model="onchainwallet" :options="walletLinks" label="Onchain Wallet" />
</div>
<div v-if="formDialogCharge.data.lnbits">
<q-select
filled
dense
emit-value
v-model="formDialog.data.wallet"
:options="g.user.walletOptions"
label="Wallet *"
>
</div>
<div class="row q-mt-lg">
<q-btn
v-if="formDialogCharge.data.id"
@@ -379,68 +237,6 @@
</q-dialog>
<q-dialog v-model="Addresses.show" position="top">
<q-card v-if="Addresses.data" class="q-pa-lg lnbits__dialog-card">
{% raw %}
<h5 class="text-subtitle1 q-my-none">Addresses</h5>
<q-separator></q-separator><br/>
<p><strong>Current:</strong>
{{ currentaddress }}
<q-btn
flat
dense
size="ms"
icon="visibility"
type="a"
:href="mempool.endpoint + '/address/' + currentaddress"
target="_blank"
></q-btn>
</p>
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
<qrcode
:value="currentaddress"
:options="{width: 800}"
class="rounded-borders"
></qrcode>
</q-responsive>
<p style="word-break: break-all;">
<q-scroll-area style="height: 200px; max-width: 100%;">
<q-list bordered v-for="data in Addresses.data.slice().reverse()">
<q-item>
<q-item-section>{{ data.address }}</q-item-section>
<q-btn
flat
dense
size="ms"
icon="visibility"
type="a"
:href="mempool.endpoint + '/address/' + data.address"
target="_blank"
></q-btn>
</q-item>
</q-list>
</q-scroll-area>
</p>
<div class="row q-mt-lg q-gutter-sm">
<q-btn
outline
color="grey"
@click="getFreshAddress(current)"
class="q-ml-sm"
>Get fresh address</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div>
</q-card>
</q-dialog>
{% endraw %}
</div>
{% endblock %} {% block scripts %} {{ window_vars(user) }}
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
@@ -485,10 +281,12 @@
data: function () {
return {
filter: '',
watchonlyactive: false,
balance: null,
checker: null,
walletLinks: [],
ChargeLinks: [],
onchainwallet: '',
currentaddress: "",
Addresses: {
show: false,
@@ -497,32 +295,7 @@
mempool:{
endpoint:""
},
WalletsTable: {
columns: [
{name: 'id', align: 'left', label: 'ID', field: 'id'},
{
name: 'title',
align: 'left',
label: 'Title',
field: 'title'
},
{
name: 'amount',
align: 'left',
label: 'Amount',
field: 'amount'
},
{
name: 'masterpub',
align: 'left',
label: 'MasterPub',
field: 'masterpub'
},
],
pagination: {
rowsPerPage: 10
}
},
ChargesTable: {
columns: [
{name: 'id', align: 'left', label: 'ID', field: 'id'},
@@ -545,10 +318,16 @@
field: 'amount_paid'
},
{
name: 'address',
name: 'onchain address',
align: 'left',
label: 'Address',
field: 'address'
label: 'Onchain Address',
field: 'onchainaddress'
},
{
name: 'LNbits wallet',
align: 'left',
label: 'LNbits wallet',
field: 'lnbits-wallet'
},
{
name: 'time to pay',
@@ -573,7 +352,7 @@
},
formDialogCharge: {
show: false,
data: {}
data: {onchain: false,lnbits:false}
},
qrCodeDialog: {
show: false,
@@ -584,59 +363,24 @@
methods: {
chargeRedirect: function (address){
window.location.href = this.mempool.endpoint + "/address/" + address;
},
getMempool: function () {
var self = this
LNbits.api
.request(
'GET',
'/satspay/api/v1/mempool',
this.g.user.wallets[0].inkey
)
.then(function (response) {
console.log(response.data.endpoint)
self.mempool.endpoint = response.data.endpoint
console.log(this.mempool.endpoint)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
updateMempool: function () {
var self = this
var wallet = this.g.user.wallets[0]
LNbits.api
.request(
'PUT',
'/satspay/api/v1/mempool',
wallet.inkey, self.mempool)
.then(function (response) {
self.mempool.endpoint = response.data.endpoint
self.walletLinks.push(mapwalletLink(response.data))
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
getWalletLinks: function () {
var self = this
LNbits.api
.request(
'GET',
'/satspay/api/v1/wallet',
'/watchonly/api/v1/wallet',
this.g.user.wallets[0].inkey
)
.then(function (response) {
self.walletLinks = response.data.map(function (obj) {
return mapWalletLink(obj)
})
for (i = 0; i < response.data.length; i++) {
self.walletLinks.push(response.data[i].title + " " + response.data[i].id)
}
console.log(self.walletLinks)
return
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
@@ -831,13 +575,23 @@
},
created: function () {
if (this.g.user.wallets.length) {
var getWalletLinks = this.getWalletLinks
getWalletLinks()
var getWalletLinks = this.getWalletLinks
getWalletLinks()
var getCharges = this.getCharges
getCharges()
var getMempool = this.getMempool
getMempool()
if (this.g.user.wallets.length) {
for (i = 0; i < this.g.extensions.length; i++) {
if(this.g.extensions[i].code == "watchonly"){
console.log(this.walletLinks)
if(this.walletLinks.length > 0 ){
this.watchonlyactive = true
}
}
}
}

View File

@@ -142,124 +142,9 @@
</q-table>
</q-card-section>
</q-card>
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">Paylinks</h5>
</div>
<div class="col-auto">
<q-input borderless dense debounce="300" v-model="filter" placeholder="Search">
<template v-slot:append>
<q-icon name="search"></q-icon>
</template>
</q-input>
</div>
</div>
<q-table
flat
dense
:data="ChargeLinks"
row-key="id"
:columns="ChargesTable.columns"
:pagination.sync="ChargesTable.pagination"
:filter="filter"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props" auto-width>
<div v-if="col.name == 'id'"></div>
<div v-else>
{{ col.label }}
</div>
</q-th>
<q-th auto-width></q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td auto-width>
<q-icon v-if="props.row.timeleft < 1 && props.row.amount_paid < props.row.amount"
#unelevated
dense
size="xs"
name="error"
:color="($q.dark.isActive) ? 'red' : 'red'"
></q-icon>
<q-icon v-else-if="props.row.amount_paid > props.row.amount"
#unelevated
dense
size="xs"
name="check"
:color="($q.dark.isActive) ? 'green' : 'green'"
></q-icon>
<q-icon v-else="props.row.amount_paid < props.row.amount && props.row.timeleft > 1"
#unelevated
dense
size="xs"
name="cached"
:color="($q.dark.isActive) ? 'blue' : 'blue'"
></q-icon>
<q-btn
flat
dense
size="xs"
@click="openUpdateDialog(props.row.id)"
icon="edit"
color="light-blue"
></q-btn>
<q-btn
flat
dense
size="xs"
@click="deleteWalletLink(props.row.id)"
icon="cancel"
color="pink"
></q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props" auto-width>
<div v-if="col.name == 'id'"></div>
<div v-else>
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
{% endraw %}
</q-table>
</q-card-section>
</q-card>
</div>
{% endraw %}
<div class="col-12 col-md-5 q-gutter-y-md">
<q-card>
@@ -271,7 +156,7 @@
<q-card-section class="q-pa-none">
<q-separator></q-separator>
<q-list>
{% include "watchonly/_api_docs.html" %}
{% include "watchonly/_api_docs.html" %}
</q-list>
</q-card-section>
</q-card>