lnbits-legend/lnbits/static/js/base.js

450 lines
12 KiB
JavaScript
Raw Normal View History

2020-10-13 18:57:26 +02:00
/* globals crypto, moment, Vue, axios, Quasar, _ */
2020-10-13 18:57:26 +02:00
window.LOCALE = 'en'
window.EventHub = new Vue()
window.LNbits = {
api: {
request: function (method, url, apiKey, data) {
return axios({
method: method,
url: url,
headers: {
2020-04-16 17:27:36 +02:00
'X-Api-Key': apiKey
},
data: data
2020-05-03 18:55:17 +02:00
})
},
createInvoice: async function (
wallet,
amount,
memo,
unit = 'sat',
lnurlCallback = null
) {
return this.request('post', '/api/v1/payments', wallet.inkey, {
out: false,
amount: amount,
memo: memo,
unit: unit,
lnurl_callback: lnurlCallback
2020-05-03 18:55:17 +02:00
})
},
payInvoice: function (wallet, bolt11) {
2020-04-01 22:18:46 +02:00
return this.request('post', '/api/v1/payments', wallet.adminkey, {
out: true,
bolt11: bolt11
2020-05-03 18:55:17 +02:00
})
},
payLnurl: function (
wallet,
callback,
description_hash,
amount,
2020-10-13 20:18:34 +02:00
description = '',
comment = ''
) {
return this.request('post', '/api/v1/payments/lnurl', wallet.adminkey, {
callback,
description_hash,
amount,
2020-10-13 20:18:34 +02:00
comment,
description
})
},
authLnurl: function (wallet, callback) {
return this.request('post', '/api/v1/lnurlauth', wallet.adminkey, {
callback
})
},
getWallet: function (wallet) {
return this.request('get', '/api/v1/wallet', wallet.inkey)
},
getPayments: function (wallet) {
return this.request('get', '/api/v1/payments', wallet.inkey)
},
getPayment: function (wallet, paymentHash) {
return this.request(
'get',
'/api/v1/payments/' + paymentHash,
wallet.inkey
)
}
},
events: {
onInvoicePaid: function (wallet, cb) {
let listener = ev => {
cb(JSON.parse(ev.data))
}
this.listenersCount = this.listenersCount || {[wallet.inkey]: 0}
this.listenersCount[wallet.inkey]++
this.listeners = this.listeners || {}
if (!(wallet.inkey in this.listeners)) {
this.listeners[wallet.inkey] = new EventSource(
'/api/v1/payments/sse?api-key=' + wallet.inkey
)
}
this.listeners[wallet.inkey].addEventListener(
'payment-received',
listener
)
return () => {
this.listeners[wallet.inkey].removeEventListener(
'payment-received',
listener
)
this.listenersCount[wallet.inkey]--
if (this.listenersCount[wallet.inkey] <= 0) {
this.listeners[wallet.inkey].close()
delete this.listeners[wallet.inkey]
}
}
}
},
href: {
createWallet: function (walletName, userId) {
2020-05-03 18:55:17 +02:00
window.location.href =
'/wallet?' + (userId ? 'usr=' + userId + '&' : '') + 'nme=' + walletName
},
2021-08-06 12:41:08 +02:00
updateWallet: function (walletName, userId, walletId) {
2022-02-07 21:43:47 +01:00
window.location.href = `/wallet?usr=${userId}&wal=${walletId}&nme=${walletName}`
2021-08-06 12:41:08 +02:00
},
deleteWallet: function (walletId, userId) {
2020-05-03 18:55:17 +02:00
window.location.href = '/deletewallet?usr=' + userId + '&wal=' + walletId
}
},
map: {
extension: function (data) {
2020-05-03 18:55:17 +02:00
var obj = _.object(
2021-03-28 03:10:24 +02:00
[
'code',
'isValid',
2022-02-07 21:43:47 +01:00
'isAdminOnly',
2021-03-28 03:10:24 +02:00
'name',
'shortDescription',
'icon',
'contributors',
'hidden'
],
2020-05-03 18:55:17 +02:00
data
)
obj.url = ['/', obj.code, '/'].join('')
return obj
},
user: function (data) {
2022-02-07 21:43:47 +01:00
var obj = {
id: data.id,
email: data.email,
extensions: data.extensions,
wallets: data.wallets
}
2020-05-03 18:55:17 +02:00
var mapWallet = this.wallet
obj.wallets = obj.wallets
.map(function (obj) {
2020-05-03 18:55:17 +02:00
return mapWallet(obj)
})
.sort(function (a, b) {
2020-05-03 18:55:17 +02:00
return a.name.localeCompare(b.name)
})
obj.walletOptions = obj.wallets.map(function (obj) {
2020-04-05 12:19:25 +02:00
return {
2020-04-18 23:17:16 +02:00
label: [obj.name, ' - ', obj.id].join(''),
2020-04-05 12:19:25 +02:00
value: obj.id
2020-05-03 18:55:17 +02:00
}
})
return obj
},
wallet: function (data) {
2022-02-07 21:43:47 +01:00
newWallet = {
id: data.id,
name: data.name,
adminkey: data.adminkey,
inkey: data.inkey
}
newWallet.msat = data.balance_msat
newWallet.sat = Math.round(data.balance_msat / 1000)
2022-02-07 21:43:47 +01:00
newWallet.fsat = new Intl.NumberFormat(window.LOCALE).format(
newWallet.sat
)
newWallet.url = ['/wallet?usr=', data.user, '&wal=', data.id].join('')
return newWallet
},
payment: function (data) {
2021-08-29 19:38:42 +02:00
obj = {
2022-02-07 21:43:47 +01:00
checking_id: data.id,
2021-08-29 19:38:42 +02:00
pending: data.pending,
amount: data.amount,
fee: data.fee,
memo: data.memo,
time: data.time,
bolt11: data.bolt11,
preimage: data.preimage,
payment_hash: data.payment_hash,
extra: data.extra,
wallet_id: data.wallet_id,
webhook: data.webhook,
2022-02-07 21:43:47 +01:00
webhook_status: data.webhook_status
}
2021-08-29 19:38:42 +02:00
2020-05-03 18:55:17 +02:00
obj.date = Quasar.utils.date.formatDate(
new Date(obj.time * 1000),
'YYYY-MM-DD HH:mm'
)
obj.dateFrom = moment(obj.date).fromNow()
2020-05-03 18:55:17 +02:00
obj.msat = obj.amount
obj.sat = obj.msat / 1000
obj.tag = obj.extra.tag
2020-10-13 18:57:26 +02:00
obj.fsat = new Intl.NumberFormat(window.LOCALE).format(obj.sat)
2020-05-03 18:55:17 +02:00
obj.isIn = obj.amount > 0
obj.isOut = obj.amount < 0
2021-06-22 04:22:52 +02:00
obj.isPaid = !obj.pending
2020-05-03 18:55:17 +02:00
obj._q = [obj.memo, obj.sat].join(' ').toLowerCase()
return obj
}
},
utils: {
confirmDialog: function (msg) {
return Quasar.plugins.Dialog.create({
message: msg,
ok: {
flat: true,
color: 'orange'
},
cancel: {
flat: true,
color: 'grey'
}
2020-05-03 18:55:17 +02:00
})
},
formatCurrency: function (value, currency) {
2020-10-13 18:57:26 +02:00
return new Intl.NumberFormat(window.LOCALE, {
2020-05-03 18:55:17 +02:00
style: 'currency',
currency: currency
}).format(value)
},
formatSat: function (value) {
2020-10-13 18:57:26 +02:00
return new Intl.NumberFormat(window.LOCALE).format(value)
},
notifyApiError: function (error) {
var types = {
400: 'warning',
401: 'warning',
500: 'negative'
2020-05-03 18:55:17 +02:00
}
Quasar.plugins.Notify.create({
timeout: 5000,
type: types[error.response.status] || 'warning',
2022-02-07 21:43:47 +01:00
message:
error.response.data.message || error.response.data.detail || null,
2020-05-03 18:55:17 +02:00
caption:
[error.response.status, ' ', error.response.statusText]
.join('')
.toUpperCase() || null,
icon: null
2020-05-03 18:55:17 +02:00
})
2020-04-01 22:18:46 +02:00
},
search: function (data, q, field, separator) {
try {
2020-05-03 18:55:17 +02:00
var queries = q.toLowerCase().split(separator || ' ')
return data.filter(function (obj) {
2020-05-03 18:55:17 +02:00
var matches = 0
_.each(queries, function (q) {
2020-05-03 18:55:17 +02:00
if (obj[field].indexOf(q) !== -1) matches++
})
return matches === queries.length
2020-05-03 18:55:17 +02:00
})
} catch (err) {
2020-05-03 18:55:17 +02:00
return data
}
},
exportCSV: function (columns, data) {
var wrapCsvValue = function (val, formatFn) {
2020-05-03 18:55:17 +02:00
var formatted = formatFn !== void 0 ? formatFn(val) : val
2020-04-01 22:18:46 +02:00
2020-05-03 18:55:17 +02:00
formatted =
formatted === void 0 || formatted === null ? '' : String(formatted)
2020-04-01 22:18:46 +02:00
2020-05-03 18:55:17 +02:00
formatted = formatted.split('"').join('""')
2020-04-01 22:18:46 +02:00
2020-05-03 18:55:17 +02:00
return `"${formatted}"`
2020-04-01 22:18:46 +02:00
}
2020-05-03 18:55:17 +02:00
var content = [
columns.map(function (col) {
2020-05-03 18:55:17 +02:00
return wrapCsvValue(col.label)
})
]
.concat(
data.map(function (row) {
2020-05-03 18:55:17 +02:00
return columns
.map(function (col) {
2020-05-03 18:55:17 +02:00
return wrapCsvValue(
typeof col.field === 'function'
? col.field(row)
: row[col.field === void 0 ? col.name : col.field],
col.format
)
})
.join(',')
})
)
.join('\r\n')
2020-04-01 22:18:46 +02:00
2020-05-03 18:55:17 +02:00
var status = Quasar.utils.exportFile(
'table-export.csv',
content,
'text/csv'
)
2020-04-01 22:18:46 +02:00
if (status !== true) {
Quasar.plugins.Notify.create({
message: 'Browser denied file download...',
color: 'negative',
icon: null
2020-05-03 18:55:17 +02:00
})
2020-04-01 22:18:46 +02:00
}
}
}
2020-05-03 18:55:17 +02:00
}
2020-10-13 18:57:26 +02:00
window.windowMixin = {
data: function () {
return {
g: {
2022-07-04 21:03:21 +02:00
offline: !navigator.onLine,
visibleDrawer: false,
extensions: [],
user: null,
wallet: null,
payments: [],
allowedThemes: null
}
2020-05-03 18:55:17 +02:00
}
},
2021-06-30 14:38:10 +02:00
methods: {
2021-06-30 14:38:10 +02:00
changeColor: function (newValue) {
document.body.setAttribute('data-theme', newValue)
2021-06-30 13:40:58 +02:00
this.$q.localStorage.set('lnbits.theme', newValue)
},
toggleDarkMode: function () {
2020-05-03 18:55:17 +02:00
this.$q.dark.toggle()
this.$q.localStorage.set('lnbits.darkMode', this.$q.dark.isActive)
},
copyText: function (text, message, position) {
2020-05-03 18:55:17 +02:00
var notify = this.$q.notify
Quasar.utils.copyToClipboard(text).then(function () {
2020-05-03 18:55:17 +02:00
notify({
message: message || 'Copied to clipboard!',
position: position || 'bottom'
})
})
}
},
created: function () {
if (
this.$q.localStorage.getItem('lnbits.darkMode') == true ||
this.$q.localStorage.getItem('lnbits.darkMode') == false
) {
2022-02-17 22:34:36 +01:00
this.$q.dark.set(this.$q.localStorage.getItem('lnbits.darkMode'))
} else {
2022-02-17 22:34:36 +01:00
this.$q.dark.set(true)
}
this.g.allowedThemes = window.allowedThemes ?? ['bitcoin']
2021-07-01 12:54:45 +02:00
2022-07-06 00:16:46 +02:00
addEventListener('offline', event => {
2022-07-04 21:03:21 +02:00
this.g.offline = true
})
addEventListener('online', event => {
this.g.offline = false
})
2021-07-01 12:54:45 +02:00
// failsafe if admin changes themes halfway
if (!this.$q.localStorage.getItem('lnbits.theme')) {
2022-02-17 22:34:36 +01:00
this.changeColor(this.g.allowedThemes[0])
}
2021-07-01 13:34:50 +02:00
if (
this.$q.localStorage.getItem('lnbits.theme') &&
!this.g.allowedThemes.includes(
this.$q.localStorage.getItem('lnbits.theme')
)
) {
this.changeColor(this.g.allowedThemes[0])
2021-06-30 13:40:58 +02:00
}
2021-07-01 12:54:45 +02:00
2021-07-01 13:34:50 +02:00
if (this.$q.localStorage.getItem('lnbits.theme')) {
document.body.setAttribute(
'data-theme',
this.$q.localStorage.getItem('lnbits.theme')
)
2021-07-01 12:54:45 +02:00
}
if (window.user) {
2020-10-13 18:57:26 +02:00
this.g.user = Object.freeze(window.LNbits.map.user(window.user))
}
if (window.wallet) {
2020-10-13 18:57:26 +02:00
this.g.wallet = Object.freeze(window.LNbits.map.wallet(window.wallet))
}
if (window.extensions) {
2020-05-03 18:55:17 +02:00
var user = this.g.user
const extensions = Object.freeze(
2020-05-03 18:55:17 +02:00
window.extensions
.map(function (data) {
2020-10-13 18:57:26 +02:00
return window.LNbits.map.extension(data)
2020-05-03 18:55:17 +02:00
})
2021-03-28 03:10:24 +02:00
.filter(function (obj) {
return !obj.hidden
})
2022-02-07 21:43:47 +01:00
.filter(function (obj) {
if (window.user.admin) return obj
return !obj.isAdminOnly
})
.map(function (obj) {
2020-05-03 18:55:17 +02:00
if (user) {
obj.isEnabled = user.extensions.indexOf(obj.code) !== -1
2020-05-03 18:55:17 +02:00
} else {
obj.isEnabled = false
}
return obj
})
.sort(function (a, b) {
const nameA = a.name.toUpperCase()
const nameB = b.name.toUpperCase()
return nameA < nameB ? -1 : nameA > nameB ? 1 : 0
2020-05-03 18:55:17 +02:00
})
)
this.g.extensions = extensions
}
}
2020-05-03 18:55:17 +02:00
}
2020-10-13 18:57:26 +02:00
window.decryptLnurlPayAES = function (success_action, preimage) {
let keyb = new Uint8Array(
preimage.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16))
)
return crypto.subtle
.importKey('raw', keyb, {name: 'AES-CBC', length: 256}, false, ['decrypt'])
.then(key => {
let ivb = Uint8Array.from(window.atob(success_action.iv), c =>
c.charCodeAt(0)
)
let ciphertextb = Uint8Array.from(
window.atob(success_action.ciphertext),
c => c.charCodeAt(0)
)
return crypto.subtle.decrypt({name: 'AES-CBC', iv: ivb}, key, ciphertextb)
})
.then(valueb => {
let decoder = new TextDecoder('utf-8')
return decoder.decode(valueb)
})
2022-07-06 00:16:46 +02:00
}