Merge branch 'master' into frontend_runtime_config
This commit is contained in:
commit
235ac204b4
@ -2,6 +2,7 @@
|
|||||||
"MEMPOOL": {
|
"MEMPOOL": {
|
||||||
"NETWORK": "mainnet",
|
"NETWORK": "mainnet",
|
||||||
"BACKEND": "electrum",
|
"BACKEND": "electrum",
|
||||||
|
"ENABLED": true,
|
||||||
"HTTP_PORT": 8999,
|
"HTTP_PORT": 8999,
|
||||||
"SPAWN_CLUSTER_PROCS": 0,
|
"SPAWN_CLUSTER_PROCS": 0,
|
||||||
"API_URL_PREFIX": "/api/v1/",
|
"API_URL_PREFIX": "/api/v1/",
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
"MEMPOOL": {
|
"MEMPOOL": {
|
||||||
|
"ENABLED": true,
|
||||||
"NETWORK": "__MEMPOOL_NETWORK__",
|
"NETWORK": "__MEMPOOL_NETWORK__",
|
||||||
"BACKEND": "__MEMPOOL_BACKEND__",
|
"BACKEND": "__MEMPOOL_BACKEND__",
|
||||||
|
"ENABLED": true,
|
||||||
"BLOCKS_SUMMARIES_INDEXING": true,
|
"BLOCKS_SUMMARIES_INDEXING": true,
|
||||||
"HTTP_PORT": 1,
|
"HTTP_PORT": 1,
|
||||||
"SPAWN_CLUSTER_PROCS": 2,
|
"SPAWN_CLUSTER_PROCS": 2,
|
||||||
|
@ -13,6 +13,7 @@ describe('Mempool Backend Config', () => {
|
|||||||
const config = jest.requireActual('../config').default;
|
const config = jest.requireActual('../config').default;
|
||||||
|
|
||||||
expect(config.MEMPOOL).toStrictEqual({
|
expect(config.MEMPOOL).toStrictEqual({
|
||||||
|
ENABLED: true,
|
||||||
NETWORK: 'mainnet',
|
NETWORK: 'mainnet',
|
||||||
BACKEND: 'none',
|
BACKEND: 'none',
|
||||||
BLOCKS_SUMMARIES_INDEXING: false,
|
BLOCKS_SUMMARIES_INDEXING: false,
|
||||||
|
@ -103,12 +103,11 @@ class Mempool {
|
|||||||
return txTimes;
|
return txTimes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $updateMempool() {
|
public async $updateMempool(): Promise<void> {
|
||||||
logger.debug('Updating mempool');
|
logger.debug(`Updating mempool...`);
|
||||||
const start = new Date().getTime();
|
const start = new Date().getTime();
|
||||||
let hasChange: boolean = false;
|
let hasChange: boolean = false;
|
||||||
const currentMempoolSize = Object.keys(this.mempoolCache).length;
|
const currentMempoolSize = Object.keys(this.mempoolCache).length;
|
||||||
let txCount = 0;
|
|
||||||
const transactions = await bitcoinApi.$getRawMempool();
|
const transactions = await bitcoinApi.$getRawMempool();
|
||||||
const diff = transactions.length - currentMempoolSize;
|
const diff = transactions.length - currentMempoolSize;
|
||||||
const newTransactions: TransactionExtended[] = [];
|
const newTransactions: TransactionExtended[] = [];
|
||||||
@ -124,7 +123,6 @@ class Mempool {
|
|||||||
try {
|
try {
|
||||||
const transaction = await transactionUtils.$getTransactionExtended(txid);
|
const transaction = await transactionUtils.$getTransactionExtended(txid);
|
||||||
this.mempoolCache[txid] = transaction;
|
this.mempoolCache[txid] = transaction;
|
||||||
txCount++;
|
|
||||||
if (this.inSync) {
|
if (this.inSync) {
|
||||||
this.txPerSecondArray.push(new Date().getTime());
|
this.txPerSecondArray.push(new Date().getTime());
|
||||||
this.vBytesPerSecondArray.push({
|
this.vBytesPerSecondArray.push({
|
||||||
@ -133,14 +131,9 @@ class Mempool {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
hasChange = true;
|
hasChange = true;
|
||||||
if (diff > 0) {
|
|
||||||
logger.debug('Fetched transaction ' + txCount + ' / ' + diff);
|
|
||||||
} else {
|
|
||||||
logger.debug('Fetched transaction ' + txCount);
|
|
||||||
}
|
|
||||||
newTransactions.push(transaction);
|
newTransactions.push(transaction);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
logger.debug(`Error finding transaction '${txid}' in the mempool: ` + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,8 +190,7 @@ class Mempool {
|
|||||||
|
|
||||||
const end = new Date().getTime();
|
const end = new Date().getTime();
|
||||||
const time = end - start;
|
const time = end - start;
|
||||||
logger.debug(`New mempool size: ${Object.keys(this.mempoolCache).length} Change: ${diff}`);
|
logger.debug(`Mempool updated in ${time / 1000} seconds. New size: ${Object.keys(this.mempoolCache).length} (${diff > 0 ? '+' + diff : diff})`);
|
||||||
logger.debug('Mempool updated in ' + time / 1000 + ' seconds');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleRbfTransactions(rbfTransactions: { [txid: string]: TransactionExtended; }) {
|
public handleRbfTransactions(rbfTransactions: { [txid: string]: TransactionExtended; }) {
|
||||||
|
@ -27,6 +27,7 @@ class MiningRoutes {
|
|||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/difficulty-adjustments/:interval', this.$getDifficultyAdjustments)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/difficulty-adjustments/:interval', this.$getDifficultyAdjustments)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/predictions/:interval', this.$getHistoricalBlockPrediction)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/predictions/:interval', this.$getHistoricalBlockPrediction)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/audit/:hash', this.$getBlockAudit)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/audit/:hash', this.$getBlockAudit)
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/timestamp/:timestamp', this.$getHeightFromTimestamp)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +253,29 @@ class MiningRoutes {
|
|||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async $getHeightFromTimestamp(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const timestamp = parseInt(req.params.timestamp, 10);
|
||||||
|
// This will prevent people from entering milliseconds etc.
|
||||||
|
// Block timestamps are allowed to be up to 2 hours off, so 24 hours
|
||||||
|
// will never put the maximum value before the most recent block
|
||||||
|
const nowPlus1day = Math.floor(Date.now() / 1000) + 60 * 60 * 24;
|
||||||
|
// Prevent non-integers that are not seconds
|
||||||
|
if (!/^[1-9][0-9]*$/.test(req.params.timestamp) || timestamp > nowPlus1day) {
|
||||||
|
throw new Error(`Invalid timestamp, value must be Unix seconds`);
|
||||||
|
}
|
||||||
|
const result = await BlocksRepository.$getBlockHeightFromTimestamp(
|
||||||
|
timestamp,
|
||||||
|
);
|
||||||
|
res.header('Pragma', 'public');
|
||||||
|
res.header('Cache-control', 'public');
|
||||||
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
||||||
|
res.json(result);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new MiningRoutes();
|
export default new MiningRoutes();
|
||||||
|
@ -4,6 +4,7 @@ const configFromFile = require(
|
|||||||
|
|
||||||
interface IConfig {
|
interface IConfig {
|
||||||
MEMPOOL: {
|
MEMPOOL: {
|
||||||
|
ENABLED: boolean;
|
||||||
NETWORK: 'mainnet' | 'testnet' | 'signet' | 'liquid' | 'liquidtestnet';
|
NETWORK: 'mainnet' | 'testnet' | 'signet' | 'liquid' | 'liquidtestnet';
|
||||||
BACKEND: 'esplora' | 'electrum' | 'none';
|
BACKEND: 'esplora' | 'electrum' | 'none';
|
||||||
HTTP_PORT: number;
|
HTTP_PORT: number;
|
||||||
@ -119,6 +120,7 @@ interface IConfig {
|
|||||||
|
|
||||||
const defaults: IConfig = {
|
const defaults: IConfig = {
|
||||||
'MEMPOOL': {
|
'MEMPOOL': {
|
||||||
|
'ENABLED': true,
|
||||||
'NETWORK': 'mainnet',
|
'NETWORK': 'mainnet',
|
||||||
'BACKEND': 'none',
|
'BACKEND': 'none',
|
||||||
'HTTP_PORT': 8999,
|
'HTTP_PORT': 8999,
|
||||||
@ -224,11 +226,11 @@ const defaults: IConfig = {
|
|||||||
'BISQ_URL': 'https://bisq.markets/api',
|
'BISQ_URL': 'https://bisq.markets/api',
|
||||||
'BISQ_ONION': 'http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api'
|
'BISQ_ONION': 'http://bisqmktse2cabavbr2xjq7xw3h6g5ottemo5rolfcwt6aly6tp5fdryd.onion/api'
|
||||||
},
|
},
|
||||||
"MAXMIND": {
|
'MAXMIND': {
|
||||||
'ENABLED': false,
|
'ENABLED': false,
|
||||||
"GEOLITE2_CITY": "/usr/local/share/GeoIP/GeoLite2-City.mmdb",
|
'GEOLITE2_CITY': '/usr/local/share/GeoIP/GeoLite2-City.mmdb',
|
||||||
"GEOLITE2_ASN": "/usr/local/share/GeoIP/GeoLite2-ASN.mmdb",
|
'GEOLITE2_ASN': '/usr/local/share/GeoIP/GeoLite2-ASN.mmdb',
|
||||||
"GEOIP2_ISP": "/usr/local/share/GeoIP/GeoIP2-ISP.mmdb"
|
'GEOIP2_ISP': '/usr/local/share/GeoIP/GeoIP2-ISP.mmdb'
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import express from "express";
|
import express from 'express';
|
||||||
import { Application, Request, Response, NextFunction } from 'express';
|
import { Application, Request, Response, NextFunction } from 'express';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as WebSocket from 'ws';
|
import * as WebSocket from 'ws';
|
||||||
@ -34,7 +34,7 @@ import miningRoutes from './api/mining/mining-routes';
|
|||||||
import bisqRoutes from './api/bisq/bisq.routes';
|
import bisqRoutes from './api/bisq/bisq.routes';
|
||||||
import liquidRoutes from './api/liquid/liquid.routes';
|
import liquidRoutes from './api/liquid/liquid.routes';
|
||||||
import bitcoinRoutes from './api/bitcoin/bitcoin.routes';
|
import bitcoinRoutes from './api/bitcoin/bitcoin.routes';
|
||||||
import fundingTxFetcher from "./tasks/lightning/sync-tasks/funding-tx-fetcher";
|
import fundingTxFetcher from './tasks/lightning/sync-tasks/funding-tx-fetcher';
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
@ -74,7 +74,7 @@ class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async startServer(worker = false) {
|
async startServer(worker = false): Promise<void> {
|
||||||
logger.notice(`Starting Mempool Server${worker ? ' (worker)' : ''}... (${backendInfo.getShortCommitHash()})`);
|
logger.notice(`Starting Mempool Server${worker ? ' (worker)' : ''}... (${backendInfo.getShortCommitHash()})`);
|
||||||
|
|
||||||
this.app
|
this.app
|
||||||
@ -92,7 +92,9 @@ class Server {
|
|||||||
this.setUpWebsocketHandling();
|
this.setUpWebsocketHandling();
|
||||||
|
|
||||||
await syncAssets.syncAssets$();
|
await syncAssets.syncAssets$();
|
||||||
diskCache.loadMempoolCache();
|
if (config.MEMPOOL.ENABLED) {
|
||||||
|
diskCache.loadMempoolCache();
|
||||||
|
}
|
||||||
|
|
||||||
if (config.DATABASE.ENABLED) {
|
if (config.DATABASE.ENABLED) {
|
||||||
await DB.checkDbConnection();
|
await DB.checkDbConnection();
|
||||||
@ -127,7 +129,10 @@ class Server {
|
|||||||
fiatConversion.startService();
|
fiatConversion.startService();
|
||||||
|
|
||||||
this.setUpHttpApiRoutes();
|
this.setUpHttpApiRoutes();
|
||||||
this.runMainUpdateLoop();
|
|
||||||
|
if (config.MEMPOOL.ENABLED) {
|
||||||
|
this.runMainUpdateLoop();
|
||||||
|
}
|
||||||
|
|
||||||
if (config.BISQ.ENABLED) {
|
if (config.BISQ.ENABLED) {
|
||||||
bisq.startBisqService();
|
bisq.startBisqService();
|
||||||
@ -149,7 +154,7 @@ class Server {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async runMainUpdateLoop() {
|
async runMainUpdateLoop(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
await memPool.$updateMemPoolInfo();
|
await memPool.$updateMemPoolInfo();
|
||||||
@ -183,7 +188,7 @@ class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async $runLightningBackend() {
|
async $runLightningBackend(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await fundingTxFetcher.$init();
|
await fundingTxFetcher.$init();
|
||||||
await networkSyncService.$startService();
|
await networkSyncService.$startService();
|
||||||
@ -195,7 +200,7 @@ class Server {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpWebsocketHandling() {
|
setUpWebsocketHandling(): void {
|
||||||
if (this.wss) {
|
if (this.wss) {
|
||||||
websocketHandler.setWebsocketServer(this.wss);
|
websocketHandler.setWebsocketServer(this.wss);
|
||||||
}
|
}
|
||||||
@ -209,19 +214,21 @@ class Server {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
websocketHandler.setupConnectionHandling();
|
websocketHandler.setupConnectionHandling();
|
||||||
statistics.setNewStatisticsEntryCallback(websocketHandler.handleNewStatistic.bind(websocketHandler));
|
if (config.MEMPOOL.ENABLED) {
|
||||||
blocks.setNewBlockCallback(websocketHandler.handleNewBlock.bind(websocketHandler));
|
statistics.setNewStatisticsEntryCallback(websocketHandler.handleNewStatistic.bind(websocketHandler));
|
||||||
memPool.setMempoolChangedCallback(websocketHandler.handleMempoolChange.bind(websocketHandler));
|
blocks.setNewBlockCallback(websocketHandler.handleNewBlock.bind(websocketHandler));
|
||||||
|
memPool.setMempoolChangedCallback(websocketHandler.handleMempoolChange.bind(websocketHandler));
|
||||||
|
}
|
||||||
fiatConversion.setProgressChangedCallback(websocketHandler.handleNewConversionRates.bind(websocketHandler));
|
fiatConversion.setProgressChangedCallback(websocketHandler.handleNewConversionRates.bind(websocketHandler));
|
||||||
loadingIndicators.setProgressChangedCallback(websocketHandler.handleLoadingChanged.bind(websocketHandler));
|
loadingIndicators.setProgressChangedCallback(websocketHandler.handleLoadingChanged.bind(websocketHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpHttpApiRoutes() {
|
setUpHttpApiRoutes(): void {
|
||||||
bitcoinRoutes.initRoutes(this.app);
|
bitcoinRoutes.initRoutes(this.app);
|
||||||
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED) {
|
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && config.MEMPOOL.ENABLED) {
|
||||||
statisticsRoutes.initRoutes(this.app);
|
statisticsRoutes.initRoutes(this.app);
|
||||||
}
|
}
|
||||||
if (Common.indexingEnabled()) {
|
if (Common.indexingEnabled() && config.MEMPOOL.ENABLED) {
|
||||||
miningRoutes.initRoutes(this.app);
|
miningRoutes.initRoutes(this.app);
|
||||||
}
|
}
|
||||||
if (config.BISQ.ENABLED) {
|
if (config.BISQ.ENABLED) {
|
||||||
@ -238,4 +245,4 @@ class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = new Server();
|
((): Server => new Server())();
|
||||||
|
@ -392,6 +392,36 @@ class BlocksRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first block at or directly after a given timestamp
|
||||||
|
* @param timestamp number unix time in seconds
|
||||||
|
* @returns The height and timestamp of a block (timestamp might vary from given timestamp)
|
||||||
|
*/
|
||||||
|
public async $getBlockHeightFromTimestamp(
|
||||||
|
timestamp: number,
|
||||||
|
): Promise<{ height: number; hash: string; timestamp: number }> {
|
||||||
|
try {
|
||||||
|
// Get first block at or after the given timestamp
|
||||||
|
const query = `SELECT height, hash, blockTimestamp as timestamp FROM blocks
|
||||||
|
WHERE blockTimestamp <= FROM_UNIXTIME(?)
|
||||||
|
ORDER BY blockTimestamp DESC
|
||||||
|
LIMIT 1`;
|
||||||
|
const params = [timestamp];
|
||||||
|
const [rows]: any[][] = await DB.query(query, params);
|
||||||
|
if (rows.length === 0) {
|
||||||
|
throw new Error(`No block was found before timestamp ${timestamp}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows[0];
|
||||||
|
} catch (e) {
|
||||||
|
logger.err(
|
||||||
|
'Cannot get block height from timestamp from the db. Reason: ' +
|
||||||
|
(e instanceof Error ? e.message : e),
|
||||||
|
);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return blocks height
|
* Return blocks height
|
||||||
*/
|
*/
|
||||||
|
@ -89,6 +89,7 @@ Below we list all settings from `mempool-config.json` and the corresponding over
|
|||||||
"MEMPOOL": {
|
"MEMPOOL": {
|
||||||
"NETWORK": "mainnet",
|
"NETWORK": "mainnet",
|
||||||
"BACKEND": "electrum",
|
"BACKEND": "electrum",
|
||||||
|
"ENABLED": true,
|
||||||
"HTTP_PORT": 8999,
|
"HTTP_PORT": 8999,
|
||||||
"SPAWN_CLUSTER_PROCS": 0,
|
"SPAWN_CLUSTER_PROCS": 0,
|
||||||
"API_URL_PREFIX": "/api/v1/",
|
"API_URL_PREFIX": "/api/v1/",
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"MEMPOOL": {
|
"MEMPOOL": {
|
||||||
"NETWORK": "__MEMPOOL_NETWORK__",
|
"NETWORK": "__MEMPOOL_NETWORK__",
|
||||||
"BACKEND": "__MEMPOOL_BACKEND__",
|
"BACKEND": "__MEMPOOL_BACKEND__",
|
||||||
|
"ENABLED": __MEMPOOL_ENABLED__,
|
||||||
"HTTP_PORT": __MEMPOOL_HTTP_PORT__,
|
"HTTP_PORT": __MEMPOOL_HTTP_PORT__,
|
||||||
"SPAWN_CLUSTER_PROCS": __MEMPOOL_SPAWN_CLUSTER_PROCS__,
|
"SPAWN_CLUSTER_PROCS": __MEMPOOL_SPAWN_CLUSTER_PROCS__,
|
||||||
"API_URL_PREFIX": "__MEMPOOL_API_URL_PREFIX__",
|
"API_URL_PREFIX": "__MEMPOOL_API_URL_PREFIX__",
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
# MEMPOOL
|
# MEMPOOL
|
||||||
__MEMPOOL_NETWORK__=${MEMPOOL_NETWORK:=mainnet}
|
__MEMPOOL_NETWORK__=${MEMPOOL_NETWORK:=mainnet}
|
||||||
__MEMPOOL_BACKEND__=${MEMPOOL_BACKEND:=electrum}
|
__MEMPOOL_BACKEND__=${MEMPOOL_BACKEND:=electrum}
|
||||||
|
__MEMPOOL_ENABLED__=${MEMPOOL_ENABLED:=true}
|
||||||
__MEMPOOL_HTTP_PORT__=${BACKEND_HTTP_PORT:=8999}
|
__MEMPOOL_HTTP_PORT__=${BACKEND_HTTP_PORT:=8999}
|
||||||
__MEMPOOL_SPAWN_CLUSTER_PROCS__=${MEMPOOL_SPAWN_CLUSTER_PROCS:=0}
|
__MEMPOOL_SPAWN_CLUSTER_PROCS__=${MEMPOOL_SPAWN_CLUSTER_PROCS:=0}
|
||||||
__MEMPOOL_API_URL_PREFIX__=${MEMPOOL_API_URL_PREFIX:=/api/v1/}
|
__MEMPOOL_API_URL_PREFIX__=${MEMPOOL_API_URL_PREFIX:=/api/v1/}
|
||||||
@ -111,6 +112,7 @@ mkdir -p "${__MEMPOOL_CACHE_DIR__}"
|
|||||||
|
|
||||||
sed -i "s/__MEMPOOL_NETWORK__/${__MEMPOOL_NETWORK__}/g" mempool-config.json
|
sed -i "s/__MEMPOOL_NETWORK__/${__MEMPOOL_NETWORK__}/g" mempool-config.json
|
||||||
sed -i "s/__MEMPOOL_BACKEND__/${__MEMPOOL_BACKEND__}/g" mempool-config.json
|
sed -i "s/__MEMPOOL_BACKEND__/${__MEMPOOL_BACKEND__}/g" mempool-config.json
|
||||||
|
sed -i "s/__MEMPOOL_ENABLED__/${__MEMPOOL_ENABLED__}/g" mempool-config.json
|
||||||
sed -i "s/__MEMPOOL_HTTP_PORT__/${__MEMPOOL_HTTP_PORT__}/g" mempool-config.json
|
sed -i "s/__MEMPOOL_HTTP_PORT__/${__MEMPOOL_HTTP_PORT__}/g" mempool-config.json
|
||||||
sed -i "s/__MEMPOOL_SPAWN_CLUSTER_PROCS__/${__MEMPOOL_SPAWN_CLUSTER_PROCS__}/g" mempool-config.json
|
sed -i "s/__MEMPOOL_SPAWN_CLUSTER_PROCS__/${__MEMPOOL_SPAWN_CLUSTER_PROCS__}/g" mempool-config.json
|
||||||
sed -i "s!__MEMPOOL_API_URL_PREFIX__!${__MEMPOOL_API_URL_PREFIX__}!g" mempool-config.json
|
sed -i "s!__MEMPOOL_API_URL_PREFIX__!${__MEMPOOL_API_URL_PREFIX__}!g" mempool-config.json
|
||||||
|
@ -221,6 +221,10 @@
|
|||||||
"proxyConfig": "proxy.conf.local.js",
|
"proxyConfig": "proxy.conf.local.js",
|
||||||
"verbose": true
|
"verbose": true
|
||||||
},
|
},
|
||||||
|
"local-esplora": {
|
||||||
|
"proxyConfig": "proxy.conf.local-esplora.js",
|
||||||
|
"verbose": true
|
||||||
|
},
|
||||||
"mixed": {
|
"mixed": {
|
||||||
"proxyConfig": "proxy.conf.mixed.js",
|
"proxyConfig": "proxy.conf.mixed.js",
|
||||||
"verbose": true
|
"verbose": true
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"serve:local-prod": "npm run generate-config && npm run ng -- serve -c local-prod",
|
"serve:local-prod": "npm run generate-config && npm run ng -- serve -c local-prod",
|
||||||
"serve:local-staging": "npm run generate-config && npm run ng -- serve -c local-staging",
|
"serve:local-staging": "npm run generate-config && npm run ng -- serve -c local-staging",
|
||||||
"start": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local",
|
"start": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local",
|
||||||
|
"start:local-esplora": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-esplora",
|
||||||
"start:stg": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c staging",
|
"start:stg": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c staging",
|
||||||
"start:local-prod": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-prod",
|
"start:local-prod": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-prod",
|
||||||
"start:local-staging": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-staging",
|
"start:local-staging": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-staging",
|
||||||
|
137
frontend/proxy.conf.local-esplora.js
Normal file
137
frontend/proxy.conf.local-esplora.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const FRONTEND_CONFIG_FILE_NAME = 'mempool-frontend-config.json';
|
||||||
|
|
||||||
|
let configContent;
|
||||||
|
|
||||||
|
// Read frontend config
|
||||||
|
try {
|
||||||
|
const rawConfig = fs.readFileSync(FRONTEND_CONFIG_FILE_NAME);
|
||||||
|
configContent = JSON.parse(rawConfig);
|
||||||
|
console.log(`${FRONTEND_CONFIG_FILE_NAME} file found, using provided config`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
if (e.code !== 'ENOENT') {
|
||||||
|
throw new Error(e);
|
||||||
|
} else {
|
||||||
|
console.log(`${FRONTEND_CONFIG_FILE_NAME} file not found, using default config`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let PROXY_CONFIG = [];
|
||||||
|
|
||||||
|
if (configContent && configContent.BASE_MODULE === 'liquid') {
|
||||||
|
PROXY_CONFIG.push(...[
|
||||||
|
{
|
||||||
|
context: ['/liquid/api/v1/**'],
|
||||||
|
target: `http://127.0.0.1:8999`,
|
||||||
|
secure: false,
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
pathRewrite: {
|
||||||
|
"^/liquid": ""
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/liquid/api/**'],
|
||||||
|
target: `http://127.0.0.1:3000`,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
pathRewrite: {
|
||||||
|
"^/liquid/api/": ""
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/liquidtestnet/api/v1/**'],
|
||||||
|
target: `http://127.0.0.1:8999`,
|
||||||
|
secure: false,
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
pathRewrite: {
|
||||||
|
"^/liquidtestnet": ""
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/liquidtestnet/api/**'],
|
||||||
|
target: `http://127.0.0.1:3000`,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
pathRewrite: {
|
||||||
|
"^/liquidtestnet/api/": "/"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (configContent && configContent.BASE_MODULE === 'bisq') {
|
||||||
|
PROXY_CONFIG.push(...[
|
||||||
|
{
|
||||||
|
context: ['/bisq/api/v1/ws'],
|
||||||
|
target: `http://127.0.0.1:8999`,
|
||||||
|
secure: false,
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
pathRewrite: {
|
||||||
|
"^/bisq": ""
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/bisq/api/v1/**'],
|
||||||
|
target: `http://127.0.0.1:8999`,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/bisq/api/**'],
|
||||||
|
target: `http://127.0.0.1:8999`,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
pathRewrite: {
|
||||||
|
"^/bisq/api/": "/api/v1/bisq/"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
PROXY_CONFIG.push(...[
|
||||||
|
{
|
||||||
|
context: ['/testnet/api/v1/lightning/**'],
|
||||||
|
target: `http://127.0.0.1:8999`,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
pathRewrite: {
|
||||||
|
"^/testnet": ""
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/api/v1/**'],
|
||||||
|
target: `http://127.0.0.1:8999`,
|
||||||
|
secure: false,
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/api/**'],
|
||||||
|
target: `http://127.0.0.1:3000`,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
pathRewrite: {
|
||||||
|
"^/api": ""
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log(PROXY_CONFIG);
|
||||||
|
|
||||||
|
module.exports = PROXY_CONFIG;
|
@ -2,9 +2,7 @@
|
|||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="search-box-container mr-2">
|
<div class="search-box-container mr-2">
|
||||||
<input (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" i18n-placeholder="search-form.searchbar-placeholder" placeholder="Explore the full Bitcoin ecosystem">
|
<input (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" i18n-placeholder="search-form.searchbar-placeholder" placeholder="Explore the full Bitcoin ecosystem">
|
||||||
|
<app-search-results #searchResults [hidden]="dropdownHidden" [results]="typeAhead$ | async" (selectedResult)="selectedResult($event)"></app-search-results>
|
||||||
<app-search-results #searchResults [results]="typeAhead$ | async" (selectedResult)="selectedResult($event)"></app-search-results>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button [disabled]="isSearching" type="submit" class="btn btn-block btn-primary">
|
<button [disabled]="isSearching" type="submit" class="btn btn-block btn-primary">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, ChangeDetectionStrategy, EventEmitter, Output, ViewChild, HostListener } from '@angular/core';
|
import { Component, OnInit, ChangeDetectionStrategy, EventEmitter, Output, ViewChild, HostListener, ElementRef } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { AssetsService } from '../../services/assets.service';
|
import { AssetsService } from '../../services/assets.service';
|
||||||
@ -23,6 +23,16 @@ export class SearchFormComponent implements OnInit {
|
|||||||
isTypeaheading$ = new BehaviorSubject<boolean>(false);
|
isTypeaheading$ = new BehaviorSubject<boolean>(false);
|
||||||
typeAhead$: Observable<any>;
|
typeAhead$: Observable<any>;
|
||||||
searchForm: FormGroup;
|
searchForm: FormGroup;
|
||||||
|
dropdownHidden = false;
|
||||||
|
|
||||||
|
@HostListener('document:click', ['$event'])
|
||||||
|
onDocumentClick(event) {
|
||||||
|
if (this.elementRef.nativeElement.contains(event.target)) {
|
||||||
|
this.dropdownHidden = false;
|
||||||
|
} else {
|
||||||
|
this.dropdownHidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[A-z]{2,5}1[a-zA-HJ-NP-Z0-9]{39,59})$/;
|
regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[A-z]{2,5}1[a-zA-HJ-NP-Z0-9]{39,59})$/;
|
||||||
regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
|
regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
|
||||||
@ -45,6 +55,7 @@ export class SearchFormComponent implements OnInit {
|
|||||||
private electrsApiService: ElectrsApiService,
|
private electrsApiService: ElectrsApiService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private relativeUrlPipe: RelativeUrlPipe,
|
private relativeUrlPipe: RelativeUrlPipe,
|
||||||
|
private elementRef: ElementRef,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"MEMPOOL": {
|
"MEMPOOL": {
|
||||||
|
"ENABLED": false,
|
||||||
"NETWORK": "mainnet",
|
"NETWORK": "mainnet",
|
||||||
"BACKEND": "esplora",
|
"BACKEND": "esplora",
|
||||||
"HTTP_PORT": 8993,
|
"HTTP_PORT": 8993,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"MEMPOOL": {
|
"MEMPOOL": {
|
||||||
|
"ENABLED": false,
|
||||||
"NETWORK": "signet",
|
"NETWORK": "signet",
|
||||||
"BACKEND": "esplora",
|
"BACKEND": "esplora",
|
||||||
"HTTP_PORT": 8991,
|
"HTTP_PORT": 8991,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"MEMPOOL": {
|
"MEMPOOL": {
|
||||||
|
"ENABLED": false,
|
||||||
"NETWORK": "testnet",
|
"NETWORK": "testnet",
|
||||||
"BACKEND": "esplora",
|
"BACKEND": "esplora",
|
||||||
"HTTP_PORT": 8992,
|
"HTTP_PORT": 8992,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user