diff --git a/README.md b/README.md index 7b19b6138..52ee1ce07 100644 --- a/README.md +++ b/README.md @@ -1,358 +1,104 @@ # The Mempool Open Source Projectâ„¢ [![mempool](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/ry4br7/master&style=flat-square)](https://dashboard.cypress.io/projects/ry4br7/runs) -Mempool is the fully featured visualizer, explorer, and API service running on [mempool.space](https://mempool.space/), an open source project developed and operated for the benefit of the Bitcoin community, with a focus on the emerging transaction fee market to help our transition into a multi-layer ecosystem. +Mempool is the fully-featured mempool visualizer, explorer, and API service running at [mempool.space](https://mempool.space/). -![mempool](https://mempool.space/resources/screenshots/v2.3.0-dashboard.png) +![mempool](https://mempool.space/resources/screenshots/v2.4.0-dashboard.png) -## Installation Methods +# Installation Methods -Mempool can be self-hosted on a wide variety of your own hardware, ranging from a simple one-click installation on a Raspberry Pi distro, all the way to an advanced high availability cluster of powerful servers for a production instance. We support the following installation methods, ranked in order from simple to advanced: +Mempool can be self-hosted on a wide variety of your own hardware, ranging from a simple one-click installation on a Raspberry Pi full-node distro all the way to a robust production instance on a powerful FreeBSD server. -1) One-click installation on: [Umbrel](https://github.com/getumbrel/umbrel), [RaspiBlitz](https://github.com/rootzoll/raspiblitz), [RoninDojo](https://code.samourai.io/ronindojo/RoninDojo), or [MyNode](https://github.com/mynodebtc/mynode). -2) [Docker installation on Linux using docker-compose](https://github.com/mempool/mempool/tree/master/docker) -3) [Manual installation on Linux or FreeBSD](https://github.com/mempool/mempool#manual-installation) -4) [Production installation on a powerful FreeBSD server](https://github.com/mempool/mempool/tree/master/production) -5) [High Availability cluster using powerful FreeBSD servers](https://github.com/mempool/mempool/tree/master/production#high-availability) +We support the following installation methods, ranked in order from simple to advanced: -# Docker Installation +1) [One-click installation on full-node distros](#one-click-installation) +2) [Docker installation on Linux using docker-compose](./docker) +3) [Manual installation on Linux or FreeBSD](#manual-installation) +4) [Production installation on a powerful FreeBSD server](./production) -The `docker` directory contains the Dockerfiles used to build and release the official images and a `docker-compose.yml` file that is intended for end users to run a Mempool instance with minimal effort. +This doc offers install notes on the one-click method and manual install method. Follow the links above for install notes on Docker and production installations. -## bitcoind only configuration + +## One-Click Installation -To run an instance with the default settings, use the following command: +Mempool can be conveniently installed on the following full-node distros: +- [Umbrel](https://github.com/getumbrel/umbrel) +- [RaspiBlitz](https://github.com/rootzoll/raspiblitz) +- [RoninDojo](https://code.samourai.io/ronindojo/RoninDojo) +- [myNode](https://github.com/mynodebtc/mynode) +- [Start9](https://github.com/Start9Labs/embassy-os) + + +## Manual Installation + +The following instructions are for a manual installation on Linux or FreeBSD. You may need to change file and directory paths to match your OS. + +You will need [Bitcoin Core](https://github.com/bitcoin/bitcoin), [Electrum Server](https://github.com/romanz/electrs), [Node.js](https://github.com/nodejs/node), [MariaDB](https://github.com/mariadb/server), and [Nginx](https://github.com/nginx/nginx). Below, we walk through how to configure each of these. + +### 1. Get Latest Mempool Release + +Clone the Mempool repo, and checkout the latest release tag: ```bash -$ docker-compose up +$ git clone https://github.com/mempool/mempool +$ cd mempool +$ latestrelease=$(curl -s https://api.github.com/repos/mempool/mempool/releases/latest|grep tag_name|head -1|cut -d '"' -f4) +$ git checkout $latestrelease ``` -The default configuration will allow you to run Mempool using `bitcoind` as the backend, so address lookups will be disabled. It assumes you have added RPC credentials for the `mempool` user with a `mempool` password in your `bitcoin.conf` file: - -``` -rpcuser=mempool -rpcpassword=mempool -``` - -If you want to use your current credentials, update them in the `docker-compose.yml` file: - -``` - api: - environment: - MEMPOOL_BACKEND: "none" - RPC_HOST: "172.27.0.1" - RPC_PORT: "8332" - RPC_USER: "mempool" - RPC_PASS: "mempool" -``` - -Note: the IP in the example above refers to Docker's default gateway IP address so the container can hit the `bitcoind` instance running on the host machine. If your setup is different, update it accordingly. - -You can check if the instance is running by visiting http://localhost - the graphs will be populated as new transactions are detected. - -## bitcoind+electrum configuration - -In order to run with a `electrum` compatible server as the backend, in addition to the settings required for running with `bitcoind` above, you will need to make the following changes to the `docker-compose.yml` file: - -- Under the `api` service, change the value of the `MEMPOOL_BACKEND` key from `none` to `electrum`: - -``` - api: - environment: - MEMPOOL_BACKEND: "none" -``` - -- Under the `api` service, set the `ELECTRUM_HOST` and `ELECTRUM_PORT` keys to your Docker host IP address and set `ELECTRUM_TLS_ENABLED` to `false`: - -``` - api: - environment: - ELECTRUM_HOST: "172.27.0.1" - ELECTRUM_PORT: "50002" - ELECTRUM_TLS_ENABLED: "false" -``` - -You can update any of the backend settings in the `mempool-config.json` file using the following environment variables to override them under the same `api` `environment` section. - -JSON: -``` - "MEMPOOL": { - "NETWORK": "mainnet", - "BACKEND": "electrum", - "HTTP_PORT": 8999, - "SPAWN_CLUSTER_PROCS": 0, - "API_URL_PREFIX": "/api/v1/", - "POLL_RATE_MS": 2000, - "CACHE_DIR": "./cache", - "CLEAR_PROTECTION_MINUTES": 20, - "RECOMMENDED_FEE_PERCENTILE": 50, - "BLOCK_WEIGHT_UNITS": 4000000, - "INITIAL_BLOCKS_AMOUNT": 8, - "MEMPOOL_BLOCKS_AMOUNT": 8, - "PRICE_FEED_UPDATE_INTERVAL": 600, - "USE_SECOND_NODE_FOR_MINFEE": false, - "EXTERNAL_ASSETS": ["https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json"], - "STDOUT_LOG_MIN_PRIORITY": "info" - }, -``` - -docker-compose overrides: -``` - MEMPOOL_NETWORK: "" - MEMPOOL_BACKEND: "" - MEMPOOL_HTTP_PORT: "" - MEMPOOL_SPAWN_CLUSTER_PROCS: "" - MEMPOOL_API_URL_PREFIX: "" - MEMPOOL_POLL_RATE_MS: "" - MEMPOOL_CACHE_DIR: "" - MEMPOOL_CLEAR_PROTECTION_MINUTES: "" - MEMPOOL_RECOMMENDED_FEE_PERCENTILE: "" - MEMPOOL_BLOCK_WEIGHT_UNITS: "" - MEMPOOL_INITIAL_BLOCKS_AMOUNT: "" - MEMPOOL_MEMPOOL_BLOCKS_AMOUNT: "" - MEMPOOL_PRICE_FEED_UPDATE_INTERVAL: "" - MEMPOOL_USE_SECOND_NODE_FOR_MINFEE: "" - MEMPOOL_EXTERNAL_ASSETS: "" - MEMPOOL_STDOUT_LOG_MIN_PRIORITY: "" -``` - -JSON: -``` -"CORE_RPC": { - "HOST": "127.0.0.1", - "PORT": 8332, - "USERNAME": "mempool", - "PASSWORD": "mempool" - }, -``` -docker-compose overrides: -``` - CORE_RPC_HOST: "" - CORE_RPC_PORT: "" - CORE_RPC_USERNAME: "" - CORE_RPC_PASSWORD: "" -``` - -JSON: -``` - "ELECTRUM": { - "HOST": "127.0.0.1", - "PORT": 50002, - "TLS_ENABLED": true - }, -``` - -docker-compose overrides: -``` - ELECTRUM_HOST: "" - ELECTRUM_PORT: "" - ELECTRUM_TLS: "" -``` - -JSON: -``` - "ESPLORA": { - "REST_API_URL": "http://127.0.0.1:3000" - }, -``` -docker-compose overrides: -``` - ESPLORA_REST_API_URL: "" -``` - -JSON: -``` - "SECOND_CORE_RPC": { - "HOST": "127.0.0.1", - "PORT": 8332, - "USERNAME": "mempool", - "PASSWORD": "mempool" - }, -``` - -docker-compose overrides: -``` - SECOND_CORE_RPC_HOST: "" - SECOND_CORE_RPC_PORT: "" - SECOND_CORE_RPC_USERNAME: "" - SECOND_CORE_RPC_PASSWORD: "" -``` - -JSON: -``` - "DATABASE": { - "ENABLED": true, - "HOST": "127.0.0.1", - "PORT": 3306, - "DATABASE": "mempool", - "USERNAME": "mempool", - "PASSWORD": "mempool" - }, -``` - -docker-compose overrides: -``` - DATABASE_ENABLED: "" - DATABASE_HOST: "" - DATABASE_PORT: "" - DATABASE_DATABASE: "" - DATABASE_USERAME: "" - DATABASE_PASSWORD: "" -``` - -JSON: -``` - "SYSLOG": { - "ENABLED": true, - "HOST": "127.0.0.1", - "PORT": 514, - "MIN_PRIORITY": "info", - "FACILITY": "local7" - }, -``` - -docker-compose overrides: -``` - SYSLOG_ENABLED: "" - SYSLOG_HOST: "" - SYSLOG_PORT: "" - SYSLOG_MIN_PRIORITY: "" - SYSLOG_FACILITY: "" -``` - -JSON: -``` - "STATISTICS": { - "ENABLED": true, - "TX_PER_SECOND_SAMPLE_PERIOD": 150 - }, -``` - -docker-compose overrides: -``` - STATISTICS_ENABLED: "" - STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD: "" -``` - -JSON: -``` - "BISQ": { - "ENABLED": false, - "DATA_PATH": "/bisq/statsnode-data/btc_mainnet/db" - } -``` - -docker-compose overrides: -``` - BISQ_ENABLED: "" - BISQ_DATA_PATH: "" -``` - -JSON: -``` - "SOCKS5PROXY": { - "ENABLED": false, - "HOST": "127.0.0.1", - "PORT": "9050", - "USERNAME": "", - "PASSWORD": "" - } -``` - -docker-compose overrides: -``` - SOCKS5PROXY_ENABLED: "" - SOCKS5PROXY_HOST: "" - SOCKS5PROXY_PORT: "" - SOCKS5PROXY_USERNAME: "" - SOCKS5PROXY_PASSWORD: "" -``` - -JSON: -``` - "PRICE_DATA_SERVER": { - "TOR_URL": "http://wizpriceje6q5tdrxkyiazsgu7irquiqjy2dptezqhrtu7l2qelqktid.onion/getAllMarketPrices", - "CLEARNET_URL": "https://price.bisq.wiz.biz/getAllMarketPrices" - } -``` - -docker-compose overrides: -``` - PRICE_DATA_SERVER_TOR_URL: "" - PRICE_DATA_SERVER_CLEARNET_URL: "" -``` - -# Manual Installation - -The following instructions are for a manual installation on Linux or FreeBSD. The file and directory paths may need to be changed to match your OS. - -## Dependencies - -* [Bitcoin](https://github.com/bitcoin/bitcoin) -* [Electrum](https://github.com/romanz/electrs) -* [NodeJS](https://github.com/nodejs/node) -* [MariaDB](https://github.com/mariadb/server) -* [Nginx](https://github.com/nginx/nginx) - -## Mempool - -Clone the mempool repo, and checkout the latest release tag: -```bash - git clone https://github.com/mempool/mempool - cd mempool - latestrelease=$(curl -s https://api.github.com/repos/mempool/mempool/releases/latest|grep tag_name|head -1|cut -d '"' -f4) - git checkout $latestrelease -``` - -## Bitcoin Core (bitcoind) +### 2. Configure Bitcoin Core Enable RPC and txindex in `bitcoin.conf`: -```bash - rpcuser=mempool - rpcpassword=mempool - txindex=1 -``` - -## MySQL - -Install MariaDB from OS package manager: -```bash - # Linux - apt-get install mariadb-server mariadb-client - - # macOS - brew install mariadb - mysql.server start -``` - -Create database and grant privileges: -```bash - MariaDB [(none)]> drop database mempool; - Query OK, 0 rows affected (0.00 sec) - - MariaDB [(none)]> create database mempool; - Query OK, 1 row affected (0.00 sec) - - MariaDB [(none)]> grant all privileges on mempool.* to 'mempool'@'%' identified by 'mempool'; - Query OK, 0 rows affected (0.00 sec) -``` - -## Mempool Backend -Install mempool dependencies from npm and build the backend: ```bash - # backend - cd backend - npm install --prod - npm run build +rpcuser=mempool +rpcpassword=mempool +txindex=1 ``` -In the `backend` folder, make a copy of the sample config and modify it to fit your settings. +### 3. Get & Configure MySQL + +Install MariaDB from your OS package manager: ```bash - cp mempool-config.sample.json mempool-config.json +# Debian, Ubuntu, etc. +$ apt-get install mariadb-server mariadb-client + +# macOS +$ brew install mariadb +$ mysql.server start ``` -Edit `mempool-config.json` to add your Bitcoin Core node RPC credentials: +Create a database and grant privileges: + +```bash +MariaDB [(none)]> drop database mempool; +Query OK, 0 rows affected (0.00 sec) + +MariaDB [(none)]> create database mempool; +Query OK, 1 row affected (0.00 sec) + +MariaDB [(none)]> grant all privileges on mempool.* to 'mempool'@'%' identified by 'mempool'; +Query OK, 0 rows affected (0.00 sec) +``` + +### 4. Build Mempool Backend + +Install Mempool dependencies with npm and build the backend: + +```bash +$ cd backend +$ npm install --prod +$ npm run build +``` + +In the `backend` folder, make a copy of the sample config: + +```bash +$ cp mempool-config.sample.json mempool-config.json +``` + +Edit `mempool-config.json` with your Bitcoin Core node RPC credentials: + ```bash { "MEMPOOL": { @@ -385,66 +131,63 @@ Edit `mempool-config.json` to add your Bitcoin Core node RPC credentials: Start the backend: ```bash - npm run start +$ npm run start ``` -When it's running you should see output like this: +When it's running, you should see output like this: ```bash - Mempool updated in 0.189 seconds - Updating mempool - Mempool updated in 0.096 seconds - Updating mempool - Mempool updated in 0.099 seconds - Updating mempool - Calculated fee for transaction 1 / 10 - Calculated fee for transaction 2 / 10 - Calculated fee for transaction 3 / 10 - Calculated fee for transaction 4 / 10 - Calculated fee for transaction 5 / 10 - Calculated fee for transaction 6 / 10 - Calculated fee for transaction 7 / 10 - Calculated fee for transaction 8 / 10 - Calculated fee for transaction 9 / 10 - Calculated fee for transaction 10 / 10 - Mempool updated in 0.243 seconds - Updating mempool +Mempool updated in 0.189 seconds +Updating mempool +Mempool updated in 0.096 seconds +Updating mempool +Mempool updated in 0.099 seconds +Updating mempool +Calculated fee for transaction 1 / 10 +Calculated fee for transaction 2 / 10 +Calculated fee for transaction 3 / 10 +Calculated fee for transaction 4 / 10 +Calculated fee for transaction 5 / 10 +Calculated fee for transaction 6 / 10 +Calculated fee for transaction 7 / 10 +Calculated fee for transaction 8 / 10 +Calculated fee for transaction 9 / 10 +Calculated fee for transaction 10 / 10 +Mempool updated in 0.243 seconds +Updating mempool ``` -## Mempool Frontend +### 5. Build Mempool Frontend -Install mempool dependencies from npm and build the frontend static HTML/CSS/JS: +Install the Mempool dependencies with npm and build the frontend: ```bash - # frontend - cd frontend - npm install --prod - npm run build +$ cd frontend +$ npm install --prod +$ npm run build ``` -Install the output into nginx webroot folder: +Install the output into the nginx webroot folder: ```bash - sudo rsync -av --delete dist/ /var/www/ +$ sudo rsync -av --delete dist/ /var/www/ ``` -## nginx + certbot +### 6. `nginx` + `certbot` -Install the supplied nginx.conf and nginx-mempool.conf in /etc/nginx +Install the supplied `nginx.conf` and `nginx-mempool.conf` in `/etc/nginx`: ```bash - # install nginx and certbot - apt-get install -y nginx python3-certbot-nginx +# install nginx and certbot +$ apt-get install -y nginx python3-certbot-nginx - # install the mempool configuration for nginx - cp nginx.conf nginx-mempool.conf /etc/nginx/ - - # replace example.com with your domain name - certbot --nginx -d example.com +# install the mempool configuration for nginx +$ cp nginx.conf nginx-mempool.conf /etc/nginx/ +# replace example.com with your domain name +$ certbot --nginx -d example.com ``` -If everything went okay you should see the beautiful mempool :grin: +If everything went well, you should see the beautiful mempool :grin: -If you get stuck on "loading blocks", this means the websocket can't connect. -Check your nginx proxy setup, firewalls, etc. and open an issue if you need help. +If you get stuck on "loading blocks", this means the websocket can't connect. Check your nginx proxy setup, firewalls, etc. and open an issue if you need help. diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index 45b82a3a5..80c338a20 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -56,7 +56,7 @@ class DatabaseMigration { } if (DatabaseMigration.currentVersion > databaseSchemaVersion) { - logger.notice('MIGRATIONS: Upgrading datababse schema'); + logger.notice('MIGRATIONS: Upgrading database schema'); try { await this.$migrateTableSchemaFromVersion(databaseSchemaVersion); logger.notice(`MIGRATIONS: OK. Database schema have been migrated from version ${databaseSchemaVersion} to ${DatabaseMigration.currentVersion} (latest version)`); diff --git a/backend/src/routes.ts b/backend/src/routes.ts index db5b4f05f..d27fab683 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -630,6 +630,8 @@ class Routes { res.json({ hashrates: hashrates, difficulty: difficulty, + currentHashrate: await bitcoinClient.getNetworkHashPs(), + currentDifficulty: await bitcoinClient.getDifficulty(), }); } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..da7bf20e3 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,334 @@ +# Docker Installation + +This directory contains the Dockerfiles used to build and release the official images and a `docker-compose.yml` for end users to run a Mempool instance with minimal effort. + +You can choose to configure Mempool to run with a basic backend powered by just `bitcoind`, or with `bitcoind` along with an Electrum-compatible server for full functionality. + +## `bitcoind`-only Configuration + +_Note: address lookups require an Electrum server and will not work with this configuration._ + +Make sure `bitcoind` is running and synced. + +The default Docker configuration assumes you have added RPC credentials for a `mempool` user with a `mempool` password in your `bitcoin.conf` file, like so: + +``` +rpcuser=mempool +rpcpassword=mempool +``` + +If you want to use different credentials, specify them in the `docker-compose.yml` file: + +``` + api: + environment: + MEMPOOL_BACKEND: "none" + CORE_RPC_HOST: "172.27.0.1" + CORE_RPC_PORT: "8332" + CORE_RPC_USERNAME: "customuser" + CORE_RPC_PASSWORD: "custompassword" +``` + +The IP address in the example above refers to Docker's default gateway IP address so that the container can hit the `bitcoind` instance running on the host machine. If your setup is different, update it accordingly. + +Now, run: + +```bash +$ docker-compose up +``` + +Your Mempool instance should be running at http://localhost. The graphs will be populated as new transactions are detected. + +## `bitcoind` + Electrum Server Configuration + +First, configure `bitcoind` as specified above, and make sure your Electrum server is running and synced. + +Then, make sure the following variables are set in `docker-compose.yml`, as shown below, so Mempool can connect to your Electrum server: + +``` + api: + environment: + MEMPOOL_BACKEND: "electrum" + ELECTRUM_HOST: "172.27.0.1" + ELECTRUM_PORT: "50002" + ELECTRUM_TLS_ENABLED: "false" +``` + +Of course, if your Docker host IP address is different, update accordingly. + +With `bitcoind` and Electrum Server set up, run Mempool with: + +```bash +$ docker-compose up +``` + +## Further Configuration + +Optionally, you can override any other backend settings from `mempool-config.json`. + +Below we list all settings from `mempool-config.json` and the corresponding overrides you can make in the `api` > `environment` section of `docker-compose.yml`. + +
+ +`mempool-config.json`: +``` + "MEMPOOL": { + "NETWORK": "mainnet", + "BACKEND": "electrum", + "HTTP_PORT": 8999, + "SPAWN_CLUSTER_PROCS": 0, + "API_URL_PREFIX": "/api/v1/", + "POLL_RATE_MS": 2000, + "CACHE_DIR": "./cache", + "CLEAR_PROTECTION_MINUTES": 20, + "RECOMMENDED_FEE_PERCENTILE": 50, + "BLOCK_WEIGHT_UNITS": 4000000, + "INITIAL_BLOCKS_AMOUNT": 8, + "MEMPOOL_BLOCKS_AMOUNT": 8, + "PRICE_FEED_UPDATE_INTERVAL": 600, + "USE_SECOND_NODE_FOR_MINFEE": false, + "EXTERNAL_ASSETS": ["https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json"], + "STDOUT_LOG_MIN_PRIORITY": "info" + }, +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + MEMPOOL_NETWORK: "" + MEMPOOL_BACKEND: "" + MEMPOOL_HTTP_PORT: "" + MEMPOOL_SPAWN_CLUSTER_PROCS: "" + MEMPOOL_API_URL_PREFIX: "" + MEMPOOL_POLL_RATE_MS: "" + MEMPOOL_CACHE_DIR: "" + MEMPOOL_CLEAR_PROTECTION_MINUTES: "" + MEMPOOL_RECOMMENDED_FEE_PERCENTILE: "" + MEMPOOL_BLOCK_WEIGHT_UNITS: "" + MEMPOOL_INITIAL_BLOCKS_AMOUNT: "" + MEMPOOL_MEMPOOL_BLOCKS_AMOUNT: "" + MEMPOOL_PRICE_FEED_UPDATE_INTERVAL: "" + MEMPOOL_USE_SECOND_NODE_FOR_MINFEE: "" + MEMPOOL_EXTERNAL_ASSETS: "" + MEMPOOL_STDOUT_LOG_MIN_PRIORITY: "" + ... +``` + +
+ +`mempool-config.json`: +``` +"CORE_RPC": { + "HOST": "127.0.0.1", + "PORT": 8332, + "USERNAME": "mempool", + "PASSWORD": "mempool" + }, +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + CORE_RPC_HOST: "" + CORE_RPC_PORT: "" + CORE_RPC_USERNAME: "" + CORE_RPC_PASSWORD: "" + ... +``` + +
+ +`mempool-config.json`: +``` + "ELECTRUM": { + "HOST": "127.0.0.1", + "PORT": 50002, + "TLS_ENABLED": true + }, +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + ELECTRUM_HOST: "" + ELECTRUM_PORT: "" + ELECTRUM_TLS: "" + ... +``` + +
+ +`mempool-config.json`: +``` + "ESPLORA": { + "REST_API_URL": "http://127.0.0.1:3000" + }, +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + ESPLORA_REST_API_URL: "" + ... +``` + +
+ +`mempool-config.json`: +``` + "SECOND_CORE_RPC": { + "HOST": "127.0.0.1", + "PORT": 8332, + "USERNAME": "mempool", + "PASSWORD": "mempool" + }, +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + SECOND_CORE_RPC_HOST: "" + SECOND_CORE_RPC_PORT: "" + SECOND_CORE_RPC_USERNAME: "" + SECOND_CORE_RPC_PASSWORD: "" + ... +``` + +
+ +`mempool-config.json`: +``` + "DATABASE": { + "ENABLED": true, + "HOST": "127.0.0.1", + "PORT": 3306, + "DATABASE": "mempool", + "USERNAME": "mempool", + "PASSWORD": "mempool" + }, +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + DATABASE_ENABLED: "" + DATABASE_HOST: "" + DATABASE_PORT: "" + DATABASE_DATABASE: "" + DATABASE_USERAME: "" + DATABASE_PASSWORD: "" + ... +``` + +
+ +`mempool-config.json`: +``` + "SYSLOG": { + "ENABLED": true, + "HOST": "127.0.0.1", + "PORT": 514, + "MIN_PRIORITY": "info", + "FACILITY": "local7" + }, +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + SYSLOG_ENABLED: "" + SYSLOG_HOST: "" + SYSLOG_PORT: "" + SYSLOG_MIN_PRIORITY: "" + SYSLOG_FACILITY: "" + ... +``` + +
+ +`mempool-config.json`: +``` + "STATISTICS": { + "ENABLED": true, + "TX_PER_SECOND_SAMPLE_PERIOD": 150 + }, +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + STATISTICS_ENABLED: "" + STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD: "" + ... +``` + +
+ +`mempool-config.json`: +``` + "BISQ": { + "ENABLED": false, + "DATA_PATH": "/bisq/statsnode-data/btc_mainnet/db" + } +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + BISQ_ENABLED: "" + BISQ_DATA_PATH: "" + ... +``` + +
+ +`mempool-config.json`: +``` + "SOCKS5PROXY": { + "ENABLED": false, + "HOST": "127.0.0.1", + "PORT": "9050", + "USERNAME": "", + "PASSWORD": "" + } +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + SOCKS5PROXY_ENABLED: "" + SOCKS5PROXY_HOST: "" + SOCKS5PROXY_PORT: "" + SOCKS5PROXY_USERNAME: "" + SOCKS5PROXY_PASSWORD: "" + ... +``` + +
+ +`mempool-config.json`: +``` + "PRICE_DATA_SERVER": { + "TOR_URL": "http://wizpriceje6q5tdrxkyiazsgu7irquiqjy2dptezqhrtu7l2qelqktid.onion/getAllMarketPrices", + "CLEARNET_URL": "https://price.bisq.wiz.biz/getAllMarketPrices" + } +``` + +Corresponding `docker-compose.yml` overrides: +``` + api: + environment: + PRICE_DATA_SERVER_TOR_URL: "" + PRICE_DATA_SERVER_CLEARNET_URL: "" + ... +``` diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index a9485f823..13465a612 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -14,7 +14,6 @@ import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.co import { StatusViewComponent } from './components/status-view/status-view.component'; import { DashboardComponent } from './dashboard/dashboard.component'; import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component'; -import { DocsComponent } from './components/docs/docs.component'; import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component'; @@ -138,25 +137,13 @@ let routes: Routes = [ path: 'about', component: AboutComponent, }, - { - path: 'docs/api/:type', - component: DocsComponent - }, - { - path: 'docs/faq', - component: DocsComponent - }, - { - path: 'docs/api', - redirectTo: 'docs/api/rest' - }, { path: 'docs', - redirectTo: 'docs/faq' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, { path: 'api', - redirectTo: 'docs/api/rest' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, { path: 'terms-of-service', @@ -281,25 +268,13 @@ let routes: Routes = [ children: [], component: AddressComponent }, - { - path: 'docs/api/:type', - component: DocsComponent - }, - { - path: 'docs/faq', - component: DocsComponent - }, - { - path: 'docs/api', - redirectTo: 'docs/api/rest' - }, { path: 'docs', - redirectTo: 'docs/faq' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, { path: 'api', - redirectTo: 'docs/api/rest' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, ], }, @@ -421,25 +396,13 @@ let routes: Routes = [ children: [], component: AddressComponent }, - { - path: 'docs/api/:type', - component: DocsComponent - }, - { - path: 'docs/faq', - component: DocsComponent - }, - { - path: 'docs/api', - redirectTo: 'docs/api/rest' - }, { path: 'docs', - redirectTo: 'docs/faq' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, { path: 'api', - redirectTo: 'docs/api/rest' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, ], }, @@ -567,21 +530,13 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { } ] }, - { - path: 'docs/api/:type', - component: DocsComponent - }, - { - path: 'docs/api', - redirectTo: 'docs/api/rest' - }, { path: 'docs', - redirectTo: 'docs/api/rest' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, { path: 'api', - redirectTo: 'docs/api/rest' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, { path: 'about', @@ -679,21 +634,13 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { } ] }, - { - path: 'docs/api/:type', - component: DocsComponent - }, - { - path: 'docs/api', - redirectTo: 'docs/api/rest' - }, { path: 'docs', - redirectTo: 'docs/api/rest' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, { path: 'api', - redirectTo: 'docs/api/rest' + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) }, { path: 'about', @@ -743,7 +690,6 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { scrollPositionRestoration: 'enabled', anchorScrolling: 'enabled' })], - exports: [RouterModule] }) export class AppRoutingModule { } diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 27a637efc..8738c37f1 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -12,7 +12,6 @@ import { StartComponent } from './components/start/start.component'; import { ElectrsApiService } from './services/electrs-api.service'; import { TransactionComponent } from './components/transaction/transaction.component'; import { TransactionsListComponent } from './components/transactions-list/transactions-list.component'; -import { AmountComponent } from './components/amount/amount.component'; import { StateService } from './services/state.service'; import { BlockComponent } from './components/block/block.component'; import { AddressComponent } from './components/address/address.component'; @@ -20,15 +19,12 @@ import { SearchFormComponent } from './components/search-form/search-form.compon import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component'; import { WebsocketService } from './services/websocket.service'; import { AddressLabelsComponent } from './components/address-labels/address-labels.component'; -import { MempoolBlocksComponent } from './components/mempool-blocks/mempool-blocks.component'; import { MasterPageComponent } from './components/master-page/master-page.component'; import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component'; import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component'; import { AboutComponent } from './components/about/about.component'; import { TelevisionComponent } from './components/television/television.component'; import { StatisticsComponent } from './components/statistics/statistics.component'; -import { BlockchainBlocksComponent } from './components/blockchain-blocks/blockchain-blocks.component'; -import { BlockchainComponent } from './components/blockchain/blockchain.component'; import { FooterComponent } from './components/footer/footer.component'; import { AudioService } from './services/audio.service'; import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component'; @@ -53,11 +49,6 @@ import { DifficultyComponent } from './components/difficulty/difficulty.componen import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle, faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl } from '@fortawesome/free-solid-svg-icons'; -import { ApiDocsComponent } from './components/docs/api-docs.component'; -import { DocsComponent } from './components/docs/docs.component'; -import { ApiDocsNavComponent } from './components/docs/api-docs-nav.component'; -import { NoSanitizePipe } from './shared/pipes/no-sanitize.pipe'; -import { CodeTemplateComponent } from './components/docs/code-template.component'; import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component'; @@ -93,20 +84,16 @@ import { BlockFeeRatesGraphComponent } from './components/block-fee-rates-graph/ BisqMasterPageComponent, LiquidMasterPageComponent, TelevisionComponent, - BlockchainComponent, StartComponent, - BlockchainBlocksComponent, StatisticsComponent, TransactionComponent, BlockComponent, TransactionsListComponent, AddressComponent, - AmountComponent, LatestBlocksComponent, SearchFormComponent, TimeSpanComponent, AddressLabelsComponent, - MempoolBlocksComponent, FooterComponent, MempoolBlockComponent, FeeDistributionGraphComponent, @@ -122,16 +109,11 @@ import { BlockFeeRatesGraphComponent } from './components/block-fee-rates-graph/ FeesBoxComponent, DashboardComponent, DifficultyComponent, - ApiDocsComponent, - NoSanitizePipe, - CodeTemplateComponent, TermsOfServiceComponent, PrivacyPolicyComponent, TrademarkPolicyComponent, SponsorComponent, PushTransactionComponent, - DocsComponent, - ApiDocsNavComponent, AssetsNavComponent, AssetsFeaturedComponent, AssetGroupComponent, diff --git a/frontend/src/app/bisq/bisq.routing.module.ts b/frontend/src/app/bisq/bisq.routing.module.ts index 73dffee51..f7385ae63 100644 --- a/frontend/src/app/bisq/bisq.routing.module.ts +++ b/frontend/src/app/bisq/bisq.routing.module.ts @@ -7,7 +7,6 @@ import { BisqBlockComponent } from './bisq-block/bisq-block.component'; import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component'; import { BisqAddressComponent } from './bisq-address/bisq-address.component'; import { BisqStatsComponent } from './bisq-stats/bisq-stats.component'; -import { DocsComponent } from '../components/docs/docs.component'; import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; import { BisqMarketComponent } from './bisq-market/bisq-market.component'; import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component'; @@ -60,21 +59,13 @@ const routes: Routes = [ path: 'about', component: AboutComponent, }, - { - path: 'docs/api/:type', - component: DocsComponent - }, - { - path: 'docs/api', - redirectTo: 'docs/api/rest' - }, { path: 'docs', - redirectTo: 'docs/api/rest' + loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) }, { path: 'api', - redirectTo: 'docs/api/rest' + loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) }, { path: 'terms-of-service', @@ -88,6 +79,5 @@ const routes: Routes = [ @NgModule({ imports: [RouterModule.forChild(routes)], - exports: [RouterModule] }) export class BisqRoutingModule { } diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html index b01a68dc4..4223eb50f 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html @@ -5,8 +5,7 @@
Hashrate

- {{ hashrates.currentHashrate | amountShortener }} - hashes/sec + {{ hashrates.currentHashrate | amountShortener: 1 : 'H/s' }}

diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts index 2a8419c24..e2c2e1db0 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -114,23 +114,15 @@ export class HashrateChartComponent implements OnInit { difficulty: diffFixed.map(val => [val.timestamp * 1000, val.difficulty]), }); this.isLoading = false; - - if (data.hashrates.length === 0) { - this.cd.markForCheck(); - throw new Error(); - } }), map((response) => { const data = response.body; return { blockCount: parseInt(response.headers.get('x-total-count'), 10), - currentDifficulty: Math.round(data.difficulty[data.difficulty.length - 1].difficulty * 100) / 100, - currentHashrate: data.hashrates[data.hashrates.length - 1].avgHashrate, + currentDifficulty: data.currentDifficulty, + currentHashrate: data.currentHashrate, }; }), - retryWhen((errors) => errors.pipe( - delay(60000) - )) ); }), share() diff --git a/frontend/src/app/components/reward-stats/reward-stats.component.html b/frontend/src/app/components/reward-stats/reward-stats.component.html index 26c791e1a..4f2919c91 100644 --- a/frontend/src/app/components/reward-stats/reward-stats.component.html +++ b/frontend/src/app/components/reward-stats/reward-stats.component.html @@ -43,21 +43,21 @@
-
Low priority
+
Miners Reward
-
Medium priority
+
Reward Per Tx
-
High priority
+
Average Fee
diff --git a/frontend/src/app/components/docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts similarity index 100% rename from frontend/src/app/components/docs/api-docs-data.ts rename to frontend/src/app/docs/api-docs/api-docs-data.ts diff --git a/frontend/src/app/components/docs/api-docs-nav.component.html b/frontend/src/app/docs/api-docs/api-docs-nav.component.html similarity index 100% rename from frontend/src/app/components/docs/api-docs-nav.component.html rename to frontend/src/app/docs/api-docs/api-docs-nav.component.html diff --git a/frontend/src/app/components/docs/api-docs-nav.component.scss b/frontend/src/app/docs/api-docs/api-docs-nav.component.scss similarity index 100% rename from frontend/src/app/components/docs/api-docs-nav.component.scss rename to frontend/src/app/docs/api-docs/api-docs-nav.component.scss diff --git a/frontend/src/app/components/docs/api-docs-nav.component.ts b/frontend/src/app/docs/api-docs/api-docs-nav.component.ts similarity index 100% rename from frontend/src/app/components/docs/api-docs-nav.component.ts rename to frontend/src/app/docs/api-docs/api-docs-nav.component.ts diff --git a/frontend/src/app/components/docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html similarity index 100% rename from frontend/src/app/components/docs/api-docs.component.html rename to frontend/src/app/docs/api-docs/api-docs.component.html diff --git a/frontend/src/app/components/docs/api-docs.component.scss b/frontend/src/app/docs/api-docs/api-docs.component.scss similarity index 100% rename from frontend/src/app/components/docs/api-docs.component.scss rename to frontend/src/app/docs/api-docs/api-docs.component.scss diff --git a/frontend/src/app/components/docs/api-docs.component.ts b/frontend/src/app/docs/api-docs/api-docs.component.ts similarity index 97% rename from frontend/src/app/components/docs/api-docs.component.ts rename to frontend/src/app/docs/api-docs/api-docs.component.ts index 39f611310..6c645a14f 100644 --- a/frontend/src/app/components/docs/api-docs.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core'; -import { Env, StateService } from 'src/app/services/state.service'; +import { Env, StateService } from '../../services/state.service'; import { Observable, merge, of } from 'rxjs'; -import { SeoService } from 'src/app/services/seo.service'; +import { SeoService } from '../../services/seo.service'; import { tap } from 'rxjs/operators'; import { ActivatedRoute } from "@angular/router"; import { faqData, restApiDocsData, wsApiDocsData } from './api-docs-data'; diff --git a/frontend/src/app/components/docs/code-template.component.html b/frontend/src/app/docs/code-template/code-template.component.html similarity index 100% rename from frontend/src/app/components/docs/code-template.component.html rename to frontend/src/app/docs/code-template/code-template.component.html diff --git a/frontend/src/app/components/docs/code-template.component.scss b/frontend/src/app/docs/code-template/code-template.component.scss similarity index 100% rename from frontend/src/app/components/docs/code-template.component.scss rename to frontend/src/app/docs/code-template/code-template.component.scss diff --git a/frontend/src/app/components/docs/code-template.component.ts b/frontend/src/app/docs/code-template/code-template.component.ts similarity index 99% rename from frontend/src/app/components/docs/code-template.component.ts rename to frontend/src/app/docs/code-template/code-template.component.ts index cc7b8254e..69478f26c 100644 --- a/frontend/src/app/components/docs/code-template.component.ts +++ b/frontend/src/app/docs/code-template/code-template.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { Env, StateService } from 'src/app/services/state.service'; +import { Env, StateService } from '../../services/state.service'; @Component({ selector: 'app-code-template', diff --git a/frontend/src/app/docs/docs.module.ts b/frontend/src/app/docs/docs.module.ts new file mode 100644 index 000000000..134886c5d --- /dev/null +++ b/frontend/src/app/docs/docs.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '../shared/shared.module'; +import { ApiDocsComponent } from './/api-docs/api-docs.component'; +import { DocsComponent } from './docs/docs.component'; +import { ApiDocsNavComponent } from './api-docs/api-docs-nav.component'; +import { CodeTemplateComponent } from './code-template/code-template.component'; +import { DocsRoutingModule } from './docs.routing.module'; +@NgModule({ + declarations: [ + ApiDocsComponent, + CodeTemplateComponent, + ApiDocsNavComponent, + DocsComponent, + ], + imports: [ + CommonModule, + SharedModule, + DocsRoutingModule, + ] +}) +export class DocsModule { } diff --git a/frontend/src/app/docs/docs.routing.module.ts b/frontend/src/app/docs/docs.routing.module.ts new file mode 100644 index 000000000..52eb54f2e --- /dev/null +++ b/frontend/src/app/docs/docs.routing.module.ts @@ -0,0 +1,58 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { DocsComponent } from './docs/docs.component'; + +const browserWindow = window || {}; +// @ts-ignore +const browserWindowEnv = browserWindow.__env || {}; + +let routes: Routes = []; + +if (browserWindowEnv.BASE_MODULE && (browserWindowEnv.BASE_MODULE === 'bisq' || browserWindowEnv.BASE_MODULE === 'liquid')) { + routes = [ + { + path: '', + redirectTo: 'api/rest' + }, + { + path: 'api/:type', + component: DocsComponent + }, + { + path: 'api', + redirectTo: 'api/rest' + }, + { + path: '**', + redirectTo: 'api/rest' + } + ]; +} else { + routes = [ + { + path: '', + redirectTo: 'faq' + }, + { + path: 'api/:type', + component: DocsComponent + }, + { + path: 'faq', + component: DocsComponent + }, + { + path: 'api', + redirectTo: 'api/rest' + }, + { + path: '**', + redirectTo: 'api/faq' + } + ]; +} + +@NgModule({ + imports: [RouterModule.forChild(routes)], +}) +export class DocsRoutingModule { } diff --git a/frontend/src/app/components/docs/docs.component.html b/frontend/src/app/docs/docs/docs.component.html similarity index 100% rename from frontend/src/app/components/docs/docs.component.html rename to frontend/src/app/docs/docs/docs.component.html diff --git a/frontend/src/app/components/docs/docs.component.scss b/frontend/src/app/docs/docs/docs.component.scss similarity index 100% rename from frontend/src/app/components/docs/docs.component.scss rename to frontend/src/app/docs/docs/docs.component.scss diff --git a/frontend/src/app/components/docs/docs.component.ts b/frontend/src/app/docs/docs/docs.component.ts similarity index 84% rename from frontend/src/app/components/docs/docs.component.ts rename to frontend/src/app/docs/docs/docs.component.ts index 2d0d0b55a..9a06f1127 100644 --- a/frontend/src/app/components/docs/docs.component.ts +++ b/frontend/src/app/docs/docs/docs.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, HostBinding } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Env, StateService } from 'src/app/services/state.service'; -import { WebsocketService } from 'src/app/services/websocket.service'; +import { Env, StateService } from '../../services/state.service'; +import { WebsocketService } from '../../services/websocket.service'; @Component({ selector: 'app-docs', @@ -26,9 +26,9 @@ export class DocsComponent implements OnInit { ngOnInit(): void { this.websocket.want(['blocks']); const url = this.route.snapshot.url; - if( url[1].path === "faq" ) { + if (url[0].path === "faq" ) { this.activeTab = 0; - } else if( url[2].path === "rest" ) { + } else if( url[1].path === "rest" ) { this.activeTab = 1; } else { this.activeTab = 2; diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index b29303c27..aa23bb639 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -12,6 +12,7 @@ import { RelativeUrlPipe } from './pipes/relative-url/relative-url.pipe'; import { ScriptpubkeyTypePipe } from './pipes/scriptpubkey-type-pipe/scriptpubkey-type.pipe'; import { BytesPipe } from './pipes/bytes-pipe/bytes.pipe'; import { WuBytesPipe } from './pipes/bytes-pipe/wubytes.pipe'; +import { BlockchainComponent } from '../components/blockchain/blockchain.component'; import { TimeSinceComponent } from '../components/time-since/time-since.component'; import { TimeUntilComponent } from '../components/time-until/time-until.component'; import { ClipboardComponent } from '../components/clipboard/clipboard.component'; @@ -23,6 +24,11 @@ import { TxFeeRatingComponent } from '../components/tx-fee-rating/tx-fee-rating. import { ReactiveFormsModule } from '@angular/forms'; import { LanguageSelectorComponent } from '../components/language-selector/language-selector.component'; import { ColoredPriceDirective } from './directives/colored-price.directive'; +import { NoSanitizePipe } from './pipes/no-sanitize.pipe'; +import { MempoolBlocksComponent } from '../components/mempool-blocks/mempool-blocks.component'; +import { BlockchainBlocksComponent } from '../components/blockchain-blocks/blockchain-blocks.component'; +import { AmountComponent } from '../components/amount/amount.component'; +import { RouterModule } from '@angular/router'; @NgModule({ declarations: [ @@ -36,6 +42,7 @@ import { ColoredPriceDirective } from './directives/colored-price.directive'; LanguageSelectorComponent, ScriptpubkeyTypePipe, RelativeUrlPipe, + NoSanitizePipe, Hex2asciiPipe, AsmStylerPipe, AbsolutePipe, @@ -47,9 +54,14 @@ import { ColoredPriceDirective } from './directives/colored-price.directive'; Decimal2HexPipe, FeeRoundingPipe, ColoredPriceDirective, + BlockchainComponent, + MempoolBlocksComponent, + BlockchainBlocksComponent, + AmountComponent, ], imports: [ CommonModule, + RouterModule, ReactiveFormsModule, NgbNavModule, NgbTooltipModule, @@ -61,8 +73,10 @@ import { ColoredPriceDirective } from './directives/colored-price.directive'; providers: [ VbytesPipe, RelativeUrlPipe, + NoSanitizePipe, ], exports: [ + RouterModule, NgbAccordionModule, NgbNavModule, CommonModule, @@ -92,6 +106,11 @@ import { ColoredPriceDirective } from './directives/colored-price.directive'; Decimal2HexPipe, FeeRoundingPipe, ColoredPriceDirective, + NoSanitizePipe, + BlockchainComponent, + MempoolBlocksComponent, + BlockchainBlocksComponent, + AmountComponent, ] }) export class SharedModule {} diff --git a/frontend/src/resources/screenshots/v2.4.0-dashboard.png b/frontend/src/resources/screenshots/v2.4.0-dashboard.png new file mode 100644 index 000000000..7fa95e441 Binary files /dev/null and b/frontend/src/resources/screenshots/v2.4.0-dashboard.png differ