code quality fixes and cleanup
This commit is contained in:
parent
29b1b17967
commit
2bbbcd4d65
32 changed files with 4943 additions and 1368 deletions
|
|
@ -1,101 +1,123 @@
|
|||
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
|
||||
%} {% block page %}
|
||||
<div class="row q-col-gutter-md" id="configTable">
|
||||
<div class="col-12 q-gutter-y-md">
|
||||
<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">NWC Service Provider - Config</h5>
|
||||
</div>
|
||||
</div>
|
||||
<template>
|
||||
<q-markup-table flat>
|
||||
<tbody>
|
||||
<q-tr v-for="entry in entries" :key="entry.key">
|
||||
<q-td>
|
||||
<span>
|
||||
${entry.key}
|
||||
</span>
|
||||
</q-td>
|
||||
<q-td>
|
||||
<q-input v-model="entry.value" />
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</tbody>
|
||||
</q-markup-table>
|
||||
</template>
|
||||
<q-btn @click="saveConfig" unelevated label="Save" color="primary" class="q-mt-md full-width" />
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
<div class="col-12 q-gutter-y-md">
|
||||
<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">
|
||||
NWC Service Provider - Config
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
<template>
|
||||
<q-markup-table flat>
|
||||
<tbody>
|
||||
<q-tr v-for="entry in entries" :key="entry.key">
|
||||
<q-td>
|
||||
<span> ${entry.key} </span>
|
||||
</q-td>
|
||||
<q-td>
|
||||
<q-input v-model="entry.value" />
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</tbody>
|
||||
</q-markup-table>
|
||||
</template>
|
||||
<q-btn
|
||||
@click="saveConfig"
|
||||
unelevated
|
||||
label="Save"
|
||||
color="primary"
|
||||
class="q-mt-md full-width"
|
||||
/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
||||
<script>
|
||||
new Vue({
|
||||
el: "#vue",
|
||||
mixins: [windowMixin],
|
||||
delimiters: ["${", "}"],
|
||||
data: function () {
|
||||
return {
|
||||
entries: [],
|
||||
columns: [
|
||||
{
|
||||
name: "key",
|
||||
required: true,
|
||||
label: "Key",
|
||||
align: "left",
|
||||
field: (row) => row.key,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: "value",
|
||||
required: true,
|
||||
label: "Value",
|
||||
align: "left",
|
||||
field: (row) => row.value,
|
||||
sortable: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
new Vue({
|
||||
el: '#vue',
|
||||
mixins: [windowMixin],
|
||||
delimiters: ['${', '}'],
|
||||
data: function () {
|
||||
return {
|
||||
entries: [],
|
||||
columns: [
|
||||
{ name: 'key', required: true, label: 'Key', align: 'left', field: row => row.key, sortable: true },
|
||||
{ name: 'value', required: true, label: 'Value', align: 'left', field: row => row.value, sortable: true },
|
||||
]
|
||||
|
||||
methods: {
|
||||
fetchConfig() {
|
||||
this.entries = [];
|
||||
LNbits.api
|
||||
.request(
|
||||
"GET",
|
||||
"/nwcprovider/api/v1/config",
|
||||
this.g.user.wallets[0].adminkey,
|
||||
)
|
||||
.then((response) => {
|
||||
const newEntries = [];
|
||||
for (const [key, value] of Object.entries(response.data)) {
|
||||
newEntries.push({
|
||||
key: key,
|
||||
value: value,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
fetchConfig() {
|
||||
this.entries = [];
|
||||
LNbits.api
|
||||
.request('GET', '/nwcprovider/api/v1/config', this.g.user.wallets[0].adminkey)
|
||||
.then((response) => {
|
||||
const newEntries = [];
|
||||
for (const [key, value] of Object.entries(response.data)) {
|
||||
newEntries.push({
|
||||
key: key,
|
||||
value: value,
|
||||
});
|
||||
}
|
||||
|
||||
this.entries = newEntries;
|
||||
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error("Error fetching config:", error);
|
||||
});
|
||||
},
|
||||
async saveConfig() {
|
||||
const data = {};
|
||||
for (const entry of this.entries) {
|
||||
data[entry.key] = entry.value;
|
||||
}
|
||||
try {
|
||||
const response = await LNbits.api
|
||||
.request('POST', '/nwcprovider/api/v1/config', this.g.user.wallets[0].adminkey, data)
|
||||
this.$q.notify({
|
||||
message: 'Config saved, please restart the server',
|
||||
color: 'positive',
|
||||
position: 'top',
|
||||
timeout: 2000,
|
||||
actions: [{ icon: 'close', color: 'white' }]
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Error saving config:", error);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
created: function () {
|
||||
this.fetchConfig()
|
||||
this.entries = newEntries;
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error("Error fetching config:", error);
|
||||
});
|
||||
},
|
||||
async saveConfig() {
|
||||
const data = {};
|
||||
for (const entry of this.entries) {
|
||||
data[entry.key] = entry.value;
|
||||
}
|
||||
})
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
"POST",
|
||||
"/nwcprovider/api/v1/config",
|
||||
this.g.user.wallets[0].adminkey,
|
||||
data,
|
||||
);
|
||||
this.$q.notify({
|
||||
message: "Config saved, please restart the server",
|
||||
color: "positive",
|
||||
position: "top",
|
||||
timeout: 2000,
|
||||
actions: [{ icon: "close", color: "white" }],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error saving config:", error);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
created: function () {
|
||||
this.fetchConfig();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,14 @@
|
|||
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-select v-model="selectedWallet" filled dense emit-value :options="g.user.walletOptions"
|
||||
label="Wallet *"></q-select>
|
||||
<q-select
|
||||
v-model="selectedWallet"
|
||||
filled
|
||||
dense
|
||||
emit-value
|
||||
:options="g.user.walletOptions"
|
||||
label="Wallet *"
|
||||
></q-select>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
|
|
@ -19,16 +25,28 @@
|
|||
<div class="col items-center">
|
||||
<h5 class="text-subtitle1">Connected Apps</h5>
|
||||
</div>
|
||||
<q-btn @click="showConnectDialog()" aria-label="Connect" icon="add" color="primary" size="sm" round />
|
||||
<q-btn
|
||||
@click="showConnectDialog()"
|
||||
aria-label="Connect"
|
||||
icon="add"
|
||||
color="primary"
|
||||
size="sm"
|
||||
round
|
||||
/>
|
||||
</div>
|
||||
<q-table dense flat :data="nwcEntries" row-key="id" :columns="nwcsTable.columns"
|
||||
:pagination.sync="nwcsTable.pagination">
|
||||
<q-table
|
||||
dense
|
||||
flat
|
||||
:data="nwcEntries"
|
||||
row-key="id"
|
||||
:columns="nwcsTable.columns"
|
||||
:pagination.sync="nwcsTable.pagination"
|
||||
>
|
||||
<nwcprovider v-slot:header="props">
|
||||
<q-tr :props="props">
|
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||
${ col.label }
|
||||
</q-th>
|
||||
|
||||
</q-tr>
|
||||
</nwcprovider>
|
||||
|
||||
|
|
@ -38,20 +56,30 @@
|
|||
<div>${ col.value }</div>
|
||||
</q-td>
|
||||
|
||||
|
||||
|
||||
<q-td>
|
||||
<q-btn flat dense size="xs" @click="openConnectionInfoDialog(props.row)" icon="info" color="primary">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="xs"
|
||||
@click="openConnectionInfoDialog(props.row)"
|
||||
icon="info"
|
||||
color="primary"
|
||||
>
|
||||
<q-tooltip> Info </q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn flat dense size="xs" @click="deleteNWC(props.row.pubkey)" icon="cancel" color="pink">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="xs"
|
||||
@click="deleteNWC(props.row.pubkey)"
|
||||
icon="cancel"
|
||||
color="pink"
|
||||
>
|
||||
<q-tooltip> Delete </q-tooltip>
|
||||
</q-btn>
|
||||
</q-td>
|
||||
|
||||
</q-tr>
|
||||
</template>
|
||||
|
||||
</q-table>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
|
@ -62,80 +90,101 @@
|
|||
<q-card-section>
|
||||
<h6 class="text-subtitle1 q-my-none">NWC Service provider</h6>
|
||||
<p>
|
||||
Nostr Wallet Connect (NWC) are a way for applications to connect to a remote lightning wallet.
|
||||
Nostr Wallet Connect (NWC) are a way for applications to connect to a
|
||||
remote lightning wallet.
|
||||
</p>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pa-none">
|
||||
<q-separator></q-separator>
|
||||
<q-list>
|
||||
|
||||
<q-item clickable @click="go('../docs#/NWC%20Service%20Provider')"
|
||||
class="q-flex items-center justify-between">
|
||||
<q-item
|
||||
clickable
|
||||
@click="go('../docs#/NWC%20Service%20Provider')"
|
||||
class="q-flex items-center justify-between"
|
||||
>
|
||||
<q-icon name="swap_vertical_circle" class="q-mr-md"></q-icon>
|
||||
<q-item-section>Swagger API</q-item-section>
|
||||
</q-item>
|
||||
<q-separator></q-separator>
|
||||
<q-item v-if="this.g.user.admin" clickable @click="go('/nwcprovider/admin')"
|
||||
class="q-flex items-center justify-between">
|
||||
<q-item
|
||||
v-if="this.g.user.admin"
|
||||
clickable
|
||||
@click="go('/nwcprovider/admin')"
|
||||
class="q-flex items-center justify-between"
|
||||
>
|
||||
<q-icon name="settings" class="q-mr-md"></q-icon>
|
||||
<q-item-section>Settings</q-item-section>
|
||||
</q-item>
|
||||
|
||||
</q-list>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
|
||||
<q-dialog v-model="connectionInfoDialog.show" @hide="closeConnectionInfoDialog">
|
||||
<q-dialog
|
||||
v-model="connectionInfoDialog.show"
|
||||
@hide="closeConnectionInfoDialog"
|
||||
>
|
||||
<q-card class="q-pa-lg q-pt-xl" style="width: 500px">
|
||||
<q-card-section class="q-ma-none">
|
||||
<h3 class="q-ma-none q-mb-md text-center">Info</h3>
|
||||
<q-list>
|
||||
|
||||
<q-item>
|
||||
<q-item-section>Description</q-item-section>
|
||||
<q-item-section side>${connectionInfoDialog.data.description}</q-item-section>
|
||||
<q-item-section side
|
||||
>${connectionInfoDialog.data.description}</q-item-section
|
||||
>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>Last used</q-item-section>
|
||||
<q-item-section side>${connectionInfoDialog.data.last_used}</q-item-section>
|
||||
<q-item-section side
|
||||
>${connectionInfoDialog.data.last_used}</q-item-section
|
||||
>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>Expires</q-item-section>
|
||||
<q-item-section side>${connectionInfoDialog.data.expires_at}</q-item-section>
|
||||
<q-item-section side
|
||||
>${connectionInfoDialog.data.expires_at}</q-item-section
|
||||
>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>Created</q-item-section>
|
||||
<q-item-section side>${connectionInfoDialog.data.created_at}</q-item-section>
|
||||
<q-item-section side
|
||||
>${connectionInfoDialog.data.created_at}</q-item-section
|
||||
>
|
||||
</q-item>
|
||||
|
||||
<q-item>
|
||||
<q-item-section>Permissions</q-item-section>
|
||||
<q-item-section side>
|
||||
|
||||
${connectionInfoDialog.data.permissions}
|
||||
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>Limits
|
||||
<q-item-section
|
||||
>Limits
|
||||
<q-markup-table class="q-mt-md">
|
||||
<thead>
|
||||
<q-tr>
|
||||
<q-th>
|
||||
Budget (sats)
|
||||
<div style="font-size: smaller;">used/max</div>
|
||||
<div style="font-size: smaller">used/max</div>
|
||||
</q-th>
|
||||
<q-th>Refresh</q-th>
|
||||
</q-tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<q-tr v-if="!connectionInfoDialog.data.budgets || connectionInfoDialog.data.budgets.length === 0">
|
||||
<q-tr
|
||||
v-if="!connectionInfoDialog.data.budgets || connectionInfoDialog.data.budgets.length === 0"
|
||||
>
|
||||
<q-td colspan="2" class="text-center">No limits</q-td>
|
||||
</q-tr>
|
||||
<q-tr v-else v-for="budget in connectionInfoDialog.data.budgets">
|
||||
<q-td class="text-center">${budget.used_budget_sats} / ${budget.budget_sats}</q-td>
|
||||
<q-tr
|
||||
v-else
|
||||
v-for="budget in connectionInfoDialog.data.budgets"
|
||||
>
|
||||
<q-td class="text-center"
|
||||
>${budget.used_budget_sats} / ${budget.budget_sats}</q-td
|
||||
>
|
||||
<q-td class="text-center">${budget.refresh_window}</q-td>
|
||||
</q-tr>
|
||||
</tbody>
|
||||
|
|
@ -144,27 +193,44 @@
|
|||
</q-item>
|
||||
</q-list>
|
||||
<q-card-actions class="q-mt-md">
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto full-width " @click="closePairingDialog">Close</q-btn>
|
||||
<q-btn
|
||||
v-close-popup
|
||||
flat
|
||||
color="grey"
|
||||
class="q-ml-auto full-width"
|
||||
@click="closePairingDialog"
|
||||
>Close</q-btn
|
||||
>
|
||||
</q-card-actions>
|
||||
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
</q-dialog>
|
||||
|
||||
|
||||
<q-dialog v-model="pairingQrDialog.show" @hide="closePairingQrDialog">
|
||||
<q-card class="q-pa-lg q-pt-xl" style="width: 500px">
|
||||
<q-card-section class="q-ma-none">
|
||||
<q-responsive :ratio="1" class="q-mx-xl q-mb-md">
|
||||
<lnbits-qrcode :value="pairingQrDialog.data.pairingUrl"></lnbits-qrcode>
|
||||
<lnbits-qrcode
|
||||
:value="pairingQrDialog.data.pairingUrl"
|
||||
></lnbits-qrcode>
|
||||
</q-responsive>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-ma-none">
|
||||
<p class=" q-ma-none text-center">Please scan this QR code with a supported app</p>
|
||||
<p class=" q-ma-none text-center text-caption ">Connect only with app you trust!</p>
|
||||
<p class="q-ma-none text-center">
|
||||
Please scan this QR code with a supported app
|
||||
</p>
|
||||
<p class="q-ma-none text-center text-caption">
|
||||
Connect only with app you trust!
|
||||
</p>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn label="Close" flat color="grey" class="full-width" @click="closePairingQrDialog"></q-btn>
|
||||
<q-btn
|
||||
label="Close"
|
||||
flat
|
||||
color="grey"
|
||||
class="full-width"
|
||||
@click="closePairingQrDialog"
|
||||
></q-btn>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
|
@ -173,33 +239,58 @@
|
|||
<q-card class="q-pa-lg q-pt-xl" style="width: 500px">
|
||||
<q-card-section class="q-ma-none">
|
||||
<h3 class="q-ma-none q-mb-md text-center">Pairing</h3>
|
||||
<p class=" q-ma-none text-center">Complete the last step of the setup by pasting or scanning your connection's
|
||||
pairing secret in the desired
|
||||
app to
|
||||
finalise the connection.</p>
|
||||
<p class=" q-ma-none text-center text-caption ">Connect only with app you trust!</p>
|
||||
<p class="q-ma-none text-center">
|
||||
Complete the last step of the setup by pasting or scanning your
|
||||
connection's pairing secret in the desired app to finalise the
|
||||
connection.
|
||||
</p>
|
||||
<p class="q-ma-none text-center text-caption">
|
||||
Connect only with app you trust!
|
||||
</p>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn label="Open in Supported App" color="primary" class="full-width" icon="launch"
|
||||
@click="openPairingUrl()"></q-btn>
|
||||
<q-btn
|
||||
label="Open in Supported App"
|
||||
color="primary"
|
||||
class="full-width"
|
||||
icon="launch"
|
||||
@click="openPairingUrl()"
|
||||
></q-btn>
|
||||
</q-card-actions>
|
||||
<q-card-section class="q-ma-none">
|
||||
<h5 class="q-ma-none text-center">Advanced</h5>
|
||||
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions>
|
||||
<q-btn label="Copy Pairing URL" flat color="grey" class="full-width" icon="content_copy"
|
||||
@click="copyPairingUrl()"></q-btn>
|
||||
<q-btn
|
||||
label="Copy Pairing URL"
|
||||
flat
|
||||
color="grey"
|
||||
class="full-width"
|
||||
icon="content_copy"
|
||||
@click="copyPairingUrl()"
|
||||
></q-btn>
|
||||
</q-card-actions>
|
||||
<q-card-actions>
|
||||
|
||||
<q-btn label="Show QR Code" flat color="grey" class="full-width" icon="qr_code_scanner"
|
||||
@click="showPairingQR()"></q-btn>
|
||||
<q-btn
|
||||
label="Show QR Code"
|
||||
flat
|
||||
color="grey"
|
||||
class="full-width"
|
||||
icon="qr_code_scanner"
|
||||
@click="showPairingQR()"
|
||||
></q-btn>
|
||||
</q-card-actions>
|
||||
|
||||
<q-card-actions>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto full-width" @click="closePairingDialog">Close</q-btn>
|
||||
<q-btn
|
||||
v-close-popup
|
||||
flat
|
||||
color="grey"
|
||||
class="q-ml-auto full-width"
|
||||
@click="closePairingDialog"
|
||||
>Close</q-btn
|
||||
>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
|
@ -209,42 +300,78 @@
|
|||
<h3 class="q-ma-none q-mb-xl text-center">Add connection</h3>
|
||||
|
||||
<q-form @submit="confirmConnectDialog" class="q-gutter-md">
|
||||
<q-input v-model="connectDialog.data.description" filled dense label="Description"></q-input>
|
||||
<q-input
|
||||
v-model="connectDialog.data.description"
|
||||
filled
|
||||
dense
|
||||
label="Description"
|
||||
></q-input>
|
||||
<div class="row">
|
||||
<q-input :disable="connectDialog.data.neverExpires" v-model="connectDialog.data.expires_at" class="col"
|
||||
v-model="connectDialog.data.expires_at" filled dense label="Expires at" type="datetime-local"></q-input>
|
||||
<q-checkbox v-model="connectDialog.data.neverExpires" class="col-auto"
|
||||
v-model="connectDialog.data.neverExpires" label="Never"></q-checkbox>
|
||||
<q-input
|
||||
:disable="connectDialog.data.neverExpires"
|
||||
v-model="connectDialog.data.expires_at"
|
||||
class="col"
|
||||
v-model="connectDialog.data.expires_at"
|
||||
filled
|
||||
dense
|
||||
label="Expires at"
|
||||
type="datetime-local"
|
||||
></q-input>
|
||||
<q-checkbox
|
||||
v-model="connectDialog.data.neverExpires"
|
||||
class="col-auto"
|
||||
v-model="connectDialog.data.neverExpires"
|
||||
label="Never"
|
||||
></q-checkbox>
|
||||
</div>
|
||||
<q-list dense bordered padding>
|
||||
<q-item>
|
||||
<q-item-label header>Authorize the app to</q-item-label>
|
||||
</q-item>
|
||||
<q-item v-for="permission in connectDialog.data.permissions">
|
||||
<q-checkbox v-model="permission.value" :label="permission.name"></q-checkbox>
|
||||
<q-checkbox
|
||||
v-model="permission.value"
|
||||
:label="permission.name"
|
||||
></q-checkbox>
|
||||
</q-item>
|
||||
</q-list>
|
||||
<q-list dense bordered padding>
|
||||
<q-item>
|
||||
<q-item-label header>Limit the spendable amount</q-item-label>
|
||||
</q-item>
|
||||
<q-item v-if="!connectDialog.data.budgets || connectDialog.data.budgets.length === 0">
|
||||
<q-item-section class="full-width text-center">No limit</q-item-section>
|
||||
<q-item
|
||||
v-if="!connectDialog.data.budgets || connectDialog.data.budgets.length === 0"
|
||||
>
|
||||
<q-item-section class="full-width text-center"
|
||||
>No limit</q-item-section
|
||||
>
|
||||
</q-item>
|
||||
<template v-else>
|
||||
|
||||
<q-item v-for="(budget,index) in connectDialog.data.budgets">
|
||||
<q-item-section>
|
||||
<q-input filled dense type="number" v-model="budget.budget_sats" label="Limit (sats)"></q-input>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
type="number"
|
||||
v-model="budget.budget_sats"
|
||||
label="Limit (sats)"
|
||||
></q-input>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-select filled dense emit-value :options="[
|
||||
<q-select
|
||||
filled
|
||||
dense
|
||||
emit-value
|
||||
:options="[
|
||||
{label: 'Daily', value: 'Daily'},
|
||||
{label: 'Weekly', value: 'Weekly'},
|
||||
{label: 'Monthly', value: 'Monthly'},
|
||||
{label: 'Yearly', value: 'Yearly'},
|
||||
{label: 'Never', value: 'Never'}
|
||||
]" label="Expires" v-model="budget.expiry"></q-select>
|
||||
]"
|
||||
label="Expires"
|
||||
v-model="budget.expiry"
|
||||
></q-select>
|
||||
</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-btn flat icon="delete" @click="deleteBudget(index)"></q-btn>
|
||||
|
|
@ -252,16 +379,25 @@
|
|||
</q-item>
|
||||
</template>
|
||||
<q-item>
|
||||
<q-btn flat icon="add" class="full-width" label="Add limit" @click="addBudget"></q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
icon="add"
|
||||
class="full-width"
|
||||
label="Add limit"
|
||||
@click="addBudget"
|
||||
></q-btn>
|
||||
</q-item>
|
||||
|
||||
</q-list>
|
||||
|
||||
<q-card-actions class="q-gutter-sm">
|
||||
<div class="col q-mr-md">
|
||||
<q-btn unelevated color="primary" type="submit" class="full-width">Connect</q-btn>
|
||||
<q-btn unelevated color="primary" type="submit" class="full-width"
|
||||
>Connect</q-btn
|
||||
>
|
||||
</div>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||
>Cancel</q-btn
|
||||
>
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</q-card>
|
||||
|
|
@ -269,16 +405,14 @@
|
|||
</div>
|
||||
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
||||
<script type="module">
|
||||
import * as NobleSecp256k1 from '/nwcprovider/static/js/noble-secp256k1.min.js';
|
||||
window.NobleSecp256k1 = NobleSecp256k1;
|
||||
import * as NobleSecp256k1 from "/nwcprovider/static/js/noble-secp256k1.min.js";
|
||||
window.NobleSecp256k1 = NobleSecp256k1;
|
||||
</script>
|
||||
<script>
|
||||
|
||||
|
||||
new Vue({
|
||||
el: '#vue',
|
||||
el: "#vue",
|
||||
mixins: [windowMixin],
|
||||
delimiters: ['${', '}'],
|
||||
delimiters: ["${", "}"],
|
||||
data: function () {
|
||||
return {
|
||||
selectedWallet: null,
|
||||
|
|
@ -286,15 +420,35 @@
|
|||
nwcEntries: [],
|
||||
nwcsTable: {
|
||||
columns: [
|
||||
{ name: 'description', align: 'left', label: 'Description', field: 'description' },
|
||||
{ name: 'status', align: 'left', label: 'Status', field: 'status' },
|
||||
{ name: 'last_used', align: 'left', label: 'Last used', field: 'last_used' },
|
||||
{ name: 'created_at', align: 'left', label: 'Created', field: 'created_at' },
|
||||
{ name: 'expires_at', align: 'left', label: 'Expires', field: 'expires_at' }
|
||||
{
|
||||
name: "description",
|
||||
align: "left",
|
||||
label: "Description",
|
||||
field: "description",
|
||||
},
|
||||
{ name: "status", align: "left", label: "Status", field: "status" },
|
||||
{
|
||||
name: "last_used",
|
||||
align: "left",
|
||||
label: "Last used",
|
||||
field: "last_used",
|
||||
},
|
||||
{
|
||||
name: "created_at",
|
||||
align: "left",
|
||||
label: "Created",
|
||||
field: "created_at",
|
||||
},
|
||||
{
|
||||
name: "expires_at",
|
||||
align: "left",
|
||||
label: "Expires",
|
||||
field: "expires_at",
|
||||
},
|
||||
],
|
||||
pagination: {
|
||||
rowsPerPage: 10
|
||||
}
|
||||
rowsPerPage: 10,
|
||||
},
|
||||
},
|
||||
connectDialog: {
|
||||
show: false,
|
||||
|
|
@ -303,28 +457,30 @@
|
|||
pairingDialog: {
|
||||
show: false,
|
||||
data: {
|
||||
pairingUrl: ''
|
||||
}
|
||||
pairingUrl: "",
|
||||
},
|
||||
},
|
||||
pairingQrDialog: {
|
||||
show: false,
|
||||
data: {
|
||||
pairingUrl: ''
|
||||
}
|
||||
pairingUrl: "",
|
||||
},
|
||||
},
|
||||
connectionInfoDialog: {
|
||||
show: false,
|
||||
data: {}
|
||||
data: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
showConnectDialog() {
|
||||
const wallet = this.getWallet();
|
||||
if (!wallet) {
|
||||
this.$q.notify({ color: 'red', message: 'Please select a wallet first' });
|
||||
this.$q.notify({
|
||||
color: "red",
|
||||
message: "Please select a wallet first",
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
this.connectDialog.show = true;
|
||||
|
|
@ -339,24 +495,28 @@
|
|||
},
|
||||
openPairingUrl() {
|
||||
const url = this.pairingDialog.data.pairingUrl;
|
||||
if (url) window.open(url, '_blank');
|
||||
if (url) window.open(url, "_blank");
|
||||
},
|
||||
go(url) {
|
||||
window.open(url, '_blank');
|
||||
window.open(url, "_blank");
|
||||
},
|
||||
async copyPairingUrl() {
|
||||
const url = this.pairingDialog.data.pairingUrl;
|
||||
if (url) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
this.$q.notify({ color: 'primary', message: 'URL copied to clipboard!' });
|
||||
this.$q.notify({
|
||||
color: "primary",
|
||||
message: "URL copied to clipboard!",
|
||||
});
|
||||
} catch (err) {
|
||||
this.$q.notify({ color: 'red', message: 'Failed to copy URL.' });
|
||||
this.$q.notify({ color: "red", message: "Failed to copy URL." });
|
||||
}
|
||||
}
|
||||
},
|
||||
showPairingQR() {
|
||||
this.pairingQrDialog.data.pairingUrl = this.pairingDialog.data.pairingUrl;
|
||||
this.pairingQrDialog.data.pairingUrl =
|
||||
this.pairingDialog.data.pairingUrl;
|
||||
this.pairingQrDialog.show = true;
|
||||
},
|
||||
closePairingQrDialog() {
|
||||
|
|
@ -364,33 +524,34 @@
|
|||
},
|
||||
loadConnectDialogData() {
|
||||
this.connectDialog.data = {
|
||||
description: '',
|
||||
description: "",
|
||||
expires_at: Date.now() + 1000 * 60 * 60 * 24 * 7,
|
||||
neverExpires: true,
|
||||
permissions: [],
|
||||
budgets: []
|
||||
budgets: [],
|
||||
};
|
||||
for (const permission of this.nodePermissions) {
|
||||
this.connectDialog.data.permissions.push({
|
||||
key: permission.key,
|
||||
name: permission.name,
|
||||
value: permission.value
|
||||
value: permission.value,
|
||||
});
|
||||
}
|
||||
},
|
||||
deleteBudget(index) {
|
||||
this.connectDialog.data.budgets.splice(index, 1)
|
||||
this.connectDialog.data.budgets.splice(index, 1);
|
||||
},
|
||||
addBudget() {
|
||||
this.connectDialog.data.budgets.push({
|
||||
budget_sats: 1000,
|
||||
used_budget_sats: 0,
|
||||
created_at: new Date(new Date().setHours(0, 0, 0, 0)).getTime() / 1000,
|
||||
expiration: 'never'
|
||||
})
|
||||
created_at:
|
||||
new Date(new Date().setHours(0, 0, 0, 0)).getTime() / 1000,
|
||||
expiration: "never",
|
||||
});
|
||||
},
|
||||
closeConnectDialog() {
|
||||
this.connectDialog.show = false
|
||||
this.connectDialog.show = false;
|
||||
this.loadConnectDialogData();
|
||||
},
|
||||
getWallet: function () {
|
||||
|
|
@ -405,7 +566,7 @@
|
|||
},
|
||||
async generateKeyPair() {
|
||||
while (!window.NobleSecp256k1) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1));
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
}
|
||||
const privKeyBytes = window.NobleSecp256k1.utils.randomPrivateKey();
|
||||
const pubKeyBytes = window.NobleSecp256k1.getPublicKey(privKeyBytes);
|
||||
|
|
@ -413,62 +574,67 @@
|
|||
privKeyBytes: privKeyBytes,
|
||||
pubKeyBytes: pubKeyBytes,
|
||||
privKey: window.NobleSecp256k1.etc.bytesToHex(privKeyBytes),
|
||||
pubKey: window.NobleSecp256k1.etc.bytesToHex(pubKeyBytes.slice(1))
|
||||
|
||||
pubKey: window.NobleSecp256k1.etc.bytesToHex(pubKeyBytes.slice(1)),
|
||||
};
|
||||
return out;
|
||||
},
|
||||
deleteNWC: async function (pubkey) {
|
||||
this.$q.dialog({
|
||||
title: 'Confirm Deletion',
|
||||
message: 'Are you sure you want to delete this connection?',
|
||||
cancel: true,
|
||||
persistent: true
|
||||
}).onOk(async () => {
|
||||
try {
|
||||
const wallet = this.getWallet();
|
||||
const response = await LNbits.api.request('DELETE', `/nwcprovider/api/v1/nwc/${pubkey}`, wallet.adminkey);
|
||||
this.loadNwcs();
|
||||
this.$q.notify({ type: 'positive', message: 'Deleted successfully' });
|
||||
|
||||
} catch (error) {
|
||||
LNbits.utils.notifyApiError(error);
|
||||
}
|
||||
}).onCancel(() => {
|
||||
// User canceled the operation
|
||||
});
|
||||
this.$q
|
||||
.dialog({
|
||||
title: "Confirm Deletion",
|
||||
message: "Are you sure you want to delete this connection?",
|
||||
cancel: true,
|
||||
persistent: true,
|
||||
})
|
||||
.onOk(async () => {
|
||||
try {
|
||||
const wallet = this.getWallet();
|
||||
const response = await LNbits.api.request(
|
||||
"DELETE",
|
||||
`/nwcprovider/api/v1/nwc/${pubkey}`,
|
||||
wallet.adminkey,
|
||||
);
|
||||
this.loadNwcs();
|
||||
this.$q.notify({
|
||||
type: "positive",
|
||||
message: "Deleted successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
LNbits.utils.notifyApiError(error);
|
||||
}
|
||||
})
|
||||
.onCancel(() => {
|
||||
// User canceled the operation
|
||||
});
|
||||
},
|
||||
loadNwcs: async function () {
|
||||
|
||||
const wallet = this.getWallet();
|
||||
if (!wallet) {
|
||||
this.nwcs = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await LNbits.api
|
||||
.request(
|
||||
'GET',
|
||||
'/nwcprovider/api/v1/nwc?includeExpired=true&calculateSpendBudget=true',
|
||||
wallet.adminkey
|
||||
)
|
||||
const response = await LNbits.api.request(
|
||||
"GET",
|
||||
"/nwcprovider/api/v1/nwc?includeExpired=true&calculateSpendBudget=true",
|
||||
wallet.adminkey,
|
||||
);
|
||||
this.nwcs = response.data;
|
||||
|
||||
} catch (error) {
|
||||
this.nwcs = [];
|
||||
}
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'GET',
|
||||
'/nwcprovider/api/v1/permissions',
|
||||
wallet.adminkey
|
||||
)
|
||||
"GET",
|
||||
"/nwcprovider/api/v1/permissions",
|
||||
wallet.adminkey,
|
||||
);
|
||||
const permissions = [];
|
||||
for (const [key, value] of Object.entries(response.data)) {
|
||||
permissions.push({
|
||||
key: key,
|
||||
name: value.name,
|
||||
value: value.default
|
||||
value: value.default,
|
||||
});
|
||||
}
|
||||
this.nodePermissions = permissions;
|
||||
|
|
@ -480,15 +646,18 @@
|
|||
for (const nwc of this.nwcs) {
|
||||
const t = Quasar.utils.date.formatDate(
|
||||
new Date(nwc.data.created_at * 1000),
|
||||
'YYYY-MM-DD HH:mm'
|
||||
"YYYY-MM-DD HH:mm",
|
||||
);
|
||||
const e = nwc.data.expires_at > 0 ? Quasar.utils.date.formatDate(
|
||||
new Date(nwc.data.expires_at * 1000),
|
||||
'YYYY-MM-DD HH:mm'
|
||||
) : "Never";
|
||||
const e =
|
||||
nwc.data.expires_at > 0
|
||||
? Quasar.utils.date.formatDate(
|
||||
new Date(nwc.data.expires_at * 1000),
|
||||
"YYYY-MM-DD HH:mm",
|
||||
)
|
||||
: "Never";
|
||||
const l = Quasar.utils.date.formatDate(
|
||||
new Date(nwc.data.last_used * 1000),
|
||||
'YYYY-MM-DD HH:mm'
|
||||
"YYYY-MM-DD HH:mm",
|
||||
);
|
||||
const nwcTableEntry = {
|
||||
description: nwc.data.description,
|
||||
|
|
@ -498,47 +667,49 @@
|
|||
pubkey: nwc.data.pubkey,
|
||||
permissions: nwc.data.permissions,
|
||||
budgets: [],
|
||||
status: "Active"
|
||||
}
|
||||
if(nwc.data.expires_at > 0 && nwc.data.expires_at < new Date().getTime() / 1000){
|
||||
status: "Active",
|
||||
};
|
||||
if (
|
||||
nwc.data.expires_at > 0 &&
|
||||
nwc.data.expires_at < new Date().getTime() / 1000
|
||||
) {
|
||||
nwcTableEntry.status = "Expired";
|
||||
}
|
||||
for (const budget of nwc.budgets) {
|
||||
const createdAt = Quasar.utils.date.formatDate(
|
||||
new Date(budget.created_at * 1000),
|
||||
'YYYY-MM-DD HH:mm'
|
||||
"YYYY-MM-DD HH:mm",
|
||||
);
|
||||
let refreshWindow = budget.refresh_window;
|
||||
if (refreshWindow <= 0) {
|
||||
refreshWindow = 'Never';
|
||||
refreshWindow = "Never";
|
||||
} else if (refreshWindow == 60 * 60 * 24) {
|
||||
refreshWindow = 'Daily';
|
||||
refreshWindow = "Daily";
|
||||
} else if (refreshWindow == 60 * 60 * 24 * 7) {
|
||||
refreshWindow = 'Weekly';
|
||||
refreshWindow = "Weekly";
|
||||
} else if (refreshWindow == 60 * 60 * 24 * 30) {
|
||||
refreshWindow = 'Monthly';
|
||||
refreshWindow = "Monthly";
|
||||
} else if (refreshWindow == 60 * 60 * 24 * 365) {
|
||||
refreshWindow = 'Yearly';
|
||||
refreshWindow = "Yearly";
|
||||
}
|
||||
nwcTableEntry.budgets.push({
|
||||
budget_sats: budget.budget_msats / 1000,
|
||||
used_budget_sats: budget.used_budget_msats / 1000,
|
||||
created_at: createdAt,
|
||||
refresh_window: refreshWindow
|
||||
refresh_window: refreshWindow,
|
||||
});
|
||||
}
|
||||
newTableEntries.push(nwcTableEntry);
|
||||
}
|
||||
this.nwcEntries = newTableEntries;
|
||||
|
||||
},
|
||||
closePairingDialog() {
|
||||
this.pairingDialog.show = false;
|
||||
},
|
||||
async showPairingDialog(secret) {
|
||||
let response = await LNbits.api.request(
|
||||
'GET',
|
||||
'/nwcprovider/api/v1/pairing/{SECRET}'
|
||||
"GET",
|
||||
"/nwcprovider/api/v1/pairing/{SECRET}",
|
||||
);
|
||||
response = response.data;
|
||||
response = response.replace("{SECRET}", secret);
|
||||
|
|
@ -550,70 +721,79 @@
|
|||
// timestamp
|
||||
let expires_at = 0;
|
||||
if (!this.connectDialog.data.neverExpires) {
|
||||
expires_at = new Date(this.connectDialog.data.expires_at).getTime() / 1000;
|
||||
expires_at =
|
||||
new Date(this.connectDialog.data.expires_at).getTime() / 1000;
|
||||
}
|
||||
const data = {
|
||||
permissions: [],
|
||||
description: this.connectDialog.data.description,
|
||||
expires_at: expires_at,
|
||||
budgets: []
|
||||
budgets: [],
|
||||
};
|
||||
for (const permission of this.connectDialog.data.permissions) {
|
||||
if (permission.value) data.permissions.push(permission.key);
|
||||
}
|
||||
for (const budget of this.connectDialog.data.budgets) {
|
||||
const budget_msats = budget.budget_sats * 1000;
|
||||
let refresh_window = 0
|
||||
let refresh_window = 0;
|
||||
switch (budget.expiry) {
|
||||
case 'Daily':
|
||||
case "Daily":
|
||||
refresh_window = 60 * 60 * 24;
|
||||
break;
|
||||
case 'Weekly':
|
||||
case "Weekly":
|
||||
refresh_window = 60 * 60 * 24 * 7;
|
||||
break;
|
||||
case 'Monthly':
|
||||
case "Monthly":
|
||||
refresh_window = 60 * 60 * 24 * 30;
|
||||
break;
|
||||
case 'Yearly':
|
||||
case "Yearly":
|
||||
refresh_window = 60 * 60 * 24 * 365;
|
||||
break;
|
||||
case 'Never':
|
||||
case "Never":
|
||||
refresh_window = 0;
|
||||
break;
|
||||
}
|
||||
data.budgets.push({
|
||||
budget_msats: budget_msats,
|
||||
refresh_window: refresh_window,
|
||||
created_at: new Date(new Date().setHours(0, 0, 0, 0)).getTime() / 1000,
|
||||
created_at:
|
||||
new Date(new Date().setHours(0, 0, 0, 0)).getTime() / 1000,
|
||||
});
|
||||
}
|
||||
const wallet = this.getWallet();
|
||||
|
||||
try {
|
||||
const response = await LNbits.api
|
||||
.request('PUT', '/nwcprovider/api/v1/nwc/' + keyPair.pubKey, wallet.adminkey, data);
|
||||
this.closeConnectDialog()
|
||||
if (!response.data || !response.data.data || !response.data.data.pubkey) {
|
||||
const response = await LNbits.api.request(
|
||||
"PUT",
|
||||
"/nwcprovider/api/v1/nwc/" + keyPair.pubKey,
|
||||
wallet.adminkey,
|
||||
data,
|
||||
);
|
||||
this.closeConnectDialog();
|
||||
if (
|
||||
!response.data ||
|
||||
!response.data.data ||
|
||||
!response.data.data.pubkey
|
||||
) {
|
||||
LNbits.utils.notifyApiError("Error creating nwc pairing");
|
||||
return;
|
||||
|
||||
}
|
||||
this.showPairingDialog(keyPair.privKey);
|
||||
} catch (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
LNbits.utils.notifyApiError(error);
|
||||
}
|
||||
this.loadNwcs();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
created: function () {
|
||||
this.loadNwcs()
|
||||
this.loadNwcs();
|
||||
},
|
||||
watch: {
|
||||
selectedWallet(newValue, oldValue) {
|
||||
this.loadNwcs();
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue