From 6928f431a759c49a990a6a8cc66466cec66c01dc Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Tue, 15 Sep 2020 15:54:05 -0300 Subject: [PATCH] poor man's flask-assets. --- .gitignore | 1 + Makefile | 5 +- lnbits/__main__.py | 5 +- lnbits/app.py | 18 +- lnbits/commands.py | 31 +- lnbits/helpers.py | 52 +++ lnbits/settings.py | 2 +- lnbits/static/css/.gitignore | 1 + lnbits/static/css/base.css | 1 - lnbits/static/vendor/bolt11/decoder.js | 507 +++++++++++++++---------- lnbits/static/vendor/bolt11/utils.js | 96 ----- lnbits/templates/base.html | 31 +- lnbits/templates/print.html | 18 +- 13 files changed, 430 insertions(+), 338 deletions(-) create mode 100644 lnbits/static/css/.gitignore delete mode 100644 lnbits/static/css/base.css delete mode 100644 lnbits/static/vendor/bolt11/utils.js diff --git a/.gitignore b/.gitignore index 08b009f..ca3fcd0 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ venv __bundle__ node_modules +lnbits/static/bundle.* diff --git a/Makefile b/Makefile index a48fa1a..f80747a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -all: format check lnbits/static/css/base.css requirements.txt +all: format check requirements.txt format: prettier black @@ -19,8 +19,5 @@ checkprettier: $(shell find lnbits -name "*.js" -name ".html") checkblack: $(shell find lnbits -name "*.py") ./venv/bin/black --check lnbits -lnbits/static/css/base.css: lnbits/static/scss/base.scss - ./venv/bin/pyscss -o lnbits/static/css/base.css lnbits/static/scss/base.scss - requirements.txt: Pipfile.lock cat Pipfile.lock | jq -r '.default | map_values(.version) | to_entries | map("\(.key)\(.value)") | join("\n")' > requirements.txt diff --git a/lnbits/__main__.py b/lnbits/__main__.py index 2fa0c72..6932fba 100644 --- a/lnbits/__main__.py +++ b/lnbits/__main__.py @@ -1,8 +1,9 @@ from .app import create_app -from .commands import migrate_databases - +from .commands import migrate_databases, transpile_scss, bundle_vendored migrate_databases() +transpile_scss() +bundle_vendored() app = create_app() app.run(host=app.config["HOST"], port=app.config["PORT"]) diff --git a/lnbits/app.py b/lnbits/app.py index 706282e..8bcc5be 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -8,7 +8,7 @@ from secure import SecureHeaders # type: ignore from .commands import db_migrate from .core import core_app from .db import open_db -from .helpers import get_valid_extensions +from .helpers import get_valid_extensions, get_js_vendored, get_css_vendored, url_for_vendored from .proxy_fix import ProxyFix secure_headers = SecureHeaders(hsts=False) @@ -25,6 +25,7 @@ def create_app(config_object="lnbits.settings") -> Quart: Compress(app) ProxyFix(app) + register_assets(app) register_blueprints(app) register_filters(app) register_commands(app) @@ -50,10 +51,23 @@ def register_commands(app): app.cli.add_command(db_migrate) +def register_assets(app): + """Serve each vendored asset separately or a bundle.""" + + @app.before_request + async def vendored_assets_variable(): + if app.config["DEBUG"]: + g.VENDORED_JS = map(url_for_vendored, get_js_vendored()) + g.VENDORED_CSS = map(url_for_vendored, get_css_vendored()) + else: + g.VENDORED_JS = ["/static/bundle.js"] + g.VENDORED_CSS = ["/static/bundle.css"] + + def register_filters(app): """Jinja filters.""" - app.jinja_env.globals["EXTENSIONS"] = get_valid_extensions() app.jinja_env.globals["SITE_TITLE"] = app.config["LNBITS_SITE_TITLE"] + app.jinja_env.globals["EXTENSIONS"] = get_valid_extensions() def register_request_hooks(app): diff --git a/lnbits/commands.py b/lnbits/commands.py index 60170a9..0cc5eff 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -1,11 +1,15 @@ import click import importlib import re +import os import sqlite3 +from scss.compiler import compile_string + from .core import migrations as core_migrations from .db import open_db, open_ext_db -from .helpers import get_valid_extensions +from .helpers import get_valid_extensions, get_css_vendored, get_js_vendored, url_for_vendored +from .settings import LNBITS_PATH @click.command("migrate") @@ -13,6 +17,31 @@ def db_migrate(): migrate_databases() +@click.command("assets") +def handle_assets(): + transpile_scss() + bundle_vendored() + + +def transpile_scss(): + with open(os.path.join(LNBITS_PATH, "static/scss/base.scss")) as scss: + with open(os.path.join(LNBITS_PATH, "static/css/base.css"), "w") as css: + css.write(compile_string(scss.read())) + + +def bundle_vendored(): + for getfiles, outputpath in [ + (get_js_vendored, os.path.join(LNBITS_PATH, "static/bundle.js")), + (get_css_vendored, os.path.join(LNBITS_PATH, "static/bundle.css")), + ]: + output = "" + for path in getfiles(): + with open(path) as f: + output += "/* " + url_for_vendored(path) + " */\n" + f.read() + ";\n" + with open(outputpath, "w") as f: + f.write(output) + + def migrate_databases(): """Creates the necessary databases if they don't exist already; or migrates them.""" diff --git a/lnbits/helpers.py b/lnbits/helpers.py index 63cecff..dec7cb6 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -1,5 +1,6 @@ import json import os +import glob import shortuuid # type: ignore from typing import List, NamedTuple, Optional @@ -54,3 +55,54 @@ def get_valid_extensions() -> List[Extension]: def urlsafe_short_hash() -> str: return shortuuid.uuid() + + +def get_js_vendored(prefer_minified: bool = False) -> List[str]: + paths = get_vendored(".js", prefer_minified) + + def sorter(key: str): + if "moment@" in key: + return 1 + if "vue@" in key: + return 2 + if "vue-router@" in key: + return 3 + if "polyfills" in key: + return 4 + return 9 + + return sorted(paths, key=sorter) + + +def get_css_vendored(prefer_minified: bool = False) -> List[str]: + return get_vendored(".css", prefer_minified) + + +def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]: + paths: List[str] = [] + for path in glob.glob(os.path.join(LNBITS_PATH, "static/vendor/**"), recursive=True): + if path.endswith(".min" + ext): + # path is minified + unminified = path.replace(".min" + ext, ext) + if prefer_minified: + paths.append(path) + if unminified in paths: + paths.remove(unminified) + elif unminified not in paths: + paths.append(path) + + elif path.endswith(ext): + # path is not minified + minified = path.replace(ext, ".min" + ext) + if not prefer_minified: + paths.append(path) + if minified in paths: + paths.remove(minified) + elif minified not in paths: + paths.append(path) + + return paths + + +def url_for_vendored(abspath: str) -> str: + return "/" + os.path.relpath(abspath, LNBITS_PATH) diff --git a/lnbits/settings.py b/lnbits/settings.py index b33356a..983b79e 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -12,7 +12,7 @@ wallets_module = importlib.import_module("lnbits.wallets") wallet_class = getattr(wallets_module, env.str("LNBITS_BACKEND_WALLET_CLASS", default="VoidWallet")) ENV = env.str("QUART_ENV", default="production") -DEBUG = env.bool("QUART_DEBUG") or ENV == "development" +DEBUG = env.bool("QUART_DEBUG", default=False) or ENV == "development" HOST = env.str("HOST", default="127.0.0.1") PORT = env.int("PORT", default=5000) diff --git a/lnbits/static/css/.gitignore b/lnbits/static/css/.gitignore new file mode 100644 index 0000000..d692bf8 --- /dev/null +++ b/lnbits/static/css/.gitignore @@ -0,0 +1 @@ +base.css diff --git a/lnbits/static/css/base.css b/lnbits/static/css/base.css deleted file mode 100644 index 457a081..0000000 --- a/lnbits/static/css/base.css +++ /dev/null @@ -1 +0,0 @@ -[v-cloak]{display:none}.bg-lnbits-dark{background-color:#1f2234}body.body--dark,body.body--dark .q-drawer--dark,body.body--dark .q-menu--dark{background:#1f2234}body.body--dark .q-card--dark{background:#333646}body.body--dark .q-table--dark{background:transparent}body.body--light,body.body--light .q-drawer{background:#f5f5f5}body.body--dark .q-field--error .text-negative,body.body--dark .q-field--error .q-field__messages{color:#ff0 !important}.lnbits-drawer__q-list .q-item{padding-top:5px !important;padding-bottom:5px !important;border-top-right-radius:3px;border-bottom-right-radius:3px}.lnbits-drawer__q-list .q-item.q-item--active{color:inherit;font-weight:bold}.lnbits__dialog-card{width:500px}.q-table--dense th:first-child,.q-table--dense td:first-child,.q-table--dense .q-table__bottom{padding-left:6px !important}.q-table--dense th:last-child,.q-table--dense td:last-child,.q-table--dense .q-table__bottom{padding-right:6px !important}a.inherit{color:inherit;text-decoration:none}video{border-radius:3px}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(../fonts/material-icons-v50.woff2) format('woff2')}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-moz-font-feature-settings:'liga';-moz-osx-font-smoothing:grayscale} diff --git a/lnbits/static/vendor/bolt11/decoder.js b/lnbits/static/vendor/bolt11/decoder.js index 88701ec..7b73260 100644 --- a/lnbits/static/vendor/bolt11/decoder.js +++ b/lnbits/static/vendor/bolt11/decoder.js @@ -4,233 +4,344 @@ //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 - } + 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 + 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 - } + 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 - } + 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 - } + 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'; - } + 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; + 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; + 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 - } + 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; + 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; + 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; -} \ No newline at end of file + 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 +} diff --git a/lnbits/static/vendor/bolt11/utils.js b/lnbits/static/vendor/bolt11/utils.js deleted file mode 100644 index f2b75bc..0000000 --- a/lnbits/static/vendor/bolt11/utils.js +++ /dev/null @@ -1,96 +0,0 @@ -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; -} \ No newline at end of file diff --git a/lnbits/templates/base.html b/lnbits/templates/base.html index b0f2a62..7dce34e 100644 --- a/lnbits/templates/base.html +++ b/lnbits/templates/base.html @@ -2,16 +2,10 @@ - - + {% for url in g.VENDORED_CSS %} + + {% endfor %} + {% block styles %}{% endblock %} @@ -113,18 +107,11 @@ </q-layout> {% block vue_templates %}{% endblock %} - <script src="/static/vendor/vue@2.6.12/vue.js"></script> - <script src="/static/vendor/vuex@3.5.1/vuex.js"></script> - <script src="/static/vendor/vue-router@3.4.3/vue-router.js"></script> - <script src="/static/vendor/quasar@1.13.2/quasar.umd.js"></script> - <script src="/static/vendor/axios@0.20.0/axios.min.js"></script> - <script src="/static/vendor/underscore@1.10.2/underscore.min.js"></script> - <script src="/static/vendor/vue-qrcode@1.0.2/vue-qrcode.min.js"></script> - <script src="/static/vendor/moment@2.27.0/moment.min.js"></script> - <script src="/static/vendor/chart.js@2.9.3/chart.min.js"></script> - <script src="/static/vendor/bolt11/utils.js"></script> - <script src="/static/vendor/bolt11/decoder.js"></script> - <script src="/static/vendor/vue-qrcode-reader@2.2.0/vue-qrcode-reader.min.js"></script> + <!----> + {% for url in g.VENDORED_JS %} + <script src="{{ url }}"></script> + {% endfor %} + <!----> <script src="/static/js/base.js"></script> <script src="/static/js/components.js"></script> {% block scripts %}{% endblock %} diff --git a/lnbits/templates/print.html b/lnbits/templates/print.html index 0af98d9..b62ee15 100644 --- a/lnbits/templates/print.html +++ b/lnbits/templates/print.html @@ -2,11 +2,9 @@ <html lang="en"> <head> - <link - rel="stylesheet" - href="//fonts.googleapis.com/css?family=Material+Icons" - type="text/css" - /> + {% for url in g.VENDORED_CSS %} + <link rel="stylesheet" type="text/css" href="{{ url }}" /> + {% endfor %} <style> @page { size: A4 portrait; @@ -38,12 +36,10 @@ </q-page-container> </q-layout> - <script src="/static/vendor/quasar@1.13.2/quasar.ie.polyfills.umd.min.js"></script> - <script src="/static/vendor/vue@2.6.12/vue.min.js"></script> - <script src="/static/vendor/vuex@3.5.1/vuex.js"></script> - <script src="/static/vendor/vue-router@3.4.3/vue-router.js"></script> - <script src="/static/vendor/quasar@1.13.2/quasar.umd.min.js"></script> - <script src="/static/vendor/vue-qrcode@1.0.2/vue-qrcode.min.js"></script> + {% for url in g.VENDORED_JS %} + <script src="{{ url }}"></script> + {% endfor %} + <!----> {% block scripts %}{% endblock %} </body> </html>