Merge branch 'master' into simon/bisq-dashboard

# Conflicts:
#	frontend/package-lock.json
#	frontend/src/app/components/master-page/master-page.component.html
This commit is contained in:
softsimon 2021-04-23 15:35:35 +04:00
commit b8fe7b621c
No known key found for this signature in database
GPG Key ID: 488D7DCFB5A430D7
32 changed files with 358 additions and 64 deletions

View File

@ -39,6 +39,13 @@
"USERNAME": "mempool",
"PASSWORD": "mempool"
},
"SYSLOG": {
"ENABLED": true,
"HOST": "127.0.0.1",
"PORT": 514,
"MIN_PRIORITY": "info",
"FACILITY": "local7"
},
"STATISTICS": {
"ENABLED": true,
"TX_PER_SECOND_SAMPLE_PERIOD": 150

View File

@ -1,6 +1,6 @@
{
"name": "mempool-backend",
"version": "2.0.0",
"version": "2.2.0-dev",
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
"license": "GNU Affero General Public License v3.0",
"homepage": "https://mempool.space",

View File

@ -1,20 +1,24 @@
import * as fs from 'fs';
import * as os from 'os';
import logger from '../logger';
import { IBackendInfo } from '../mempool.interfaces';
class BackendInfo {
gitCommitHash = '';
hostname = '';
private gitCommitHash = '';
private hostname = '';
private version = '';
constructor() {
this.setLatestCommitHash();
this.setVersion();
this.hostname = os.hostname();
}
public getBackendInfo() {
public getBackendInfo(): IBackendInfo {
return {
'hostname': this.hostname,
'git-commit': this.gitCommitHash,
hostname: this.hostname,
gitCommit: this.gitCommitHash,
version: this.version,
};
}
@ -29,6 +33,15 @@ class BackendInfo {
logger.err('Could not load git commit info: ' + e.message || e);
}
}
private setVersion(): void {
try {
const packageJson = fs.readFileSync('package.json').toString();
this.version = JSON.parse(packageJson).version;
} catch (e) {
throw new Error(e);
}
}
}
export default new BackendInfo();

View File

@ -169,7 +169,7 @@ class Mempool {
if (!this.inSync && transactions.length === Object.keys(this.mempoolCache).length) {
this.inSync = true;
logger.info('The mempool is now in sync!');
logger.notice('The mempool is now in sync!');
loadingIndicators.setProgress('mempool', 100);
}

View File

@ -167,8 +167,7 @@ class WebsocketHandler {
'conversions': fiatConversion.getConversionRates(),
'mempool-blocks': mempoolBlocks.getMempoolBlocks(),
'transactions': memPool.getLatestTransactions(),
'git-commit': backendInfo.gitCommitHash,
'hostname': backendInfo.hostname,
'backendInfo': backendInfo.getBackendInfo(),
'loadingIndicators': loadingIndicators.getLoadingIndicators(),
...this.extraInitProperties
};

View File

@ -41,6 +41,13 @@ interface IConfig {
USERNAME: string;
PASSWORD: string;
};
SYSLOG: {
ENABLED: boolean;
HOST: string;
PORT: number;
MIN_PRIORITY: 'emerg' | 'alert' | 'crit' | 'err' |'warn' | 'notice' | 'info' | 'debug';
FACILITY: string;
};
STATISTICS: {
ENABLED: boolean;
TX_PER_SECOND_SAMPLE_PERIOD: number;
@ -96,6 +103,13 @@ const defaults: IConfig = {
'USERNAME': 'mempool',
'PASSWORD': 'mempool'
},
'SYSLOG': {
'ENABLED': true,
'HOST': '127.0.0.1',
'PORT': 514,
'MIN_PRIORITY': 'info',
'FACILITY': 'local7'
},
'STATISTICS': {
'ENABLED': true,
'TX_PER_SECOND_SAMPLE_PERIOD': 150
@ -117,6 +131,7 @@ class Config implements IConfig {
CORE_RPC: IConfig['CORE_RPC'];
CORE_RPC_MINFEE: IConfig['CORE_RPC_MINFEE'];
DATABASE: IConfig['DATABASE'];
SYSLOG: IConfig['SYSLOG'];
STATISTICS: IConfig['STATISTICS'];
BISQ_BLOCKS: IConfig['BISQ_BLOCKS'];
BISQ_MARKETS: IConfig['BISQ_MARKETS'];
@ -129,6 +144,7 @@ class Config implements IConfig {
this.CORE_RPC = configs.CORE_RPC;
this.CORE_RPC_MINFEE = configs.CORE_RPC_MINFEE;
this.DATABASE = configs.DATABASE;
this.SYSLOG = configs.SYSLOG;
this.STATISTICS = configs.STATISTICS;
this.BISQ_BLOCKS = configs.BISQ_BLOCKS;
this.BISQ_MARKETS = configs.BISQ_MARKETS;

View File

@ -81,7 +81,7 @@ class Server {
await checkDbConnection();
}
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED) {
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && cluster.isMaster) {
statistics.startStatistics();
}

View File

@ -50,17 +50,11 @@ class Logger {
public debug: ((msg: string) => void);
private name = 'mempool';
private fac: any;
private loghost: string;
private logport: number;
private client: dgram.Socket;
private network: string;
constructor(fac) {
constructor() {
let prio;
this.fac = fac != null ? fac : Logger.facilities.local0;
this.loghost = '127.0.0.1';
this.logport = 514;
for (prio in Logger.priorities) {
if (true) {
this.addprio(prio);
@ -97,10 +91,12 @@ class Logger {
}
const network = this.network ? ' <' + this.network + '>' : '';
prionum = Logger.priorities[priority] || Logger.priorities.info;
syslogmsg = `<${(this.fac * 8 + prionum)}> ${this.name}[${process.pid}]: ${priority.toUpperCase()}${network} ${msg}`;
consolemsg = `${this.ts()} [${process.pid}] ${priority.toUpperCase()}:${network} ${msg}`;
this.syslog(syslogmsg);
if (config.SYSLOG.ENABLED && Logger.priorities[priority] <= Logger.priorities[config.SYSLOG.MIN_PRIORITY]) {
syslogmsg = `<${(Logger.facilities[config.SYSLOG.FACILITY] * 8 + prionum)}> ${this.name}[${process.pid}]: ${priority.toUpperCase()}${network} ${msg}`;
this.syslog(syslogmsg);
}
if (priority === 'warning') {
priority = 'warn';
}
@ -116,7 +112,7 @@ class Logger {
private syslog(msg) {
let msgbuf;
msgbuf = Buffer.from(msg);
this.client.send(msgbuf, 0, msgbuf.length, this.logport, this.loghost, function(err, bytes) {
this.client.send(msgbuf, 0, msgbuf.length, config.SYSLOG.PORT, config.SYSLOG.HOST, function(err, bytes) {
if (err) {
console.log(err);
}
@ -146,4 +142,4 @@ class Logger {
}
}
export default new Logger(Logger.facilities.local7);
export default new Logger();

View File

@ -161,3 +161,9 @@ interface RequiredParams {
export interface ILoadingIndicators { [name: string]: number; }
export interface IConversionRates { [currency: string]: number; }
export interface IBackendInfo {
hostname: string;
gitCommit: string;
version: string;
}

View File

@ -5,6 +5,8 @@ const GENERATED_CONFIG_FILE_NAME = 'generated-config.js';
let settings = [];
let configContent = {};
let gitCommitHash = '';
let packetJsonVersion = '';
try {
const rawConfig = fs.readFileSync(CONFIG_FILE_NAME);
@ -15,6 +17,13 @@ try {
}
}
try {
const packageJson = fs.readFileSync('package.json');
packetJsonVersion = JSON.parse(packageJson).version;
} catch (e) {
throw new Error(e);
}
for (setting in configContent) {
settings.push({
key: setting,
@ -22,9 +31,17 @@ for (setting in configContent) {
});
}
try {
gitCommitHash = fs.readFileSync('../.git/refs/heads/master').toString().trim();
} catch (e) {
console.log('Could not load git commit info: ' + e.message || e);
}
const code = `(function (window) {
window.__env = window.__env || {};${settings.reduce((str, obj) => `${str}
window.__env.${obj.key} = ${ typeof obj.value === 'string' ? `'${obj.value}'` : obj.value };`, '')}
window.__env.GIT_COMMIT_HASH = '${gitCommitHash}';
window.__env.PACKAGE_JSON_VERSION = '${packetJsonVersion}';
}(global || this));`;
try {

View File

@ -1,6 +1,6 @@
{
"name": "mempool-frontend",
"version": "2.0.0",
"version": "2.2.0-dev",
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
"license": "GNU Affero General Public License v3.0",
"homepage": "https://mempool.space",
@ -25,12 +25,14 @@
"i18n-extract-from-source": "./node_modules/@angular/cli/bin/ng xi18n --ivy --out-file ./src/locale/messages.xlf",
"i18n-pull-from-transifex": "tx pull -a --parallel --minimum-perc 1 --force",
"serve": "ng serve --proxy-config proxy.conf.json",
"serve:stg": "ng serve --host 0.0.0.0 --disable-host-check --proxy-config proxy.stg.conf.json --verbose",
"start": "npm run generate-config && npm run sync-assets-dev && ng serve --proxy-config proxy.conf.json",
"build": "npm run generate-config && ng build --prod --localize && npm run sync-assets && npm run build-mempool-js",
"start:stg": "npm run generate-config && npm run sync-assets-dev && ng serve --host 0.0.0.0 --disable-host-check --proxy-config proxy.stg.conf.json",
"build": "npm run generate-config && ng build --prod --localize && npm run sync-assets && npm run build-mempool.js",
"sync-assets": "node sync-assets.js && rsync -av ./dist/mempool/browser/en-US/resources ./dist/mempool/browser/resources",
"sync-assets-dev": "node sync-assets.js dev",
"generate-config": "node generate-config.js",
"build-mempool-js": "tsc | browserify -p tinyify ./node_modules/@mempool/mempool-js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js",
"build-mempool.js": "tsc | browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
@ -55,7 +57,7 @@
"@fortawesome/fontawesome-svg-core": "^1.2.35",
"@fortawesome/free-solid-svg-icons": "^5.15.3",
"@mempool/chartist": "^0.11.4",
"@mempool/mempool-js": "^2.2.1",
"@mempool/mempool.js": "^2.2.0",
"@ng-bootstrap/ng-bootstrap": "^7.0.0",
"@nguniversal/express-engine": "11.2.1",
"@types/qrcode": "^1.3.4",

View File

@ -1,8 +1,4 @@
{
"/api/v1/donations": {
"target": "http://localhost:9000/",
"secure": false
},
"/api/v1": {
"target": "http://localhost:8999/",
"secure": false

View File

@ -0,0 +1,68 @@
{
"/api/v1/ws": {
"target": "https://mempool.ninja",
"secure": false,
"ws": true
},
"/api/*": {
"target": "https://mempool.ninja",
"secure": false,
"changeOrigin": true,
"logLevel": "debug",
"pathRewrite": {
"^/api": "https://mempool.ninja/api"
},
"timeout": 3600000
},
"/testnet/api/v1/ws": {
"target": "https://mempool.ninja/testnet",
"secure": false,
"ws": true,
"pathRewrite": {
"^/testnet/api": "/api/v1/ws"
}
},
"/testnet/api/v1/*": {
"target": "https://mempool.ninja/testnet",
"secure": false,
"changeOrigin": true,
"pathRewrite": {
"^/testnet/api/v1": "/api/v1"
},
"timeout": 3600000
},
"/bisq/api/v1/ws": {
"target": "https://mempool.ninja/bisq",
"secure": false,
"ws": true,
"pathRewrite": {
"^/bisq/api": "/api/v1/ws"
}
},
"/bisq/api/*": {
"target": "https://mempool.ninja/bisq",
"secure": false,
"changeOrigin": true,
"pathRewrite": {
"^/bisq/api/": "/api/v1/bisq/"
},
"timeout": 3600000
},
"/liquid/api/v1/ws": {
"target": "https://mempool.ninja/liquid",
"secure": false,
"ws": true,
"pathRewrite": {
"^/liquid/api": "/api/v1/ws"
}
},
"/liquid/api/*": {
"target": "https://mempool.ninja/liquid",
"secure": false,
"changeOrigin": true,
"pathRewrite": {
"^/liquid/api/": "/api/v1/liquid/"
},
"timeout": 3600000
}
}

View File

@ -5,7 +5,7 @@
<br>
<div class="text-small text-center offset-md-1">
v2.2-dev ({{ gitCommit$ | async }})
v{{ packetJsonVersion }} [{{ frontendGitCommitHash }}]
</div>
<br>
@ -48,7 +48,7 @@
<a href="https://squarecrypto.org/" target="_blank">
<div class="profile_photo enterprise_sponsor d-inline-block" title="Square Crypto">
<img class="profile_img" src="/resources/profile/sqcrypto.png" />
<img class="profile_img" src="/resources/profile/sqcrypto.svg" />
Square
</div>
</a>
@ -60,6 +60,13 @@
</div>
</a>
<a href="https://exodus.com/" target="_blank">
<div class="profile_photo enterprise_sponsor d-inline-block" title="Exodus">
<img class="profile_img" src="/resources/profile/exodus.svg" />
Exodus
</div>
</a>
<br><br>
<br><br>
@ -392,4 +399,10 @@
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
</div>
<br>
<div class="text-small text-center" *ngIf="officialMempoolSpace">
{{ (backendInfo$ | async)?.hostname }} (v{{ (backendInfo$ | async )?.version }}) [{{ (backendInfo$ | async )?.gitCommit | slice:0:8 }}]
</div>
</div>

View File

@ -6,7 +6,8 @@ import { Observable, Subscription } from 'rxjs';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ApiService } from 'src/app/services/api.service';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { delay, map, retryWhen, switchMap, tap } from 'rxjs/operators';
import { delay, retryWhen, switchMap, tap } from 'rxjs/operators';
import { IBackendInfo } from 'src/app/interfaces/websocket.interface';
@Component({
selector: 'app-about',
@ -14,7 +15,7 @@ import { delay, map, retryWhen, switchMap, tap } from 'rxjs/operators';
styleUrls: ['./about.component.scss'],
})
export class AboutComponent implements OnInit, OnDestroy {
gitCommit$: Observable<string>;
backendInfo$: Observable<IBackendInfo>;
donationForm: FormGroup;
paymentForm: FormGroup;
donationStatus = 1;
@ -22,6 +23,9 @@ export class AboutComponent implements OnInit, OnDestroy {
contributors$: Observable<any>;
donationObj: any;
sponsorsEnabled = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
frontendGitCommitHash = this.stateService.env.GIT_COMMIT_HASH.substr(0, 8);
packetJsonVersion = this.stateService.env.PACKAGE_JSON_VERSION;
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
sponsors = null;
contributors = null;
requestSubscription: Subscription | undefined;
@ -36,7 +40,7 @@ export class AboutComponent implements OnInit, OnDestroy {
) { }
ngOnInit() {
this.gitCommit$ = this.stateService.gitCommit$.pipe(map((str) => str.substr(0, 8)));
this.backendInfo$ = this.stateService.backendInfo$;
this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`);
this.websocketService.want(['blocks']);

View File

@ -4,7 +4,7 @@
<div class="clearfix"></div>
<table class="table table-borderless" [alwaysCallback]="true" [fromRoot]="true" [infiniteScrollContainer]="'body'" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
<table class="table table-borderless" [alwaysCallback]="true" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
<thead>
<th style="width: 15%;" i18n="latest-blocks.height">Height</th>
<th class="d-none d-md-block" style="width: 20%;" i18n="latest-blocks.timestamp">Timestamp</th>

View File

@ -26,7 +26,7 @@
</div>
<div class="navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto pt-2 pb-2 pb-md-0 pt-md-0 {{ network.val }}">
<ul class="navbar-nav {{ network.val }}">
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a>
</li>
@ -62,7 +62,7 @@
<a class="nav-link" [routerLink]="['/about']" (click)="collapse()"><fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true" i18n-title="master-page.about" title="About"></fa-icon></a>
</li>
</ul>
<app-search-form location="top" (searchTriggered)="collapse()"></app-search-form>
<app-search-form class="search-form-container" location="top" (searchTriggered)="collapse()"></app-search-form>
</div>
</nav>
</header>

View File

@ -8,14 +8,16 @@ fa-icon {
.navbar {
z-index: 100;
min-height: 64px;
}
li.nav-item {
margin: auto 10px;
padding-left: 10px;
padding-right: 10px;
}
@media (min-width: 768px) {
@media (min-width: 992px) {
.navbar {
padding: 0rem 2rem;
}
@ -26,17 +28,50 @@ li.nav-item {
margin-right: 16px;
}
li.nav-item {
margin: auto 0px;
padding: 10px;
}
}
li.nav-item a {
color: #ffffff;
.navbar-nav {
background: #212121;
bottom: 0;
box-shadow: 0px 0px 15px 0px #000;
flex-direction: row;
left: 0;
justify-content: center;
position: fixed;
width: 100%;
}
.navbar-nav {
flex-direction: row;
justify-content: center;
@media (min-width: 992px) {
.navbar-nav {
background: transparent;
box-shadow: none;
position: relative;
width: auto;
}
}
.navbar-collapse {
flex-basis: auto;
justify-content: flex-end;
}
@media (min-width: 992px) {
.navbar-collapse {
justify-content: space-between;
}
}
.navbar-brand {
width: 60%;
}
@media (min-width: 576px) {
.navbar-brand {
width: 140px;
}
}
nav {
@ -81,5 +116,16 @@ nav {
.dropdown-item {
display: flex;
align-items:center;
align-items: center;
}
@media (min-width: 992px) {
.search-form-container {
width: 100%;
max-width: 500px;
padding-left: 15px;
}
}
.navbar-dark .navbar-nav .nav-link {
color: #f1f1f1;
}

View File

@ -5,12 +5,53 @@
text-overflow: ellipsis;
}
form {
margin-top: 5px;
}
@media (min-width: 576px) {
form {
margin-top: 0px;
margin-left: 8px;
}
}
@media (min-width: 992px) {
form {
width: 100%;
}
}
.btn-block {
width: 58.55px;
}
.search-box-container {
width: 100%;
}
@media (min-width: 992px) {
.search-box-container input {
border: 0px;
}
.search-box-container .btn {
width: 100px;
}
@media (min-width: 768px) {
.search-box-container {
width: 350px;
min-width: 400px;
}
}
@media (min-width: 992px) {
.search-box-container {
min-width: 260px;
}
}
@media (min-width: 1200px) {
.search-box-container {
min-width: 300px;
}
}

View File

@ -15,7 +15,7 @@
<div class="card-header">
<i class="fa fa-area-chart"></i> <span i18n="statistics.memory-by-vBytes">Mempool by vBytes (sat/vByte)</span>
<form [formGroup]="radioGroupForm" style="float: right;">
<form [formGroup]="radioGroupForm" style="float: right;" (click)="saveGraphPreference()">
<div class="spinner-border text-light bootstrap-spinner" *ngIf="spinnerLoading"></div>
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
<label ngbButtonLabel class="btn-primary btn-sm">

View File

@ -35,6 +35,7 @@ export class StatisticsComponent implements OnInit {
radioGroupForm: FormGroup;
inverted: boolean;
graphWindowPreference: String;
constructor(
@Inject(LOCALE_ID) private locale: string,
@ -45,16 +46,13 @@ export class StatisticsComponent implements OnInit {
private stateService: StateService,
private seoService: SeoService,
private storageService: StorageService,
) {
this.radioGroupForm = this.formBuilder.group({
dateSpan: '2h'
});
}
) { }
ngOnInit() {
this.seoService.setTitle($localize`:@@5d4f792f048fcaa6df5948575d7cb325c9393383:Graphs`);
this.stateService.networkChanged$.subscribe((network) => this.network = network);
this.inverted = this.storageService.getValue('inverted-graph') === 'true';
this.graphWindowPreference = this.storageService.getValue('graphWindowPreference') ? this.storageService.getValue('graphWindowPreference').trim() : '2h';
const isMobile = window.innerWidth <= 767.98;
let labelHops = isMobile ? 48 : 24;
@ -62,8 +60,12 @@ export class StatisticsComponent implements OnInit {
labelHops = 96;
}
this.radioGroupForm = this.formBuilder.group({
dateSpan: this.graphWindowPreference
});
const labelInterpolationFnc = (value: any, index: any) => {
switch (this.radioGroupForm.controls.dateSpan.value) {
switch (this.graphWindowPreference) {
case '2h':
case '24h':
value = formatDate(value, 'HH:mm', this.locale);
@ -166,4 +168,8 @@ export class StatisticsComponent implements OnInit {
this.storageService.setValue('inverted-graph', !this.inverted);
document.location.reload();
}
saveGraphPreference() {
this.storageService.setValue('graphWindowPreference', this.radioGroupForm.controls.dateSpan.value);
}
}

View File

@ -12,7 +12,7 @@
</div>
<div class="clearfix"></div>
</div>
<div class="header-bg box" infiniteScroll [alwaysCallback]="true" [fromRoot]="true" [infiniteScrollContainer]="'body'" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()">
<div class="header-bg box" infiniteScroll [alwaysCallback]="true" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()">
<div class="row">
<div class="col">
<table class="table table-borderless smaller-text table-xs" style="margin: 0;">

View File

@ -1,5 +1,5 @@
<div class="container-xl mt-2">
<div class="container-xl mt-2 dashboard-container">
<div class="row row-cols-1 row-cols-md-2" *ngIf="{ value: (mempoolInfoData$ | async) } as mempoolInfoData">
<ng-template [ngIf]="collapseLevel === 'three'" [ngIfElse]="expanded">

View File

@ -1,3 +1,12 @@
.dashboard-container {
padding-bottom: 60px;
}
@media (min-width: 992px) {
.dashboard-container {
padding-bottom: 0px;
}
}
.card {
background-color: #1d1f31;
}

View File

@ -16,6 +16,7 @@ export interface WebsocketResponse {
rbfTransaction?: Transaction;
transactions?: TransactionStripped[];
loadingIndicators?: ILoadingIndicators;
backendInfo?: IBackendInfo;
'track-tx'?: string;
'track-address'?: string;
'track-asset'?: string;
@ -50,3 +51,8 @@ export interface TransactionStripped {
value: number;
}
export interface IBackendInfo {
hostname: string;
gitCommit: string;
version: string;
}

View File

@ -1,7 +1,7 @@
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs';
import { Block, Transaction } from '../interfaces/electrs.interface';
import { MempoolBlock, MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface';
import { IBackendInfo, MempoolBlock, MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface';
import { OptimizedMempoolStats } from '../interfaces/node-api.interface';
import { Router, NavigationStart } from '@angular/router';
import { isPlatformBrowser } from '@angular/common';
@ -28,6 +28,8 @@ export interface Env {
NGINX_PROTOCOL?: string;
NGINX_HOSTNAME?: string;
NGINX_PORT?: string;
GIT_COMMIT_HASH: string;
PACKAGE_JSON_VERSION: string;
}
const defaultEnv: Env = {
@ -43,6 +45,8 @@ const defaultEnv: Env = {
'NGINX_PROTOCOL': 'http',
'NGINX_HOSTNAME': '127.0.0.1',
'NGINX_PORT': '80',
'GIT_COMMIT_HASH': '',
'PACKAGE_JSON_VERSION': '',
};
@Injectable({
@ -67,7 +71,7 @@ export class StateService {
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
vbytesPerSecond$ = new ReplaySubject<number>(1);
lastDifficultyAdjustment$ = new ReplaySubject<number>(1);
gitCommit$ = new ReplaySubject<string>(1);
backendInfo$ = new ReplaySubject<IBackendInfo>(1);
loadingIndicators$ = new ReplaySubject<ILoadingIndicators>(1);
live2Chart$ = new Subject<OptimizedMempoolStats>();

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
import { WebsocketResponse } from '../interfaces/websocket.interface';
import { WebsocketResponse, IBackendInfo } from '../interfaces/websocket.interface';
import { StateService } from './state.service';
import { Block, Transaction } from '../interfaces/electrs.interface';
import { Subscription } from 'rxjs';
@ -244,13 +244,13 @@ export class WebsocketService {
this.stateService.bsqPrice$.next(response['bsq-price']);
}
if (response['git-commit']) {
this.stateService.gitCommit$.next(response['git-commit']);
if (response.backendInfo) {
this.stateService.backendInfo$.next(response.backendInfo);
if (!this.latestGitCommit) {
this.latestGitCommit = response['git-commit'];
this.latestGitCommit = response.backendInfo.gitCommit;
} else {
if (this.latestGitCommit !== response['git-commit']) {
if (this.latestGitCommit !== response.backendInfo.gitCommit) {
setTimeout(() => {
window.location.reload();
}, Math.floor(Math.random() * 60000) + 60000);
@ -291,7 +291,7 @@ export class WebsocketService {
}
if (response['git-commit']) {
this.stateService.gitCommit$.next(response['git-commit']);
this.stateService.backendInfo$.next(response['git-commit']);
}
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="400px" height="400px" viewBox="0 0 400 400" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Exodus_logo</title>
<defs>
<linearGradient x1="0%" y1="50%" x2="100%" y2="50%" id="linearGradient-1">
<stop stop-color="#00BFFF" offset="0%"></stop>
<stop stop-color="#6619FF" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Exodus_logo" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Exodus-logo">
<rect id="Rectangle" fill="#1A1D40" x="0" y="0" width="400" height="400"></rect>
<path d="M244.25,200 L310,265.75 L286.8,265.75 C282.823093,265.746499 279.010347,264.16385 276.2,261.35 L215,200 L276.25,138.6 C279.068515,135.804479 282.880256,134.240227 286.85,134.249954 L310,134.249954 L244.25,200 Z M123.75,138.6 C120.931485,135.804479 117.119744,134.240227 113.15,134.249954 L90,134.249954 L155.75,200 L90,265.75 L113.2,265.75 C117.176907,265.746499 120.989653,264.16385 123.8,261.35 L185,200 L123.75,138.6 Z M200,215 L138.6,276.25 C135.804479,279.068515 134.240227,282.880256 134.249954,286.85 L134.249954,310 L200,244.25 L265.750046,310 L265.750046,286.85 C265.759773,282.880256 264.195521,279.068515 261.4,276.25 L200,215 Z M200,185 L261.4,123.75 C264.195521,120.931485 265.759773,117.119744 265.750046,113.15 L265.750046,90 L200,155.75 L134.249954,90 L134.249954,113.15 C134.240227,117.119744 135.804479,120.931485 138.6,123.75 L200,185 Z" id="01-Exodus-wallet" fill="url(#linearGradient-1)" fill-rule="nonzero"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="200" height="200" viewBox="0 0 200 200" xml:space="preserve">
<defs>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="transparent"></rect>
<g transform="matrix(1 0 0 1 100 100)" id="83eb3c06-e75b-4ba8-8ae0-6ecee4a0a0b2">
<rect style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1; visibility: hidden;" vector-effect="non-scaling-stroke" x="-100" y="-100" rx="0" ry="0" width="200" height="200" />
</g>
<g transform="matrix(Infinity NaN NaN Infinity 0 0)" id="dde875c5-c3d7-4902-975f-2f9bad8740df">
</g>
<g transform="matrix(0.63 0 0 0.63 100 100)">
<g style="" vector-effect="non-scaling-stroke" >
<g transform="matrix(1 0 0 1 0 0)">
<rect style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(27,20,100); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" x="-250" y="-250" rx="0" ry="0" width="500" height="500" />
</g>
<g transform="matrix(1 0 0 1 0 0.18)">
<linearGradient id="SVGID_linear-gradient_3" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 0 0)" x1="175" y1="250" x2="325" y2="250">
<stop offset="0%" style="stop-color:rgb(255,0,255);stop-opacity: 1"/>
<stop offset="100%" style="stop-color:rgb(0,0,255);stop-opacity: 1"/>
</linearGradient>
<path style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: url(#SVGID_linear-gradient_3); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" transform=" translate(-250, -250.18)" d="M 188.66 297.69 C 185.0380626090283 297.68734656530177 181.56356211596963 299.12429596327104 179.00152732293847 301.6844551817831 C 176.4394925299073 304.24461440029523 174.99999902804578 307.71806163707413 175 311.34 L 175 311.34 C 174.86558420938763 316.3065680538503 177.43841429565256 320.95448731944555 181.7185832795378 323.47738630824387 C 185.99875226342303 326.0002852970422 191.31124773657697 326.0002852970422 195.5914167204622 323.47738630824387 C 199.87158570434744 320.95448731944555 202.44441579061237 316.3065680538503 202.31 311.34 L 202.31 311.34 C 202.30999999999997 303.80131316480964 196.1986868351903 297.69 188.66 297.69 Z M 270.38 236.34 L 175 236.34 L 175 263.66 L 270.38 263.66 Z M 188.66 202.34 C 196.20160441229743 202.33999191662778 202.31630849754845 196.22826042470857 202.319983736653 188.6866569079313 C 202.32365897575752 181.14505339115408 196.2149146365801 175.02736506319306 188.67331381425902 175.02000650248846 C 181.13171299193795 175.01264794178383 175.01104188054944 181.11840367107047 175 188.66 C 174.99999902804578 192.2819383629258 176.4394925299073 195.75538559970477 179.00152732293847 198.3155448182169 C 181.56356211596963 200.875704036729 185.03806260902826 202.31265343469823 188.66 202.31 Z M 309.65999999999997 175.03 L 229.65999999999997 175.03 L 229.65999999999997 202.34 L 325 202.34 L 325 190.34 C 325 181.86795193759565 318.1320480624044 175.00000000000003 309.66 175 Z M 229.65999999999997 325.03 L 309.65999999999997 325.03 C 313.7336241240884 325.03000779010233 317.63996644087825 323.4097163707201 320.5176384181544 320.52641661252363 C 323.3953103954305 317.6431168543272 325.00796667039356 313.73361633399355 325 309.66 L 325 297.66 L 229.62 297.66 Z M 311.38 236.36999999999998 C 305.8487069817187 236.35381616694565 300.8536160844738 239.674936531664 298.7284620682323 244.7817148485476 C 296.6033080519909 249.8884931654312 297.7676142525571 255.7728083014525 301.67741021826106 259.68547489561877 C 305.587206183965 263.59814148978506 311.4706651985733 264.7667661395809 316.5790018858666 262.645360789396 C 321.6873385731599 260.52395543921114 325.01212416464153 255.53130340653732 325 250 C 325 246.3771427487918 323.5608255606084 242.90266829859192 320.9990786310082 240.34092136899176 C 318.4373317014081 237.7791744393916 314.96285725120816 236.34 311.34 236.34 Z" stroke-linecap="round" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -50,11 +50,11 @@ $dropdown-link-active-bg: #11131f;
html, body {
height: 100%;
overflow-x: hidden;
}
body {
background-color: #11131f;
min-width: 320px;
}
.container {

View File

@ -6,6 +6,7 @@
"MINED_BLOCKS_CACHE": 144,
"SPAWN_CLUSTER_PROCS": 0,
"API_URL_PREFIX": "/api/v1/",
"CLEAR_PROTECTION_MINUTES": 5,
"POLL_RATE_MS": 2000
},
"CORE_RPC": {

2
production/sysctl.conf Normal file
View File

@ -0,0 +1,2 @@
net.inet.tcp.fast_finwait2_recycle=1
kern.ipc.soacceptqueue=1024