FEAT: use versioning for frontend (npm) and copy it to lnbits/static/vendor for easier updating (#1590)

* remove static/vendor

* add node dependencies

* add bolt11-decoder

* run npm install inside dockerimage

* only use bundle.js and bundle.css

* use node_modules for bundling vendor assets

* remove dead code

* make argument optional

* reintroduce vendor dir

* reintroduce vendor and single javascript files, minification

* wrong moment, remove minification

* lock packages with non critical issues

* black
This commit is contained in:
dni ⚡
2023-03-31 12:46:24 +02:00
committed by GitHub
parent 43c9c9754b
commit a9bdf24425
34 changed files with 49381 additions and 236 deletions
+20776
View File
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
/*
* DOM element rendering detection
* https://davidwalsh.name/detect-node-insertion
*/
@keyframes chartjs-render-animation {
from { opacity: 0.99; }
to { opacity: 1; }
}
.chartjs-render-monitor {
animation: chartjs-render-animation 0.001s;
}
/*
* DOM element resizing detection
* https://github.com/marcj/css-element-queries
*/
.chartjs-size-monitor,
.chartjs-size-monitor-expand,
.chartjs-size-monitor-shrink {
position: absolute;
direction: ltr;
left: 0;
top: 0;
right: 0;
bottom: 0;
overflow: hidden;
pointer-events: none;
visibility: hidden;
z-index: -1;
}
.chartjs-size-monitor-expand > div {
position: absolute;
width: 1000000px;
height: 1000000px;
left: 0;
top: 0;
}
.chartjs-size-monitor-shrink > div {
position: absolute;
width: 200%;
height: 200%;
left: 0;
top: 0;
}
+2964
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
-347
View File
@@ -1,347 +0,0 @@
//TODO - A reader MUST check that the signature is valid (see the n tagged field)
//TODO - Tagged part of type f: the fallback on-chain address should be decoded into an address format
//TODO - A reader MUST check that the SHA-2 256 in the h field exactly matches the hashed description.
//TODO - A reader MUST use the n field to validate the signature instead of performing signature recovery if a valid n field is provided.
function decode(paymentRequest) {
let input = paymentRequest.toLowerCase()
let splitPosition = input.lastIndexOf('1')
let humanReadablePart = input.substring(0, splitPosition)
let data = input.substring(splitPosition + 1, input.length - 6)
let checksum = input.substring(input.length - 6, input.length)
if (
!verify_checksum(humanReadablePart, bech32ToFiveBitArray(data + checksum))
) {
throw 'Malformed request: checksum is incorrect' // A reader MUST fail if the checksum is incorrect.
}
return {
human_readable_part: decodeHumanReadablePart(humanReadablePart),
data: decodeData(data, humanReadablePart),
checksum: checksum
}
}
function decodeHumanReadablePart(humanReadablePart) {
let prefixes = ['lnbc', 'lntb', 'lnbcrt', 'lnsb']
let prefix
prefixes.forEach(value => {
if (humanReadablePart.substring(0, value.length) === value) {
prefix = value
}
})
if (prefix == null) throw 'Malformed request: unknown prefix' // A reader MUST fail if it does not understand the prefix.
let amount = decodeAmount(
humanReadablePart.substring(prefix.length, humanReadablePart.length)
)
return {
prefix: prefix,
amount: amount
}
}
function decodeData(data, humanReadablePart) {
let date32 = data.substring(0, 7)
let dateEpoch = bech32ToInt(date32)
let signature = data.substring(data.length - 104, data.length)
let tagData = data.substring(7, data.length - 104)
let decodedTags = decodeTags(tagData)
let value = bech32ToFiveBitArray(date32 + tagData)
value = fiveBitArrayTo8BitArray(value, true)
value = textToHexString(humanReadablePart).concat(byteArrayToHexString(value))
return {
time_stamp: dateEpoch,
tags: decodedTags,
signature: decodeSignature(signature),
signing_data: value
}
}
function decodeSignature(signature) {
let data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(signature))
let recoveryFlag = data[data.length - 1]
let r = byteArrayToHexString(data.slice(0, 32))
let s = byteArrayToHexString(data.slice(32, data.length - 1))
return {
r: r,
s: s,
recovery_flag: recoveryFlag
}
}
function decodeAmount(str) {
let multiplier = str.charAt(str.length - 1)
let amount = str.substring(0, str.length - 1)
if (amount.substring(0, 1) === '0') {
throw 'Malformed request: amount cannot contain leading zeros'
}
amount = Number(amount)
if (amount < 0 || !Number.isInteger(amount)) {
throw 'Malformed request: amount must be a positive decimal integer' // A reader SHOULD fail if amount contains a non-digit
}
switch (multiplier) {
case '':
return 'Any amount' // A reader SHOULD indicate if amount is unspecified
case 'p':
return amount / 10
case 'n':
return amount * 100
case 'u':
return amount * 100000
case 'm':
return amount * 100000000
default:
// A reader SHOULD fail if amount is followed by anything except a defined multiplier.
throw 'Malformed request: undefined amount multiplier'
}
}
function decodeTags(tagData) {
let tags = extractTags(tagData)
let decodedTags = []
tags.forEach(value =>
decodedTags.push(decodeTag(value.type, value.length, value.data))
)
return decodedTags
}
function extractTags(str) {
let tags = []
while (str.length > 0) {
let type = str.charAt(0)
let dataLength = bech32ToInt(str.substring(1, 3))
let data = str.substring(3, dataLength + 3)
tags.push({
type: type,
length: dataLength,
data: data
})
str = str.substring(3 + dataLength, str.length)
}
return tags
}
function decodeTag(type, length, data) {
switch (type) {
case 'p':
if (length !== 52) break // A reader MUST skip over a 'p' field that does not have data_length 52
return {
type: type,
length: length,
description: 'payment_hash',
value: byteArrayToHexString(
fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))
)
}
case 'd':
return {
type: type,
length: length,
description: 'description',
value: bech32ToUTF8String(data)
}
case 'n':
if (length !== 53) break // A reader MUST skip over a 'n' field that does not have data_length 53
return {
type: type,
length: length,
description: 'payee_public_key',
value: byteArrayToHexString(
fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))
)
}
case 'h':
if (length !== 52) break // A reader MUST skip over a 'h' field that does not have data_length 52
return {
type: type,
length: length,
description: 'description_hash',
value: data
}
case 'x':
return {
type: type,
length: length,
description: 'expiry',
value: bech32ToInt(data)
}
case 'c':
return {
type: type,
length: length,
description: 'min_final_cltv_expiry',
value: bech32ToInt(data)
}
case 'f':
let version = bech32ToFiveBitArray(data.charAt(0))[0]
if (version < 0 || version > 18) break // a reader MUST skip over an f field with unknown version.
data = data.substring(1, data.length)
return {
type: type,
length: length,
description: 'fallback_address',
value: {
version: version,
fallback_address: data
}
}
case 'r':
data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data))
let pubkey = data.slice(0, 33)
let shortChannelId = data.slice(33, 41)
let feeBaseMsat = data.slice(41, 45)
let feeProportionalMillionths = data.slice(45, 49)
let cltvExpiryDelta = data.slice(49, 51)
return {
type: type,
length: length,
description: 'routing_information',
value: {
public_key: byteArrayToHexString(pubkey),
short_channel_id: byteArrayToHexString(shortChannelId),
fee_base_msat: byteArrayToInt(feeBaseMsat),
fee_proportional_millionths: byteArrayToInt(
feeProportionalMillionths
),
cltv_expiry_delta: byteArrayToInt(cltvExpiryDelta)
}
}
default:
// reader MUST skip over unknown fields
}
}
function polymod(values) {
let GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
let chk = 1
values.forEach(value => {
let b = chk >> 25
chk = ((chk & 0x1ffffff) << 5) ^ value
for (let i = 0; i < 5; i++) {
if (((b >> i) & 1) === 1) {
chk ^= GEN[i]
} else {
chk ^= 0
}
}
})
return chk
}
function expand(str) {
let array = []
for (let i = 0; i < str.length; i++) {
array.push(str.charCodeAt(i) >> 5)
}
array.push(0)
for (let i = 0; i < str.length; i++) {
array.push(str.charCodeAt(i) & 31)
}
return array
}
function verify_checksum(hrp, data) {
hrp = expand(hrp)
let all = hrp.concat(data)
let bool = polymod(all)
return bool === 1
}
const bech32CharValues = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
function byteArrayToInt(byteArray) {
let value = 0
for (let i = 0; i < byteArray.length; ++i) {
value = (value << 8) + byteArray[i]
}
return value
}
function bech32ToInt(str) {
let sum = 0
for (let i = 0; i < str.length; i++) {
sum = sum * 32
sum = sum + bech32CharValues.indexOf(str.charAt(i))
}
return sum
}
function bech32ToFiveBitArray(str) {
let array = []
for (let i = 0; i < str.length; i++) {
array.push(bech32CharValues.indexOf(str.charAt(i)))
}
return array
}
function fiveBitArrayTo8BitArray(int5Array, includeOverflow) {
let count = 0
let buffer = 0
let byteArray = []
int5Array.forEach(value => {
buffer = (buffer << 5) + value
count += 5
if (count >= 8) {
byteArray.push((buffer >> (count - 8)) & 255)
count -= 8
}
})
if (includeOverflow && count > 0) {
byteArray.push((buffer << (8 - count)) & 255)
}
return byteArray
}
function bech32ToUTF8String(str) {
let int5Array = bech32ToFiveBitArray(str)
let byteArray = fiveBitArrayTo8BitArray(int5Array)
let utf8String = ''
for (let i = 0; i < byteArray.length; i++) {
utf8String += '%' + ('0' + byteArray[i].toString(16)).slice(-2)
}
return decodeURIComponent(utf8String)
}
function byteArrayToHexString(byteArray) {
return Array.prototype.map
.call(byteArray, function (byte) {
return ('0' + (byte & 0xff).toString(16)).slice(-2)
})
.join('')
}
function textToHexString(text) {
let hexString = ''
for (let i = 0; i < text.length; i++) {
hexString += text.charCodeAt(i).toString(16)
}
return hexString
}
function epochToDate(int) {
let date = new Date(int * 1000)
return date.toUTCString()
}
function isEmptyOrSpaces(str) {
return str === null || str.match(/^ *$/) !== null
}
function toFixed(x) {
if (Math.abs(x) < 1.0) {
var e = parseInt(x.toString().split('e-')[1])
if (e) {
x *= Math.pow(10, e - 1)
x = '0.' + new Array(e).join('0') + x.toString().substring(2)
}
} else {
var e = parseInt(x.toString().split('+')[1])
if (e > 20) {
e -= 20
x /= Math.pow(10, e)
x += new Array(e + 1).join('0')
}
}
return x
}
-1
View File
@@ -1 +0,0 @@
@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}
File diff suppressed because one or more lines are too long
+5685
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+11680
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2042
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+5509
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long