code quality fixes and cleanup

This commit is contained in:
Riccardo Balbo 2024-09-07 09:09:22 +00:00
commit 2bbbcd4d65
32 changed files with 4943 additions and 1368 deletions

View file

@ -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 %}

View file

@ -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 %}