diff --git a/backend/.eslintrc b/backend/.eslintrc index 3029ebab6..1b2889e50 100644 --- a/backend/.eslintrc +++ b/backend/.eslintrc @@ -31,6 +31,7 @@ "prefer-const": 1, "prefer-rest-params": 1, "quotes": [1, "single", { "allowTemplateLiterals": true }], - "semi": 1 + "semi": 1, + "eqeqeq": 1 } } diff --git a/backend/README.md b/backend/README.md index 823998fdc..3d7c23eaa 100644 --- a/backend/README.md +++ b/backend/README.md @@ -110,6 +110,11 @@ Run the Mempool backend: ``` npm run start + +``` +You can also set env var `MEMPOOL_CONFIG_FILE` to specify a custom config file location: +``` +MEMPOOL_CONFIG_FILE=/path/to/mempool-config.json npm run start ``` When it's running, you should see output like this: diff --git a/backend/package.json b/backend/package.json index 082449dac..3cd05e8ab 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,7 +22,10 @@ "main": "index.ts", "scripts": { "tsc": "./node_modules/typescript/bin/tsc -p tsconfig.build.json", - "build": "npm run tsc", + "build": "npm run tsc && npm run create-resources", + "create-resources": "cp ./src/tasks/price-feeds/mtgox-weekly.json ./dist/tasks && node dist/api/fetch-version.js", + "package": "npm run build && rm -rf package && mv dist package && mv node_modules package && npm run package-rm-build-deps", + "package-rm-build-deps": "(cd package/node_modules; rm -r typescript @typescript-eslint)", "start": "node --max-old-space-size=2048 dist/index.js", "start-production": "node --max-old-space-size=4096 dist/index.js", "test": "./node_modules/.bin/jest --coverage", diff --git a/backend/src/api/backend-info.ts b/backend/src/api/backend-info.ts index d98675671..57bb5fe13 100644 --- a/backend/src/api/backend-info.ts +++ b/backend/src/api/backend-info.ts @@ -1,60 +1,37 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import logger from '../logger'; +import fs from 'fs'; +import path from 'path'; +import os from 'os'; import { IBackendInfo } from '../mempool.interfaces'; -const { spawnSync } = require('child_process'); class BackendInfo { - private gitCommitHash = ''; - private hostname = ''; - private version = ''; + private backendInfo: IBackendInfo; constructor() { - this.setLatestCommitHash(); - this.setVersion(); - this.hostname = os.hostname(); - } - - public getBackendInfo(): IBackendInfo { - return { - hostname: this.hostname, - gitCommit: this.gitCommitHash, - version: this.version, + // This file is created by ./fetch-version.ts during building + const versionFile = path.join(__dirname, 'version.json') + var versionInfo; + if (fs.existsSync(versionFile)) { + versionInfo = JSON.parse(fs.readFileSync(versionFile).toString()); + } else { + // Use dummy values if `versionFile` doesn't exist (e.g., during testing) + versionInfo = { + version: '?', + gitCommit: '?' + }; + } + this.backendInfo = { + hostname: os.hostname(), + version: versionInfo.version, + gitCommit: versionInfo.gitCommit }; } + public getBackendInfo(): IBackendInfo { + return this.backendInfo; + } + public getShortCommitHash() { - return this.gitCommitHash.slice(0, 7); - } - - private setLatestCommitHash(): void { - //TODO: share this logic with `generate-config.js` - if (process.env.DOCKER_COMMIT_HASH) { - this.gitCommitHash = process.env.DOCKER_COMMIT_HASH; - } else { - try { - const gitRevParse = spawnSync('git', ['rev-parse', '--short', 'HEAD']); - if (!gitRevParse.error) { - const output = gitRevParse.stdout.toString('utf-8').replace(/[\n\r\s]+$/, ''); - this.gitCommitHash = output ? output : '?'; - } else if (gitRevParse.error.code === 'ENOENT') { - console.log('git not found, cannot parse git hash'); - this.gitCommitHash = '?'; - } - } catch (e: any) { - console.log('Could not load git commit info: ' + e.message); - this.gitCommitHash = '?'; - } - } - } - - private setVersion(): void { - try { - const packageJson = fs.readFileSync('package.json').toString(); - this.version = JSON.parse(packageJson).version; - } catch (e) { - throw new Error(e instanceof Error ? e.message : 'Error'); - } + return this.backendInfo.gitCommit.slice(0, 7); } } diff --git a/backend/src/api/explorer/channels.api.ts b/backend/src/api/explorer/channels.api.ts index b396e4808..a52b0f28f 100644 --- a/backend/src/api/explorer/channels.api.ts +++ b/backend/src/api/explorer/channels.api.ts @@ -39,7 +39,8 @@ class ChannelsApi { FROM channels JOIN nodes AS nodes_1 on nodes_1.public_key = channels.node1_public_key JOIN nodes AS nodes_2 on nodes_2.public_key = channels.node2_public_key - WHERE nodes_1.latitude IS NOT NULL AND nodes_1.longitude IS NOT NULL + WHERE channels.status = 1 + AND nodes_1.latitude IS NOT NULL AND nodes_1.longitude IS NOT NULL AND nodes_2.latitude IS NOT NULL AND nodes_2.longitude IS NOT NULL `; @@ -374,6 +375,7 @@ class ChannelsApi { 'transaction_vout': channel.transaction_vout, 'closing_transaction_id': channel.closing_transaction_id, 'closing_reason': channel.closing_reason, + 'closing_date': channel.closing_date, 'updated_at': channel.updated_at, 'created': channel.created, 'status': channel.status, diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index 128405ffd..cbd70a34f 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -5,6 +5,49 @@ import { ILightningApi } from '../lightning/lightning-api.interface'; import { ITopNodesPerCapacity, ITopNodesPerChannels } from '../../mempool.interfaces'; class NodesApi { + public async $getWorldNodes(): Promise { + try { + let query = ` + SELECT nodes.public_key as publicKey, IF(nodes.alias = '', SUBSTRING(nodes.public_key, 1, 20), alias) as alias, + CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, + CAST(COALESCE(nodes.channels, 0) as INT) as channels, + nodes.longitude, nodes.latitude, + geo_names_country.names as country, geo_names_iso.names as isoCode + FROM nodes + LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' + LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' + WHERE status = 1 AND nodes.as_number IS NOT NULL + ORDER BY capacity + `; + + const [nodes]: any[] = await DB.query(query); + + for (let i = 0; i < nodes.length; ++i) { + nodes[i].country = JSON.parse(nodes[i].country); + } + + query = ` + SELECT MAX(nodes.capacity) as maxLiquidity, MAX(nodes.channels) as maxChannels + FROM nodes + WHERE status = 1 AND nodes.as_number IS NOT NULL + `; + + const [maximums]: any[] = await DB.query(query); + + return { + maxLiquidity: maximums[0].maxLiquidity, + maxChannels: maximums[0].maxChannels, + nodes: nodes.map(node => [ + node.longitude, node.latitude, + node.publicKey, node.alias, node.capacity, node.channels, + node.country, node.isoCode + ]) + }; + } catch (e) { + logger.err(`Can't get world nodes list. Reason: ${e instanceof Error ? e.message : e}`); + } + } + public async $getNode(public_key: string): Promise { try { // General info @@ -133,10 +176,13 @@ class NodesApi { CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, CAST(COALESCE(nodes.channels, 0) as INT) as channels, UNIX_TIMESTAMP(nodes.first_seen) as firstSeen, UNIX_TIMESTAMP(nodes.updated_at) as updatedAt, - geo_names_city.names as city, geo_names_country.names as country + geo_names_city.names as city, geo_names_country.names as country, + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision FROM nodes LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' + LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' + LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division' ORDER BY capacity DESC LIMIT 100 `; @@ -175,10 +221,13 @@ class NodesApi { CAST(COALESCE(nodes.channels, 0) as INT) as channels, CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, UNIX_TIMESTAMP(nodes.first_seen) as firstSeen, UNIX_TIMESTAMP(nodes.updated_at) as updatedAt, - geo_names_city.names as city, geo_names_country.names as country + geo_names_city.names as city, geo_names_country.names as country, + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision FROM nodes LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' + LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' + LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division' ORDER BY channels DESC LIMIT 100 `; @@ -221,11 +270,14 @@ class NodesApi { CAST(COALESCE(node_stats.channels, 0) as INT) as channels, CAST(COALESCE(node_stats.capacity, 0) as INT) as capacity, UNIX_TIMESTAMP(nodes.first_seen) as firstSeen, UNIX_TIMESTAMP(nodes.updated_at) as updatedAt, - geo_names_city.names as city, geo_names_country.names as country + geo_names_city.names as city, geo_names_country.names as country, + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision FROM node_stats RIGHT JOIN nodes ON nodes.public_key = node_stats.public_key LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' + LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' + LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division' WHERE added = FROM_UNIXTIME(${latestDate}) ORDER BY first_seen LIMIT 100 @@ -382,12 +434,14 @@ class NodesApi { SELECT nodes.public_key, CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, CAST(COALESCE(nodes.channels, 0) as INT) as channels, nodes.alias, UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at, geo_names_city.names as city, geo_names_country.names as country, - geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision, + nodes.longitude, nodes.latitude, nodes.as_number, geo_names_isp.names as isp FROM nodes LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' LEFT JOIN geo_names geo_names_iso ON geo_names_iso.id = nodes.country_id AND geo_names_iso.type = 'country_iso_code' LEFT JOIN geo_names geo_names_subdivision on geo_names_subdivision.id = nodes.subdivision_id AND geo_names_subdivision.type = 'division' + LEFT JOIN geo_names geo_names_isp on geo_names_isp.id = nodes.as_number AND geo_names_isp.type = 'as_organization' WHERE geo_names_country.id = ? ORDER BY capacity DESC `; @@ -397,6 +451,7 @@ class NodesApi { rows[i].country = JSON.parse(rows[i].country); rows[i].city = JSON.parse(rows[i].city); rows[i].subdivision = JSON.parse(rows[i].subdivision); + rows[i].isp = JSON.parse(rows[i].isp); } return rows; } catch (e) { @@ -411,7 +466,8 @@ class NodesApi { SELECT nodes.public_key, CAST(COALESCE(nodes.capacity, 0) as INT) as capacity, CAST(COALESCE(nodes.channels, 0) as INT) as channels, nodes.alias, UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at, geo_names_city.names as city, geo_names_country.names as country, - geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision + geo_names_iso.names as iso_code, geo_names_subdivision.names as subdivision, + nodes.longitude, nodes.latitude FROM nodes LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id AND geo_names_country.type = 'country' LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id AND geo_names_city.type = 'city' diff --git a/backend/src/api/explorer/nodes.routes.ts b/backend/src/api/explorer/nodes.routes.ts index 7a5ff880a..cf3f75208 100644 --- a/backend/src/api/explorer/nodes.routes.ts +++ b/backend/src/api/explorer/nodes.routes.ts @@ -9,6 +9,7 @@ class NodesRoutes { public initRoutes(app: Application) { app + .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/world', this.$getWorldNodes) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/country/:country', this.$getNodesPerCountry) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/search/:search', this.$searchNode) .get(config.MEMPOOL.API_URL_PREFIX + 'lightning/nodes/isp-ranking', this.$getISPRanking) @@ -115,7 +116,6 @@ class NodesRoutes { private async $getISPRanking(req: Request, res: Response): Promise { try { const nodesPerAs = await nodesApi.$getNodesISPRanking(); - res.header('Pragma', 'public'); res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString()); @@ -125,6 +125,18 @@ class NodesRoutes { } } + private async $getWorldNodes(req: Request, res: Response) { + try { + const worldNodes = await nodesApi.$getWorldNodes(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString()); + res.json(worldNodes); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + private async $getNodesPerCountry(req: Request, res: Response) { try { const [country]: any[] = await DB.query( diff --git a/backend/src/api/fetch-version.ts b/backend/src/api/fetch-version.ts new file mode 100644 index 000000000..cb0813c35 --- /dev/null +++ b/backend/src/api/fetch-version.ts @@ -0,0 +1,37 @@ +import fs from 'fs'; +import path from "path"; +const { spawnSync } = require('child_process'); + +function getVersion(): string { + const packageJson = fs.readFileSync('package.json').toString(); + return JSON.parse(packageJson).version; +} + +function getGitCommit(): string { + if (process.env.MEMPOOL_COMMIT_HASH) { + return process.env.MEMPOOL_COMMIT_HASH; + } else { + const gitRevParse = spawnSync('git', ['rev-parse', '--short', 'HEAD']); + if (!gitRevParse.error) { + const output = gitRevParse.stdout.toString('utf-8').replace(/[\n\r\s]+$/, ''); + if (output) { + return output; + } else { + console.log('Could not fetch git commit: No repo available'); + } + } else if (gitRevParse.error.code === 'ENOENT') { + console.log('Could not fetch git commit: Command `git` is unavailable'); + } + } + return '?'; +} + +const versionInfo = { + version: getVersion(), + gitCommit: getGitCommit() +} + +fs.writeFileSync( + path.join(__dirname, 'version.json'), + JSON.stringify(versionInfo, null, 2) + "\n" +); diff --git a/backend/src/api/lightning/clightning/clightning-convert.ts b/backend/src/api/lightning/clightning/clightning-convert.ts index 8b055832e..121fb20ea 100644 --- a/backend/src/api/lightning/clightning/clightning-convert.ts +++ b/backend/src/api/lightning/clightning/clightning-convert.ts @@ -124,7 +124,7 @@ async function buildIncompleteChannel(clChannel: any): Promise { + if (this.closedChannelsScanBlock === blocks.getCurrentBlockHeight()) { + logger.debug(`We've already scan closed channels for this block, skipping.`); + return; + } + let progress = 0; try { - logger.info(`Starting closed channels scan`); + let log = `Starting closed channels scan`; + if (this.closedChannelsScanBlock > 0) { + log += `. Last scan was at block ${this.closedChannelsScanBlock}`; + } else { + log += ` for the first time`; + } + logger.info(log); + const channels = await channelsApi.$getChannelsByStatus([0, 1]); for (const channel of channels) { const spendingTx = await bitcoinApi.$getOutspend(channel.transaction_id, channel.transaction_vout); @@ -263,7 +277,9 @@ class NetworkSyncService { this.loggerTimer = new Date().getTime() / 1000; } } - logger.info(`Closed channels scan complete.`); + + this.closedChannelsScanBlock = blocks.getCurrentBlockHeight(); + logger.info(`Closed channels scan completed at block ${this.closedChannelsScanBlock}`); } catch (e) { logger.err('$scanForClosedChannels() error: ' + (e instanceof Error ? e.message : e)); } diff --git a/backend/src/tasks/price-updater.ts b/backend/src/tasks/price-updater.ts index 81066efb2..891e2bce3 100644 --- a/backend/src/tasks/price-updater.ts +++ b/backend/src/tasks/price-updater.ts @@ -1,4 +1,5 @@ import * as fs from 'fs'; +import path from "path"; import { Common } from '../api/common'; import config from '../config'; import logger from '../logger'; @@ -159,7 +160,7 @@ class PriceUpdater { const existingPriceTimes = await PricesRepository.$getPricesTimes(); // Insert MtGox weekly prices - const pricesJson: any[] = JSON.parse(fs.readFileSync('./src/tasks/price-feeds/mtgox-weekly.json').toString()); + const pricesJson: any[] = JSON.parse(fs.readFileSync(path.join(__dirname, 'mtgox-weekly.json')).toString()); const prices = this.getEmptyPricesObj(); let insertedCount: number = 0; for (const price of pricesJson) { diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index 8d13bc7f4..9be457bb2 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -1,7 +1,7 @@ FROM node:16.16.0-buster-slim AS builder ARG commitHash -ENV DOCKER_COMMIT_HASH=${commitHash} +ENV MEMPOOL_COMMIT_HASH=${commitHash} WORKDIR /build COPY . . @@ -9,18 +9,15 @@ COPY . . RUN apt-get update RUN apt-get install -y build-essential python3 pkg-config RUN npm install --omit=dev --omit=optional -RUN npm run build +RUN npm run package FROM node:16.16.0-buster-slim WORKDIR /backend -COPY --from=builder /build/ . - -RUN chmod +x /backend/start.sh -RUN chmod +x /backend/wait-for-it.sh - -RUN chown -R 1000:1000 /backend && chmod -R 755 /backend +RUN chown 1000:1000 ./ +COPY --from=builder --chown=1000:1000 /build/package ./package/ +COPY --from=builder --chown=1000:1000 /build/mempool-config.json /build/start.sh /build/wait-for-it.sh ./ USER 1000 diff --git a/docker/backend/start.sh b/docker/backend/start.sh old mode 100644 new mode 100755 index 8e6a34013..5a4628f29 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -132,8 +132,8 @@ sed -i "s!__MEMPOOL_USER_AGENT__!${__MEMPOOL_USER_AGENT__}!g" mempool-config.jso sed -i "s/__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__/${__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__}/g" mempool-config.json sed -i "s/__MEMPOOL_INDEXING_BLOCKS_AMOUNT__/${__MEMPOOL_INDEXING_BLOCKS_AMOUNT__}/g" mempool-config.json sed -i "s/__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__/${__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__}/g" mempool-config.json -sed -i "s!__MEMPOOL_POOLS_JSON_URL__!${__MEMPOOL_POOLS_JSON_URL__}/g" mempool-config.json -sed -i "s!__MEMPOOL_POOLS_JSON_TREE_URL__!${__MEMPOOL_POOLS_JSON_TREE_URL__}/g" mempool-config.json +sed -i "s!__MEMPOOL_POOLS_JSON_URL__!${__MEMPOOL_POOLS_JSON_URL__}!g" mempool-config.json +sed -i "s!__MEMPOOL_POOLS_JSON_TREE_URL__!${__MEMPOOL_POOLS_JSON_TREE_URL__}!g" mempool-config.json sed -i "s/__CORE_RPC_HOST__/${__CORE_RPC_HOST__}/g" mempool-config.json sed -i "s/__CORE_RPC_PORT__/${__CORE_RPC_PORT__}/g" mempool-config.json @@ -205,4 +205,4 @@ sed -i "s!__LND_REST_API_URL__!${__LND_REST_API_URL__}!g" mempool-config.json # CLN sed -i "s!__CLN_SOCKET__!${__CLN_SOCKET__}!g" mempool-config.json -node /backend/dist/index.js +node /backend/package/index.js diff --git a/docker/backend/wait-for-it.sh b/docker/backend/wait-for-it.sh old mode 100644 new mode 100755 diff --git a/docker/init.sh b/docker/init.sh index 49b53d646..4eb06f0c1 100755 --- a/docker/init.sh +++ b/docker/init.sh @@ -1,10 +1,7 @@ #!/bin/sh #backend -gitMaster="\.\.\/\.git\/refs\/heads\/master" -git ls-remote https://github.com/mempool/mempool.git "$1^{}" | awk '{ print $1}' > ./backend/master cp ./docker/backend/* ./backend/ -sed -i "s/${gitMaster}/master/g" ./backend/src/api/backend-info.ts #frontend localhostIP="127.0.0.1" diff --git a/frontend/.eslintrc b/frontend/.eslintrc index 4dbcf98d9..9347a2050 100644 --- a/frontend/.eslintrc +++ b/frontend/.eslintrc @@ -32,6 +32,7 @@ "prefer-const": 1, "prefer-rest-params": 1, "quotes": [1, "single", { "allowTemplateLiterals": true }], - "semi": 1 + "semi": 1, + "eqeqeq": 1 } } diff --git a/frontend/angular.json b/frontend/angular.json index 4eb697071..1ed29cad9 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -170,6 +170,10 @@ }, "configurations": { "production": { + "assets": [ + "src/favicon.ico", + "src/robots.txt" + ], "fileReplacements": [ { "replace": "src/environments/environment.ts", diff --git a/frontend/package.json b/frontend/package.json index d2f7f2f6c..b5055a5de 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,7 +34,7 @@ "start:local-staging": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-staging", "start:mixed": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c mixed", "build": "npm run generate-config && npm run ng -- build --configuration production --localize && npm run sync-assets && npm run build-mempool.js", - "sync-assets": "node sync-assets.js && rsync -av ./dist/mempool/browser/en-US/resources ./dist/mempool/browser/resources", + "sync-assets": "rsync -av ./src/resources ./dist/mempool/browser && node sync-assets.js", "sync-assets-dev": "node sync-assets.js dev", "generate-config": "node generate-config.js", "build-mempool.js": "npm run build-mempool-js && npm run build-mempool-liquid-js && npm run build-mempool-bisq-js", diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html index 36d765a17..5cfb2c905 100644 --- a/frontend/src/app/components/about/about.component.html +++ b/frontend/src/app/components/about/about.component.html @@ -187,8 +187,8 @@ -
-

Self-Hosted Integrations

+
+

Community Integrations

-
- -
-

Wallet Integrations

-
+ + + BTCPay + Bisq + + + BlueWallet + + + + Muun + - + Electrum @@ -244,18 +250,14 @@ Phoenix + + + LNBits + Mercury - - - Muun - - - - BlueWallet - Blixt diff --git a/frontend/src/app/components/about/about.component.scss b/frontend/src/app/components/about/about.component.scss index 2cd755bde..2cc5d5102 100644 --- a/frontend/src/app/components/about/about.component.scss +++ b/frontend/src/app/components/about/about.component.scss @@ -43,7 +43,6 @@ .alliances, .enterprise-sponsor, .community-integrations-sponsor, - .selfhosted-integrations-sponsor, .maintainers { margin-top: 68px; margin-bottom: 68px; @@ -117,7 +116,6 @@ .community-sponsor, .project-translators, .community-integrations-sponsor, - .selfhosted-integrations-sponsor, .maintainers { .wrapper { display: inline-block; @@ -193,6 +191,6 @@ } .community-integrations-sponsor { - max-width: 830px; + max-width: 970px; margin: auto; } diff --git a/frontend/src/app/components/address/address-preview.component.html b/frontend/src/app/components/address/address-preview.component.html index 30b9c29e6..ede06247c 100644 --- a/frontend/src/app/components/address/address-preview.component.html +++ b/frontend/src/app/components/address/address-preview.component.html @@ -1,12 +1,14 @@
+ + Address +
-
-

Address

+
+
+

{{addressString.slice(0,-4)}}{{addressString.slice(-4)}}

+
-
- {{addressString.slice(0,-4)}}{{addressString.slice(-4)}} - diff --git a/frontend/src/app/components/address/address-preview.component.scss b/frontend/src/app/components/address/address-preview.component.scss index 2de368547..afa8cb4b4 100644 --- a/frontend/src/app/components/address/address-preview.component.scss +++ b/frontend/src/app/components/address/address-preview.component.scss @@ -1,6 +1,5 @@ -h1 { - font-size: 52px; - margin: 0; +.title-wrapper { + padding: 0 15px; } .qr-wrapper { @@ -23,27 +22,9 @@ h1 { .table { font-size: 32px; + margin-top: 48px; ::ng-deep .symbol { font-size: 24px; } -} - -.address-link { - font-size: 24px; - margin-bottom: 0.5em; - display: flex; - flex-direction: row; - align-items: baseline; - .truncated-address { - text-overflow: ellipsis; - overflow: hidden; - max-width: calc(640px - 4em); - display: inline-block; - white-space: nowrap; - } - .last-four { - display: inline-block; - white-space: nowrap; - } -} +} \ No newline at end of file diff --git a/frontend/src/app/components/asset-circulation/asset-circulation.component.ts b/frontend/src/app/components/asset-circulation/asset-circulation.component.ts index 4d65417e6..d64607fe1 100644 --- a/frontend/src/app/components/asset-circulation/asset-circulation.component.ts +++ b/frontend/src/app/components/asset-circulation/asset-circulation.component.ts @@ -4,7 +4,6 @@ import { map } from 'rxjs/operators'; import { moveDec } from 'src/app/bitcoin.utils'; import { AssetsService } from 'src/app/services/assets.service'; import { ElectrsApiService } from 'src/app/services/electrs-api.service'; -import { formatNumber } from '@angular/common'; import { environment } from 'src/environments/environment'; @Component({ diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html index 517eab653..782cbe25e 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.html @@ -1,6 +1,6 @@
-
+
diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index 7309a0a85..4cc465ad7 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -17,6 +17,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy { @Input() blockLimit: number; @Input() orientation = 'left'; @Input() flip = true; + @Input() disableSpinner = false; @Output() txClickEvent = new EventEmitter(); @Output() readyEvent = new EventEmitter(); diff --git a/frontend/src/app/components/block/block-preview.component.html b/frontend/src/app/components/block/block-preview.component.html index 768bc3da3..666714b24 100644 --- a/frontend/src/app/components/block/block-preview.component.html +++ b/frontend/src/app/components/block/block-preview.component.html @@ -1,19 +1,18 @@
+ + Block +
-

- Genesis - - {{ blockHeight }} - - - Block - - - {{ blockHeight }} - - -

+
+
+

+ Genesis + {{ blockHeight }} +

+
+
+ {{blockHash.slice(0,-4)}}{{blockHash.slice(-4)}}