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..db6e92920 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 `; diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index b959d49a0..9d82dc83d 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -176,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 `; @@ -218,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 `; @@ -264,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 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/config.ts b/backend/src/config.ts index 8c91e104b..43ba16cb3 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -1,4 +1,6 @@ -const configFile = require('../mempool-config.json'); +const configFromFile = require( + process.env.MEMPOOL_CONFIG_FILE ? process.env.MEMPOOL_CONFIG_FILE : '../mempool-config.json' +); interface IConfig { MEMPOOL: { @@ -249,7 +251,7 @@ class Config implements IConfig { MAXMIND: IConfig['MAXMIND']; constructor() { - const configs = this.merge(configFile, defaults); + const configs = this.merge(configFromFile, defaults); this.MEMPOOL = configs.MEMPOOL; this.ESPLORA = configs.ESPLORA; this.ELECTRUM = configs.ELECTRUM; 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..ade739f1f --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -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/src/app/components/address/address-preview.component.html b/frontend/src/app/components/address/address-preview.component.html index 30b9c29e6..6a2d1efee 100644 --- a/frontend/src/app/components/address/address-preview.component.html +++ b/frontend/src/app/components/address/address-preview.component.html @@ -1,12 +1,12 @@
+

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/block/block-preview.component.html b/frontend/src/app/components/block/block-preview.component.html index 768bc3da3..38018bbb4 100644 --- a/frontend/src/app/components/block/block-preview.component.html +++ b/frontend/src/app/components/block/block-preview.component.html @@ -1,19 +1,16 @@
+

Block

-

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

+
+
+

+ Genesis + {{ blockHeight }} +

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