mirror of
https://github.com/mempool/mempool.git
synced 2025-01-18 21:32:55 +01:00
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:
commit
b8fe7b621c
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -1,8 +1,4 @@
|
||||
{
|
||||
"/api/v1/donations": {
|
||||
"target": "http://localhost:9000/",
|
||||
"secure": false
|
||||
},
|
||||
"/api/v1": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false
|
||||
|
68
frontend/proxy.stg.conf.json
Normal file
68
frontend/proxy.stg.conf.json
Normal 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
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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']);
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;">
|
||||
|
@ -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">
|
||||
|
@ -1,3 +1,12 @@
|
||||
.dashboard-container {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.dashboard-container {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1d1f31;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>();
|
||||
|
@ -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']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
frontend/src/resources/profile/exodus.svg
Normal file
16
frontend/src/resources/profile/exodus.svg
Normal 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 |
26
frontend/src/resources/profile/sqcrypto.svg
Normal file
26
frontend/src/resources/profile/sqcrypto.svg
Normal 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 |
@ -50,11 +50,11 @@ $dropdown-link-active-bg: #11131f;
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #11131f;
|
||||
min-width: 320px;
|
||||
}
|
||||
|
||||
.container {
|
||||
|
@ -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
2
production/sysctl.conf
Normal file
@ -0,0 +1,2 @@
|
||||
net.inet.tcp.fast_finwait2_recycle=1
|
||||
kern.ipc.soacceptqueue=1024
|
Loading…
Reference in New Issue
Block a user