Merge branch 'master' into nymkappa/feature/node-world-map
This commit is contained in:
		
						commit
						d0b4d1da4a
					
				@ -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:
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
      `;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								backend/src/api/fetch-version.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								backend/src/api/fetch-version.ts
									
									
									
									
									
										Normal file
									
								
							@ -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"
 | 
			
		||||
);
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								docker/backend/start.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										2
									
								
								docker/backend/start.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@ -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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										0
									
								
								docker/backend/wait-for-it.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								docker/backend/wait-for-it.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@ -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"
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,12 @@
 | 
			
		||||
<div class="box preview-box" *ngIf="address && !error">
 | 
			
		||||
  <h2 class="preview-header" i18n="shared.address">Address</h2>
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-md">
 | 
			
		||||
      <div class="title-address">
 | 
			
		||||
        <h1 i18n="shared.address">Address</h1>
 | 
			
		||||
      <div class="row d-flex justify-content-between">
 | 
			
		||||
        <div class="title-wrapper">
 | 
			
		||||
          <h1 class="title truncated"><span class="first">{{addressString.slice(0,-4)}}</span><span class="last-four">{{addressString.slice(-4)}}</span></h1>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <a [routerLink]="['/address/' | relativeUrl, addressString]" class="address-link" >
 | 
			
		||||
        <span class="truncated-address">{{addressString.slice(0,-4)}}</span><span class="last-four">{{addressString.slice(-4)}}</span>
 | 
			
		||||
      </a>
 | 
			
		||||
      <table class="table table-borderless table-striped">
 | 
			
		||||
        <tbody>
 | 
			
		||||
          <tr *ngIf="addressInfo && addressInfo.unconfidential">
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@ -1,19 +1,16 @@
 | 
			
		||||
<div class="box preview-box" *ngIf="!error">
 | 
			
		||||
  <h2 class="preview-header" i18n="shared.block-title">Block</h2>
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-sm">
 | 
			
		||||
      <h1 class="block-title">
 | 
			
		||||
        <ng-template [ngIf]="blockHeight === 0"><ng-container i18n="@@2303359202781425764">Genesis</ng-container>
 | 
			
		||||
          <span class="next-previous-blocks">
 | 
			
		||||
            <a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a>
 | 
			
		||||
          </span>
 | 
			
		||||
        </ng-template>
 | 
			
		||||
        <ng-template [ngIf]="blockHeight" i18n="shared.block-title">Block <ng-container *ngTemplateOutlet="blockTemplateContent"></ng-container></ng-template>
 | 
			
		||||
        <ng-template #blockTemplateContent>
 | 
			
		||||
          <span class="next-previous-blocks">
 | 
			
		||||
            <a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a>
 | 
			
		||||
          </span>
 | 
			
		||||
        </ng-template>
 | 
			
		||||
      </h1>
 | 
			
		||||
      <div class="row d-flex justify-content-between">
 | 
			
		||||
        <div class="title-wrapper">
 | 
			
		||||
          <h1 class="title">
 | 
			
		||||
            <ng-template [ngIf]="blockHeight === 0"><ng-container i18n="@@2303359202781425764">Genesis</ng-container></ng-template>
 | 
			
		||||
            <ng-template [ngIf]="blockHeight" i18n="shared.block-title">{{ blockHeight }}</ng-template>
 | 
			
		||||
          </h1>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <a class="subtitle truncated" [routerLink]="['/block/' | relativeUrl, blockHash]"><span class="first">{{blockHash.slice(0,-4)}}</span><span class="last-four">{{blockHash.slice(-4)}}</span></a>
 | 
			
		||||
      <table class="table table-borderless table-striped">
 | 
			
		||||
        <tbody>
 | 
			
		||||
          <!-- <tr>
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,10 @@
 | 
			
		||||
.block-title {
 | 
			
		||||
  margin-bottom: 48px;
 | 
			
		||||
  font-size: 52px;
 | 
			
		||||
 | 
			
		||||
  ::ng-deep .next-previous-blocks {
 | 
			
		||||
    font-size: 52px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table {
 | 
			
		||||
  font-size: 32px;
 | 
			
		||||
  margin-top: 6px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.title-wrapper {
 | 
			
		||||
  padding-left: 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.chart-container {
 | 
			
		||||
 | 
			
		||||
@ -7,12 +7,12 @@
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <div [ngSwitch]="network.val">
 | 
			
		||||
      <span *ngSwitchCase="'signet'" class="network signet"><app-svg-images name="signet" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Signet <ng-template [ngIf]="(lightning$ | async)">Lightning</ng-template></span>
 | 
			
		||||
      <span *ngSwitchCase="'testnet'" class="network testnet"><app-svg-images name="testnet" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Testnet <ng-template [ngIf]="(lightning$ | async)">Lightning</ng-template></span>
 | 
			
		||||
      <span *ngSwitchCase="'signet'" class="network signet"><app-svg-images name="signet" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Signet</span>
 | 
			
		||||
      <span *ngSwitchCase="'testnet'" class="network testnet"><app-svg-images name="testnet" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Testnet</span>
 | 
			
		||||
      <span *ngSwitchCase="'bisq'" class="network bisq"><app-svg-images name="bisq" width="35" height="35" viewBox="0 0 75 75" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Bisq</span>
 | 
			
		||||
      <span *ngSwitchCase="'liquid'" class="network liquid"><app-svg-images name="liquid" width="35" height="35" viewBox="0 0 125 125" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Liquid</span>
 | 
			
		||||
      <span *ngSwitchCase="'liquidtestnet'" class="network liquidtestnet"><app-svg-images name="liquidtestnet" width="35" height="35" viewBox="0 0 125 125" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Liquid Testnet</span>
 | 
			
		||||
      <span *ngSwitchDefault class="network mainnet"><app-svg-images name="bitcoin" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Mainnet <ng-template [ngIf]="(lightning$ | async)">Lightning</ng-template></span>
 | 
			
		||||
      <span *ngSwitchDefault class="network mainnet"><app-svg-images name="bitcoin" width="35" height="35" viewBox="0 0 65 65" style="width: 40px; height: 48px;" class="mainnet mr-1"></app-svg-images> Mainnet</span>
 | 
			
		||||
    </div>
 | 
			
		||||
  </header>
 | 
			
		||||
  <router-outlet></router-outlet>
 | 
			
		||||
 | 
			
		||||
@ -33,4 +33,66 @@
 | 
			
		||||
    justify-content: flex-start;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ::ng-deep .preview-header {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: -80px;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    padding: 0 220px;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
    z-index: 101;
 | 
			
		||||
    line-height: 80px;
 | 
			
		||||
    text-transform: capitalize;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ::ng-deep .title {
 | 
			
		||||
    font-size: 52px;
 | 
			
		||||
  }
 | 
			
		||||
  ::ng-deep .subtitle {
 | 
			
		||||
    font-size: 28px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ::ng-deep .title, ::ng-deep .subtitle {
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
 | 
			
		||||
    &.truncated {
 | 
			
		||||
      text-overflow: unset;
 | 
			
		||||
      display: flex;
 | 
			
		||||
      flex-direction: row;
 | 
			
		||||
      align-items: baseline;
 | 
			
		||||
 | 
			
		||||
      .first {
 | 
			
		||||
        flex-grow: 1;
 | 
			
		||||
        flex-shrink: 1;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        text-overflow: ellipsis;
 | 
			
		||||
        margin-right: -2px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .last-four {
 | 
			
		||||
        flex-shrink: 0;
 | 
			
		||||
        flex-grow: 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ::ng-deep .title-wrapper {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    align-items: flex-start;
 | 
			
		||||
    justify-content: flex-start;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    width: 0;
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
    flex-shrink: 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,9 @@
 | 
			
		||||
<div class="box preview-box" *ngIf="tx && !error">
 | 
			
		||||
 | 
			
		||||
  <div class="page-title">
 | 
			
		||||
    <h1 i18n="shared.transaction">Transaction</h1>
 | 
			
		||||
    <a class="tx-link" [routerLink]="['/tx/' | relativeUrl, txId]">
 | 
			
		||||
      <span class="truncated">{{txId.slice(0,-4)}}</span><span class="last-four">{{txId.slice(-4)}}</span>
 | 
			
		||||
    </a>
 | 
			
		||||
  <h2 class="preview-header" i18n="shared.transaction">Transaction</h2>
 | 
			
		||||
  <div class="row d-flex justify-content-between full-width-row">
 | 
			
		||||
    <div class="title-wrapper">
 | 
			
		||||
      <h1 class="title truncated"><span class="first">{{txId.slice(0,-4)}}</span><span class="last-four">{{txId.slice(-4)}}</span></h1>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div *ngIf="network !== 'liquid' && network !== 'liquidtestnet'" class="features">
 | 
			
		||||
      <app-tx-features [tx]="tx"></app-tx-features>
 | 
			
		||||
      <span *ngIf="cpfpInfo && cpfpInfo.bestDescendant" class="badge badge-primary mr-1">
 | 
			
		||||
@ -15,7 +14,6 @@
 | 
			
		||||
      </span>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="top-data row">
 | 
			
		||||
    <span class="field col-sm-4 text-left">
 | 
			
		||||
      <ng-template [ngIf]="isLiquid && haveBlindedOutputValues(tx)" [ngIfElse]="defaultAmount" i18n="shared.confidential">Confidential</ng-template>
 | 
			
		||||
 | 
			
		||||
@ -26,56 +26,9 @@
 | 
			
		||||
	margin-top: 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.page-title {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  align-items: baseline;
 | 
			
		||||
  margin-bottom: 2px;
 | 
			
		||||
  max-width: 100%;
 | 
			
		||||
 | 
			
		||||
  h1 {
 | 
			
		||||
    font-size: 52px;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    line-height: 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .features {
 | 
			
		||||
    font-size: 24px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  & > * {
 | 
			
		||||
    flex-grow: 0;
 | 
			
		||||
    flex-shrink: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .tx-link {
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
    flex-shrink: 1;
 | 
			
		||||
    margin: 0 1em;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    align-items: baseline;
 | 
			
		||||
 | 
			
		||||
    .truncated {
 | 
			
		||||
      flex-grow: 1;
 | 
			
		||||
      flex-shrink: 1;
 | 
			
		||||
      overflow: hidden;
 | 
			
		||||
      text-overflow: ellipsis;
 | 
			
		||||
      margin-right: -2px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .last-four {
 | 
			
		||||
      flex-shrink: 0;
 | 
			
		||||
      flex-grow: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .features {
 | 
			
		||||
    align-self: center;
 | 
			
		||||
  }
 | 
			
		||||
.features {
 | 
			
		||||
  font-size: 24px;
 | 
			
		||||
  margin-left: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.top-data {
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -161,6 +161,9 @@ export interface ITopNodesPerChannels {
 | 
			
		||||
  updatedAt?: number,
 | 
			
		||||
  city?: any,
 | 
			
		||||
  country?: any,
 | 
			
		||||
  subdivision?: any,
 | 
			
		||||
  iso_code?: string,
 | 
			
		||||
  geolocation?: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ITopNodesPerCapacity {
 | 
			
		||||
@ -172,6 +175,9 @@ export interface ITopNodesPerCapacity {
 | 
			
		||||
  updatedAt?: number,
 | 
			
		||||
  city?: any,
 | 
			
		||||
  country?: any,
 | 
			
		||||
  subdivision?: any,
 | 
			
		||||
  iso_code?: string,
 | 
			
		||||
  geolocation?: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INodesRanking {
 | 
			
		||||
@ -188,6 +194,9 @@ export interface IOldestNodes {
 | 
			
		||||
  updatedAt?: number,
 | 
			
		||||
  city?: any,
 | 
			
		||||
  country?: any,
 | 
			
		||||
  subdivision?: any,
 | 
			
		||||
  iso_code?: string,
 | 
			
		||||
  geolocation?: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IChannel {
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
<div class="box preview-box" *ngIf="(channel$ | async) as channel">
 | 
			
		||||
  <h2 class="preview-header" i18n="lightning.channel">lightning channel</h2>
 | 
			
		||||
  <div class="row d-flex justify-content-between full-width-row">
 | 
			
		||||
    <h1 class="title">
 | 
			
		||||
      <span i18n="lightning.channel">Channel</span>: 
 | 
			
		||||
      <a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]"> {{ channel.short_id }}</a>
 | 
			
		||||
    </h1>
 | 
			
		||||
    <div class="title-wrapper">
 | 
			
		||||
      <h1 class="title">{{ channel.short_id }}</h1>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="badges mb-2">
 | 
			
		||||
      <span class="badge rounded-pill badge-secondary" *ngIf="channel.status === 0">Inactive</span>
 | 
			
		||||
      <span class="badge rounded-pill badge-success" *ngIf="channel.status === 1">Active</span>
 | 
			
		||||
@ -12,20 +12,11 @@
 | 
			
		||||
      <app-closing-type [type]="channel.closing_reason" *ngIf="channel.status === 2"></app-closing-type>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="row d-flex justify-content-between full-width-row nodes">
 | 
			
		||||
    <span class="node left">
 | 
			
		||||
      {{ channel.node_left.alias || '?' }}
 | 
			
		||||
    </span>
 | 
			
		||||
    <fa-icon class="between-arrow" [icon]="['fas', 'arrow-right-arrow-left']" [fixedWidth]="true" title="channel between"></fa-icon>
 | 
			
		||||
    <span class="node right">
 | 
			
		||||
      {{ channel.node_right.alias || '?' }}
 | 
			
		||||
    </span>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-md">
 | 
			
		||||
      <a class="subtitle" [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">{{ channel.id }}</a>
 | 
			
		||||
      <table class="table table-borderless table-striped">
 | 
			
		||||
        <tbody>
 | 
			
		||||
          <tr></tr>
 | 
			
		||||
          <tr>
 | 
			
		||||
            <td i18n="channel.created">Created</td>
 | 
			
		||||
            <td>{{ channel.created | date:'yyyy-MM-dd HH:mm' }}</td>
 | 
			
		||||
@ -61,6 +52,15 @@
 | 
			
		||||
      <app-nodes-channels-map *ngIf="!error" [style]="'channelpage'" [channel]="channelGeo" [fitContainer]="true" [placeholder]="true" (readyEvent)="onMapReady()"></app-nodes-channels-map>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="row d-flex justify-content-between full-width-row nodes">
 | 
			
		||||
    <span class="node left">
 | 
			
		||||
      {{ channel.node_left.alias || '?' }}
 | 
			
		||||
    </span>
 | 
			
		||||
    <fa-icon class="between-arrow" [icon]="['fas', 'arrow-right-arrow-left']" [fixedWidth]="true" title="channel between"></fa-icon>
 | 
			
		||||
    <span class="node right">
 | 
			
		||||
      {{ channel.node_right.alias || '?' }}
 | 
			
		||||
    </span>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<ng-template [ngIf]="error">
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,17 @@
 | 
			
		||||
.title {
 | 
			
		||||
  font-size: 52px;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table {
 | 
			
		||||
  font-size: 32px;
 | 
			
		||||
  margin-top: 36px;
 | 
			
		||||
  margin-top: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.badges {
 | 
			
		||||
  font-size: 28px;
 | 
			
		||||
  flex-shrink: 0;
 | 
			
		||||
  flex-grow: 0;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  flex-wrap: nowrap;
 | 
			
		||||
  align-items: baseline;
 | 
			
		||||
  justify-content: flex-end;
 | 
			
		||||
 | 
			
		||||
  ::ng-deep .badge {
 | 
			
		||||
    margin-left: 0.5em;
 | 
			
		||||
@ -23,11 +25,12 @@
 | 
			
		||||
.full-width-row {
 | 
			
		||||
  padding-left: 15px;
 | 
			
		||||
  padding-right: 15px;
 | 
			
		||||
  flex-wrap: nowrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  &:nth-child(even) {
 | 
			
		||||
    background: #181b2d;
 | 
			
		||||
    margin: 15px 0;
 | 
			
		||||
  }
 | 
			
		||||
.row.nodes {
 | 
			
		||||
  background: #181b2d;
 | 
			
		||||
  margin: 15px 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nodes {
 | 
			
		||||
@ -46,7 +49,7 @@
 | 
			
		||||
  min-width: 470px;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  background: #181b2d;
 | 
			
		||||
  max-height: 470px;
 | 
			
		||||
  max-height: 350px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,17 @@
 | 
			
		||||
<div class="box preview-box" *ngIf="(node$ | async) as node">
 | 
			
		||||
  <h2 class="preview-header" i18n="lightning.node">lightning node</h2>
 | 
			
		||||
  <div class="row d-flex justify-content-between full-width-row">
 | 
			
		||||
    <h1 class="title">
 | 
			
		||||
      <span i18n="lightning.node">Node</span>:
 | 
			
		||||
      <a [routerLink]="['/lightning/node' | relativeUrl, node.id]"> {{ node.alias }}</a>
 | 
			
		||||
    </h1>
 | 
			
		||||
    <h1 class="title"></h1>
 | 
			
		||||
    <div class="title-wrapper">
 | 
			
		||||
      <h1 class="title">{{ node.alias }}</h1>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="badges mb-2">
 | 
			
		||||
      <span class="badge rounded-pill badge-success" *ngFor="let socketType of socketTypes">{{ socketType }}</span>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-md">
 | 
			
		||||
      <a class="subtitle" [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">{{ node.public_key }}</a>
 | 
			
		||||
      <table class="table table-borderless table-striped">
 | 
			
		||||
        <tbody>
 | 
			
		||||
          <tr>
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,17 @@
 | 
			
		||||
.title {
 | 
			
		||||
  font-size: 52px;
 | 
			
		||||
  margin-bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table {
 | 
			
		||||
  margin-top: 48px;
 | 
			
		||||
  margin-top: 6px;
 | 
			
		||||
  font-size: 32px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.badges {
 | 
			
		||||
  font-size: 28px;
 | 
			
		||||
  flex-shrink: 0;
 | 
			
		||||
  flex-grow: 0;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  flex-wrap: nowrap;
 | 
			
		||||
  align-items: baseline;
 | 
			
		||||
  justify-content: flex-end;
 | 
			
		||||
 | 
			
		||||
  ::ng-deep .badge {
 | 
			
		||||
    margin-left: 0.5em;
 | 
			
		||||
@ -20,14 +22,14 @@
 | 
			
		||||
  flex-grow: 0;
 | 
			
		||||
  flex-shrink: 0;
 | 
			
		||||
  width: 470px;
 | 
			
		||||
  height: 390px;
 | 
			
		||||
  height: 408px;
 | 
			
		||||
  min-width: 470px;
 | 
			
		||||
  min-height: 390px;
 | 
			
		||||
  max-height: 390px;
 | 
			
		||||
  min-height: 408px;
 | 
			
		||||
  max-height: 408px;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  background: #181b2d;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  margin-top: 18px;
 | 
			
		||||
  margin-top: 6px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.row {
 | 
			
		||||
@ -36,6 +38,7 @@
 | 
			
		||||
 | 
			
		||||
.full-width-row {
 | 
			
		||||
  padding-left: 15px;
 | 
			
		||||
  flex-wrap: nowrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
::ng-deep .symbol {
 | 
			
		||||
 | 
			
		||||
@ -120,7 +120,7 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div *ngIf="!error">
 | 
			
		||||
    <div class="row" *ngIf="node.as_number">
 | 
			
		||||
    <div class="row" *ngIf="node.as_number && node.active_channel_count">
 | 
			
		||||
      <div class="col-sm">
 | 
			
		||||
        <app-nodes-channels-map [style]="'nodepage'" [publicKey]="node.public_key" [hasLocation]="!!node.as_number"></app-nodes-channels-map>
 | 
			
		||||
      </div>
 | 
			
		||||
@ -128,7 +128,7 @@
 | 
			
		||||
        <app-node-statistics-chart [publicKey]="node.public_key"></app-node-statistics-chart>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div *ngIf="!node.as_number">
 | 
			
		||||
    <div *ngIf="!node.as_number || !node.active_channel_count">
 | 
			
		||||
      <app-node-statistics-chart [publicKey]="node.public_key"></app-node-statistics-chart>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@
 | 
			
		||||
          <td class="text-right capacity">
 | 
			
		||||
            <app-amount *ngIf="country.capacity > 100000000; else smallchannel" [satoshis]="country.capacity" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount>
 | 
			
		||||
            <ng-template #smallchannel>
 | 
			
		||||
              {{ country.capacity | amountShortener: 1 }}
 | 
			
		||||
              {{ country.capacity ?? 0 | amountShortener: 1 }}
 | 
			
		||||
              <span class="sats" i18n="shared.sats">sats</span>
 | 
			
		||||
            </ng-template>
 | 
			
		||||
          </td>
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@
 | 
			
		||||
        <th class="timestamp-update text-left" i18n="lightning.last_update">Last update</th>
 | 
			
		||||
        <th class="capacity text-right" i18n="lightning.capacity">Capacity</th>
 | 
			
		||||
        <th class="channels text-right" i18n="lightning.channels">Channels</th>
 | 
			
		||||
        <th class="city text-right" i18n="lightning.city">City</th>
 | 
			
		||||
        <th class="city text-right" i18n="lightning.location">Location</th>
 | 
			
		||||
      </thead>
 | 
			
		||||
      <tbody *ngIf="nodes$ | async as nodes">
 | 
			
		||||
        <tr *ngFor="let node of nodes; let i= index; trackBy: trackByPublicKey">
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@
 | 
			
		||||
        <th class="timestamp-update text-left" i18n="lightning.last_update">Last update</th>
 | 
			
		||||
        <th class="capacity text-right" i18n="lightning.capacity">Capacity</th>
 | 
			
		||||
        <th class="channels text-right" i18n="lightning.channels">Channels</th>
 | 
			
		||||
        <th class="city text-right" i18n="lightning.city">City</th>
 | 
			
		||||
        <th class="city text-right" i18n="lightning.location">Location</th>
 | 
			
		||||
      </thead>
 | 
			
		||||
      <tbody *ngIf="nodes$ | async as nodes">
 | 
			
		||||
        <tr *ngFor="let node of nodes; let i= index; trackBy: trackByPublicKey">
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@
 | 
			
		||||
        <th class="rank"></th>
 | 
			
		||||
        <th class="alias text-left" i18n="nodes.alias">Alias</th>
 | 
			
		||||
        <th  class="timestamp-first text-right" i18n="lightning.first_seen">First seen</th>
 | 
			
		||||
        <th *ngIf="!widget" class="capacity text-right" i18n="node.capacity">Capacity</th>
 | 
			
		||||
        <th *ngIf="!widget" class="capacity text-right" i18n="node.liquidity">Liquidity</th>
 | 
			
		||||
        <th *ngIf="!widget" class="channels text-right" i18n="lightning.channels">Channels</th>
 | 
			
		||||
        <th *ngIf="!widget" class="timestamp-update text-left" i18n="lightning.last_update">Last update</th>
 | 
			
		||||
        <th *ngIf="!widget" class="location text-right" i18n="lightning.location">Location</th>
 | 
			
		||||
@ -35,7 +35,7 @@
 | 
			
		||||
            <app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.updatedAt"></app-timestamp>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td *ngIf="!widget" class="location text-right text-truncate">
 | 
			
		||||
            {{ node?.city?.en ?? '-' }}
 | 
			
		||||
            <app-geolocation [data]="node.geolocation" [type]="'list-isp'"></app-geolocation>
 | 
			
		||||
          </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </tbody>
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
 | 
			
		||||
import { map, Observable } from 'rxjs';
 | 
			
		||||
import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component';
 | 
			
		||||
import { IOldestNodes } from '../../../interfaces/node-api.interface';
 | 
			
		||||
import { LightningApiService } from '../../lightning-api.service';
 | 
			
		||||
 | 
			
		||||
@ -23,11 +24,23 @@ export class OldestNodes implements OnInit {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.widget === false) {
 | 
			
		||||
      this.oldestNodes$ = this.apiService.getOldestNodes$();
 | 
			
		||||
      this.oldestNodes$ = this.apiService.getOldestNodes$().pipe(
 | 
			
		||||
        map((ranking) => {
 | 
			
		||||
          for (const i in ranking) {
 | 
			
		||||
            ranking[i].geolocation = <GeolocationData>{
 | 
			
		||||
              country: ranking[i].country?.en,
 | 
			
		||||
              city: ranking[i].city?.en,
 | 
			
		||||
              subdivision: ranking[i].subdivision?.en,
 | 
			
		||||
              iso: ranking[i].iso_code,
 | 
			
		||||
            };
 | 
			
		||||
          }
 | 
			
		||||
          return ranking;
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      this.oldestNodes$ = this.apiService.getOldestNodes$().pipe(
 | 
			
		||||
        map((nodes: IOldestNodes[]) => {
 | 
			
		||||
          return nodes.slice(0, 10);
 | 
			
		||||
          return nodes.slice(0, 7);
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@
 | 
			
		||||
      <thead>
 | 
			
		||||
        <th class="rank"></th>
 | 
			
		||||
        <th class="alias text-left" i18n="nodes.alias">Alias</th>
 | 
			
		||||
        <th class="capacity text-right" i18n="node.capacity">Capacity</th>
 | 
			
		||||
        <th class="capacity text-right" i18n="node.liquidity">Liquidity</th>
 | 
			
		||||
        <th *ngIf="!widget" class="channels text-right" i18n="lightning.channels">Channels</th>
 | 
			
		||||
        <th *ngIf="!widget" class="timestamp-first text-left" i18n="lightning.first_seen">First seen</th>
 | 
			
		||||
        <th *ngIf="!widget" class="timestamp-update text-left" i18n="lightning.last_update">Last update</th>
 | 
			
		||||
@ -35,7 +35,7 @@
 | 
			
		||||
            <app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.updatedAt"></app-timestamp>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td *ngIf="!widget" class="location text-right text-truncate">
 | 
			
		||||
            {{ node?.city?.en ?? '-' }}
 | 
			
		||||
            <app-geolocation [data]="node.geolocation" [type]="'list-isp'"></app-geolocation>
 | 
			
		||||
          </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </tbody>
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core
 | 
			
		||||
import { map, Observable } from 'rxjs';
 | 
			
		||||
import { INodesRanking, ITopNodesPerCapacity } from 'src/app/interfaces/node-api.interface';
 | 
			
		||||
import { isMobile } from 'src/app/shared/common.utils';
 | 
			
		||||
import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component';
 | 
			
		||||
import { LightningApiService } from '../../lightning-api.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@ -25,7 +26,19 @@ export class TopNodesPerCapacity implements OnInit {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.widget === false) {
 | 
			
		||||
      this.topNodesPerCapacity$ = this.apiService.getTopNodesByCapacity$();
 | 
			
		||||
      this.topNodesPerCapacity$ = this.apiService.getTopNodesByCapacity$().pipe(
 | 
			
		||||
        map((ranking) => {
 | 
			
		||||
          for (const i in ranking) {
 | 
			
		||||
            ranking[i].geolocation = <GeolocationData>{
 | 
			
		||||
              country: ranking[i].country?.en,
 | 
			
		||||
              city: ranking[i].city?.en,
 | 
			
		||||
              subdivision: ranking[i].subdivision?.en,
 | 
			
		||||
              iso: ranking[i].iso_code,
 | 
			
		||||
            };
 | 
			
		||||
          }
 | 
			
		||||
          return ranking;
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      this.topNodesPerCapacity$ = this.nodes$.pipe(
 | 
			
		||||
        map((ranking) => {
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@
 | 
			
		||||
        <th class="rank"></th>
 | 
			
		||||
        <th class="alias text-left" i18n="nodes.alias">Alias</th>
 | 
			
		||||
        <th class="channels text-right" i18n="node.channels">Channels</th>
 | 
			
		||||
        <th *ngIf="!widget" class="capacity text-right" i18n="lightning.capacity">Capacity</th>
 | 
			
		||||
        <th *ngIf="!widget" class="capacity text-right" i18n="lightning.liquidity">Liquidity</th>
 | 
			
		||||
        <th *ngIf="!widget" class="timestamp-first text-left" i18n="lightning.first_seen">First seen</th>
 | 
			
		||||
        <th *ngIf="!widget" class="timestamp-update text-left" i18n="lightning.last_update">Last update</th>
 | 
			
		||||
        <th *ngIf="!widget" class="location text-right" i18n="lightning.location">Location</th>
 | 
			
		||||
@ -35,9 +35,9 @@
 | 
			
		||||
            <app-timestamp [customFormat]="'yyyy-MM-dd'" [unixTime]="node.updatedAt"></app-timestamp>
 | 
			
		||||
          </td>
 | 
			
		||||
          <td *ngIf="!widget" class="location text-right text-truncate">
 | 
			
		||||
            {{ node?.city?.en ?? '-' }}
 | 
			
		||||
            <app-geolocation [data]="node.geolocation" [type]="'list-isp'"></app-geolocation>
 | 
			
		||||
          </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </tr>
 | 
			
		||||
      </tbody>
 | 
			
		||||
      <ng-template #skeleton>
 | 
			
		||||
        <tbody>
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core
 | 
			
		||||
import { map, Observable } from 'rxjs';
 | 
			
		||||
import { INodesRanking, ITopNodesPerChannels } from 'src/app/interfaces/node-api.interface';
 | 
			
		||||
import { isMobile } from 'src/app/shared/common.utils';
 | 
			
		||||
import { GeolocationData } from 'src/app/shared/components/geolocation/geolocation.component';
 | 
			
		||||
import { LightningApiService } from '../../lightning-api.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@ -25,7 +26,19 @@ export class TopNodesPerChannels implements OnInit {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.widget === false) {
 | 
			
		||||
      this.topNodesPerChannels$ = this.apiService.getTopNodesByChannels$();
 | 
			
		||||
      this.topNodesPerChannels$ = this.apiService.getTopNodesByChannels$().pipe(
 | 
			
		||||
        map((ranking) => {
 | 
			
		||||
          for (const i in ranking) {
 | 
			
		||||
            ranking[i].geolocation = <GeolocationData>{
 | 
			
		||||
              country: ranking[i].country?.en,
 | 
			
		||||
              city: ranking[i].city?.en,
 | 
			
		||||
              subdivision: ranking[i].subdivision?.en,
 | 
			
		||||
              iso: ranking[i].iso_code,
 | 
			
		||||
            };
 | 
			
		||||
          }
 | 
			
		||||
          return ranking;
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      this.topNodesPerChannels$ = this.nodes$.pipe(
 | 
			
		||||
        map((ranking) => {
 | 
			
		||||
 | 
			
		||||
@ -89,7 +89,7 @@ body {
 | 
			
		||||
 | 
			
		||||
.preview-box {
 | 
			
		||||
  min-height: 520px;
 | 
			
		||||
  padding: 1.5rem 3rem;
 | 
			
		||||
  padding: 1rem 3rem 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 767.98px) {
 | 
			
		||||
 | 
			
		||||
@ -1009,7 +1009,6 @@ osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_
 | 
			
		||||
osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-build-all upgrade
 | 
			
		||||
osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-kill-all stop
 | 
			
		||||
osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-start-all start
 | 
			
		||||
osSudo "${MEMPOOL_USER}" ln -s mempool/production/mempool-restart-all restart
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
case $OS in
 | 
			
		||||
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
#!/usr/bin/env zsh
 | 
			
		||||
HOSTNAME=$(hostname)
 | 
			
		||||
 | 
			
		||||
echo restarting mempool backends | wall
 | 
			
		||||
echo "${HOSTNAME} restarted mempool backends" | /usr/local/bin/keybase chat send --nonblock --channel general mempool.ops
 | 
			
		||||
ps uaxw|grep 'dist/index'|grep -v grep|grep -v services|awk '{print $2}'|xargs -n 1 kill
 | 
			
		||||
 | 
			
		||||
exit 0
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user