nwcprovider/static/js/index.js
dni ⚡ 603b345073
Some checks failed
/ release (push) Has been cancelled
Tests / test (push) Has been cancelled
/ pullrequest (push) Has been cancelled
fix: use lnbits linting, change to uv (#9)
* fix: use lnbits linting, change to uv

- formatting
- prettier
- pyright

* dont ignore packagejson

* readd poetry lock for integration tests

* fix pyright

* fix mypy

* fix test?

* fix lnbits version

* fixup tests

* fixup!
2025-09-12 09:30:16 +02:00

385 lines
11 KiB
JavaScript

window.app = Vue.createApp({
el: '#vue',
mixins: [windowMixin],
delimiters: ['${', '}'],
data: function () {
return {
selectedWallet: null,
nodePermissions: [],
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'
}
],
pagination: {
rowsPerPage: 10
}
},
connectDialog: {
show: false,
data: {}
},
pairingDialog: {
show: false,
data: {
pairingUrl: ''
}
},
pairingQrDialog: {
show: false,
data: {
pairingUrl: ''
}
},
connectionInfoDialog: {
show: false,
data: {}
}
}
},
methods: {
showConnectDialog() {
const wallet = this.getWallet()
if (!wallet) {
Quasar.Notify.create({
type: 'negative',
message: 'Please select a wallet first'
})
return
} else {
this.connectDialog.show = true
}
},
openConnectionInfoDialog(data) {
this.connectionInfoDialog.data = data
this.connectionInfoDialog.show = true
},
closeConnectionInfoDialog() {
this.connectionInfoDialog.show = false
},
openPairingUrl() {
const url = this.pairingDialog.data.pairingUrl
if (url) window.open(url, '_blank')
},
go(url) {
window.open(url, '_blank')
},
async copyPairingUrl() {
const url = this.pairingDialog.data.pairingUrl
if (url) {
try {
await navigator.clipboard.writeText(url)
Quasar.Notify.create({
type: 'positive',
message: 'URL copied to clipboard'
})
} catch (err) {
Quasar.Notify.create({
type: 'negative',
message: 'Failed to copy URL.'
})
}
}
},
showPairingQR() {
this.pairingQrDialog.data.pairingUrl = this.pairingDialog.data.pairingUrl
this.pairingQrDialog.show = true
},
closePairingQrDialog() {
this.pairingQrDialog.show = false
},
loadConnectDialogData() {
this.connectDialog.data = {
description: '',
expires_at: Date.now() + 1000 * 60 * 60 * 24 * 7,
neverExpires: true,
permissions: [],
budgets: []
}
for (const permission of this.nodePermissions) {
this.connectDialog.data.permissions.push({
key: permission.key,
name: permission.name,
value: permission.value
})
}
},
deleteBudget(index) {
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'
})
},
closeConnectDialog() {
this.connectDialog.show = false
this.loadConnectDialogData()
},
getWallet: function () {
let wallet = undefined
for (let i = 0; i < this.g.user.wallets.length; i++) {
if (this.g.user.wallets[i].id == this.selectedWallet) {
wallet = this.g.user.wallets[i]
break
}
}
return wallet
},
async generateKeyPair() {
while (!window.NobleSecp256k1) {
await new Promise(resolve => setTimeout(resolve, 1))
}
const privKeyBytes = window.NobleSecp256k1.utils.randomPrivateKey()
const pubKeyBytes = window.NobleSecp256k1.getPublicKey(privKeyBytes)
const out = {
privKeyBytes: privKeyBytes,
pubKeyBytes: pubKeyBytes,
privKey: window.NobleSecp256k1.etc.bytesToHex(privKeyBytes),
pubKey: window.NobleSecp256k1.etc.bytesToHex(pubKeyBytes.slice(1))
}
return out
},
deleteNWC: async function (pubkey) {
Quasar.Dialog.create({
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()
Quasar.Notify.create({
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?include_expired=true&calculate_spent_budget=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
)
const permissions = []
for (const [key, value] of Object.entries(response.data)) {
permissions.push({
key: key,
name: value.name,
value: value.default
})
}
this.nodePermissions = permissions
} catch (error) {
Lnbits.utils.notifyApiError(error)
}
this.loadConnectDialogData()
const newTableEntries = []
for (const nwc of this.nwcs) {
const t = Quasar.date.formatDate(
new Date(nwc.data.created_at * 1000),
'YYYY-MM-DD HH:mm'
)
const e =
nwc.data.expires_at > 0
? Quasar.date.formatDate(
new Date(nwc.data.expires_at * 1000),
'YYYY-MM-DD HH:mm'
)
: 'Never'
const l = Quasar.date.formatDate(
new Date(nwc.data.last_used * 1000),
'YYYY-MM-DD HH:mm'
)
const nwcTableEntry = {
description: nwc.data.description,
created_at: t,
expires_at: e,
last_used: l,
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
) {
nwcTableEntry.status = 'Expired'
}
for (const budget of nwc.budgets) {
const createdAt = Quasar.date.formatDate(
new Date(budget.created_at * 1000),
'YYYY-MM-DD HH:mm'
)
let refreshWindow = budget.refresh_window
if (refreshWindow <= 0) {
refreshWindow = 'Never'
} else if (refreshWindow == 60 * 60 * 24) {
refreshWindow = 'Daily'
} else if (refreshWindow == 60 * 60 * 24 * 7) {
refreshWindow = 'Weekly'
} else if (refreshWindow == 60 * 60 * 24 * 30) {
refreshWindow = 'Monthly'
} else if (refreshWindow == 60 * 60 * 24 * 365) {
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
})
}
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}'
)
response = response.data
response = response.replace('{SECRET}', secret)
this.pairingDialog.data.pairingUrl = response
this.pairingDialog.show = true
},
async confirmConnectDialog() {
const keyPair = await this.generateKeyPair()
// timestamp
let expires_at = 0
if (!this.connectDialog.data.neverExpires) {
expires_at =
new Date(this.connectDialog.data.expires_at).getTime() / 1000
}
const data = {
permissions: [],
description: this.connectDialog.data.description,
expires_at: expires_at,
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
switch (budget.expiry) {
case 'Daily':
refresh_window = 60 * 60 * 24
break
case 'Weekly':
refresh_window = 60 * 60 * 24 * 7
break
case 'Monthly':
refresh_window = 60 * 60 * 24 * 30
break
case 'Yearly':
refresh_window = 60 * 60 * 24 * 365
break
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
})
}
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
) {
LNbits.utils.notifyApiError('Error creating nwc pairing')
return
}
this.showPairingDialog(keyPair.privKey)
} catch (error) {
LNbits.utils.notifyApiError(error)
}
this.loadNwcs()
}
},
created: function () {
this.loadNwcs()
},
watch: {
selectedWallet(newValue, oldValue) {
this.loadNwcs()
}
}
})