* 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!
385 lines
11 KiB
JavaScript
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()
|
|
}
|
|
}
|
|
})
|