Merge branch 'master' into nymkappa/bugfix/index-blocks-prices-often
This commit is contained in:
commit
2dc875bb33
4
backend/.gitignore
vendored
4
backend/.gitignore
vendored
@ -1,9 +1,9 @@
|
|||||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
# production config and external assets
|
# production config and external assets
|
||||||
*.json
|
|
||||||
!mempool-config.sample.json
|
|
||||||
|
|
||||||
|
mempool-config.json
|
||||||
|
pools.json
|
||||||
icons.json
|
icons.json
|
||||||
|
|
||||||
# compiled output
|
# compiled output
|
||||||
|
@ -593,7 +593,7 @@ class Blocks {
|
|||||||
|
|
||||||
// Index the response if needed
|
// Index the response if needed
|
||||||
if (Common.blocksSummariesIndexingEnabled() === true) {
|
if (Common.blocksSummariesIndexingEnabled() === true) {
|
||||||
await BlocksSummariesRepository.$saveSummary(block.height, summary, null);
|
await BlocksSummariesRepository.$saveSummary({height: block.height, mined: summary});
|
||||||
}
|
}
|
||||||
|
|
||||||
return summary.transactions;
|
return summary.transactions;
|
||||||
|
@ -13,6 +13,30 @@ class ChannelsApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async $getAllChannelsGeo(): Promise<any[]> {
|
||||||
|
try {
|
||||||
|
const query = `SELECT nodes_1.public_key as node1_public_key, nodes_1.alias AS node1_alias,
|
||||||
|
nodes_1.latitude AS node1_latitude, nodes_1.longitude AS node1_longitude,
|
||||||
|
nodes_2.public_key as node2_public_key, nodes_2.alias AS node2_alias,
|
||||||
|
nodes_2.latitude AS node2_latitude, nodes_2.longitude AS node2_longitude,
|
||||||
|
channels.capacity
|
||||||
|
FROM channels
|
||||||
|
JOIN nodes AS nodes_1 on nodes_1.public_key = channels.node1_public_key
|
||||||
|
JOIN nodes AS nodes_2 on nodes_2.public_key = channels.node2_public_key
|
||||||
|
WHERE nodes_1.latitude IS NOT NULL AND nodes_1.longitude IS NOT NULL
|
||||||
|
AND nodes_2.latitude IS NOT NULL AND nodes_2.longitude IS NOT NULL
|
||||||
|
`;
|
||||||
|
const [rows]: any = await DB.query(query);
|
||||||
|
return rows.map((row) => [
|
||||||
|
row.node1_public_key, row.node1_alias, row.node1_longitude, row.node1_latitude,
|
||||||
|
row.node2_public_key, row.node2_alias, row.node2_longitude, row.node2_latitude,
|
||||||
|
row.capacity]);
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$getAllChannelsGeo error: ' + (e instanceof Error ? e.message : e));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async $searchChannelsById(search: string): Promise<any[]> {
|
public async $searchChannelsById(search: string): Promise<any[]> {
|
||||||
try {
|
try {
|
||||||
const searchStripped = search.replace('%', '') + '%';
|
const searchStripped = search.replace('%', '') + '%';
|
||||||
|
@ -11,6 +11,7 @@ class ChannelsRoutes {
|
|||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/search/:search', this.$searchChannelsById)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/search/:search', this.$searchChannelsById)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/:short_id', this.$getChannel)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/:short_id', this.$getChannel)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels', this.$getChannelsForNode)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels', this.$getChannelsForNode)
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels-geo', this.$getChannelsGeo)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +94,15 @@ class ChannelsRoutes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async $getChannelsGeo(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const channels = await channelsApi.$getAllChannelsGeo();
|
||||||
|
res.json(channels);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new ChannelsRoutes();
|
export default new ChannelsRoutes();
|
||||||
|
@ -451,9 +451,12 @@ class WebsocketHandler {
|
|||||||
value: tx.value,
|
value: tx.value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
BlocksSummariesRepository.$saveSummary(block.height, null, {
|
BlocksSummariesRepository.$saveSummary({
|
||||||
id: block.id,
|
height: block.height,
|
||||||
transactions: stripped
|
template: {
|
||||||
|
id: block.id,
|
||||||
|
transactions: stripped
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
BlocksAuditsRepository.$saveAudit({
|
BlocksAuditsRepository.$saveAudit({
|
||||||
|
@ -17,18 +17,18 @@ class BlocksSummariesRepository {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $saveSummary(height: number, mined: BlockSummary | null = null, template: BlockSummary | null = null) {
|
public async $saveSummary(params: { height: number, mined?: BlockSummary, template?: BlockSummary}) {
|
||||||
const blockId = mined?.id ?? template?.id;
|
const blockId = params.mined?.id ?? params.template?.id;
|
||||||
try {
|
try {
|
||||||
const [dbSummary]: any[] = await DB.query(`SELECT * FROM blocks_summaries WHERE id = "${blockId}"`);
|
const [dbSummary]: any[] = await DB.query(`SELECT * FROM blocks_summaries WHERE id = "${blockId}"`);
|
||||||
if (dbSummary.length === 0) { // First insertion
|
if (dbSummary.length === 0) { // First insertion
|
||||||
await DB.query(`INSERT INTO blocks_summaries VALUE (?, ?, ?, ?)`, [
|
await DB.query(`INSERT INTO blocks_summaries VALUE (?, ?, ?, ?)`, [
|
||||||
height, blockId, JSON.stringify(mined?.transactions ?? []), JSON.stringify(template?.transactions ?? [])
|
params.height, blockId, JSON.stringify(params.mined?.transactions ?? []), JSON.stringify(params.template?.transactions ?? [])
|
||||||
]);
|
]);
|
||||||
} else if (mined !== null) { // Update mined block summary
|
} else if (params.mined !== undefined) { // Update mined block summary
|
||||||
await DB.query(`UPDATE blocks_summaries SET transactions = ? WHERE id = "${mined.id}"`, [JSON.stringify(mined?.transactions)]);
|
await DB.query(`UPDATE blocks_summaries SET transactions = ? WHERE id = "${params.mined.id}"`, [JSON.stringify(params.mined.transactions)]);
|
||||||
} else if (template !== null) { // Update template block summary
|
} else if (params.template !== undefined) { // Update template block summary
|
||||||
await DB.query(`UPDATE blocks_summaries SET template = ? WHERE id = "${template.id}"`, [JSON.stringify(template?.transactions)]);
|
await DB.query(`UPDATE blocks_summaries SET template = ? WHERE id = "${params.template.id}"`, [JSON.stringify(params.template?.transactions)]);
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
|
if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
|
||||||
|
32
frontend/package-lock.json
generated
32
frontend/package-lock.json
generated
@ -34,6 +34,7 @@
|
|||||||
"clipboard": "^2.0.10",
|
"clipboard": "^2.0.10",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"echarts": "~5.3.2",
|
"echarts": "~5.3.2",
|
||||||
|
"echarts-gl": "^2.0.9",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"lightweight-charts": "~3.8.0",
|
"lightweight-charts": "~3.8.0",
|
||||||
"ngx-echarts": "8.0.1",
|
"ngx-echarts": "8.0.1",
|
||||||
@ -6396,6 +6397,11 @@
|
|||||||
"webpack": ">=4.0.1"
|
"webpack": ">=4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/claygl": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
|
||||||
|
},
|
||||||
"node_modules/clean-stack": {
|
"node_modules/clean-stack": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||||
@ -8107,6 +8113,18 @@
|
|||||||
"zrender": "5.3.1"
|
"zrender": "5.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/echarts-gl": {
|
||||||
|
"version": "2.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz",
|
||||||
|
"integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==",
|
||||||
|
"dependencies": {
|
||||||
|
"claygl": "^1.2.1",
|
||||||
|
"zrender": "^5.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"echarts": "^5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/echarts/node_modules/tslib": {
|
"node_modules/echarts/node_modules/tslib": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
||||||
@ -22520,6 +22538,11 @@
|
|||||||
"integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==",
|
"integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"claygl": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
|
||||||
|
},
|
||||||
"clean-stack": {
|
"clean-stack": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||||
@ -23866,6 +23889,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"echarts-gl": {
|
||||||
|
"version": "2.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz",
|
||||||
|
"integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==",
|
||||||
|
"requires": {
|
||||||
|
"claygl": "^1.2.1",
|
||||||
|
"zrender": "^5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ee-first": {
|
"ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
@ -88,6 +88,7 @@
|
|||||||
"clipboard": "^2.0.10",
|
"clipboard": "^2.0.10",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"echarts": "~5.3.2",
|
"echarts": "~5.3.2",
|
||||||
|
"echarts-gl": "^2.0.9",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"lightweight-charts": "~3.8.0",
|
"lightweight-charts": "~3.8.0",
|
||||||
"ngx-echarts": "8.0.1",
|
"ngx-echarts": "8.0.1",
|
||||||
|
@ -6,6 +6,7 @@ import { AppRoutingModule } from './app-routing.module';
|
|||||||
import { AppComponent } from './components/app/app.component';
|
import { AppComponent } from './components/app/app.component';
|
||||||
import { ElectrsApiService } from './services/electrs-api.service';
|
import { ElectrsApiService } from './services/electrs-api.service';
|
||||||
import { StateService } from './services/state.service';
|
import { StateService } from './services/state.service';
|
||||||
|
import { EnterpriseService } from './services/enterprise.service';
|
||||||
import { WebsocketService } from './services/websocket.service';
|
import { WebsocketService } from './services/websocket.service';
|
||||||
import { AudioService } from './services/audio.service';
|
import { AudioService } from './services/audio.service';
|
||||||
import { SeoService } from './services/seo.service';
|
import { SeoService } from './services/seo.service';
|
||||||
@ -36,6 +37,7 @@ import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe
|
|||||||
AudioService,
|
AudioService,
|
||||||
SeoService,
|
SeoService,
|
||||||
StorageService,
|
StorageService,
|
||||||
|
EnterpriseService,
|
||||||
LanguageService,
|
LanguageService,
|
||||||
ShortenStringPipe,
|
ShortenStringPipe,
|
||||||
FiatShortenerPipe,
|
FiatShortenerPipe,
|
||||||
|
@ -143,9 +143,7 @@ export default class TxView implements TransactionStripped {
|
|||||||
|
|
||||||
getColor(): Color {
|
getColor(): Color {
|
||||||
// Block audit
|
// Block audit
|
||||||
if (this.status === 'found') {
|
if (this.status === 'missing') {
|
||||||
// return hexToColor('1a4987');
|
|
||||||
} else if (this.status === 'missing') {
|
|
||||||
return hexToColor('039BE5');
|
return hexToColor('039BE5');
|
||||||
} else if (this.status === 'added') {
|
} else if (this.status === 'added') {
|
||||||
return hexToColor('D81B60');
|
return hexToColor('D81B60');
|
||||||
|
@ -156,7 +156,7 @@ export class BlockPredictionGraphComponent implements OnInit {
|
|||||||
type: 'category',
|
type: 'category',
|
||||||
axisLine: { onZero: true },
|
axisLine: { onZero: true },
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
formatter: val => formatterXAxisTimeCategory(this.locale, this.timespan, parseInt(val, 10)),
|
formatter: val => formatterXAxisTimeCategory(this.locale, this.timespan, parseInt(val, 10) * 1000),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
lineHeight: 12,
|
lineHeight: 12,
|
||||||
|
@ -39,7 +39,9 @@
|
|||||||
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-per-country' | relativeUrl]"
|
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-per-country' | relativeUrl]"
|
||||||
i18n="lightning.nodes-per-isp">Lightning nodes per country</a>
|
i18n="lightning.nodes-per-isp">Lightning nodes per country</a>
|
||||||
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-map' | relativeUrl]"
|
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-map' | relativeUrl]"
|
||||||
i18n="lightning.nodes-per-isp">Lightning nodes world map</a>
|
i18n="lightning.lightning.nodes-heatmap">Lightning nodes world map</a>
|
||||||
|
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-channels-map' | relativeUrl]"
|
||||||
|
i18n="lightning.nodes-channels-world-map">Lightning nodes channels world map</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
<ng-container *ngIf="{ val: network$ | async } as network">
|
<ng-container *ngIf="{ val: network$ | async } as network">
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
|
||||||
|
<ng-template [ngIf]="subdomain">
|
||||||
|
<div class="subdomain_container">
|
||||||
|
<img [src]="'/api/v1/enterprise/images/' + subdomain + '/logo'" class="subdomain_logo">
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
<a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;">
|
<a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;">
|
||||||
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
|
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
|
||||||
<img *ngIf="!officialMempoolSpace" src="/resources/mempool-logo.png" height="35" width="140" class="logo" [ngStyle]="{'opacity': connectionState.val === 2 ? 1 : 0.5 }" alt="The Mempool Open Source Project logo">
|
<img *ngIf="!officialMempoolSpace" src="/resources/mempool-logo.png" height="35" width="140" class="logo" [ngStyle]="{'opacity': connectionState.val === 2 ? 1 : 0.5 }" alt="The Mempool Open Source Project logo">
|
||||||
|
@ -145,3 +145,15 @@ nav {
|
|||||||
.navbar-dark .navbar-nav .nav-link {
|
.navbar-dark .navbar-nav .nav-link {
|
||||||
color: #f1f1f1;
|
color: #f1f1f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subdomain_logo {
|
||||||
|
max-height: 45px;
|
||||||
|
max-width: 140px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subdomain_container {
|
||||||
|
width: 140px;
|
||||||
|
margin-right: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
import { Env, StateService } from '../../services/state.service';
|
import { Env, StateService } from '../../services/state.service';
|
||||||
import { Observable, merge, of } from 'rxjs';
|
import { Observable, merge, of } from 'rxjs';
|
||||||
import { LanguageService } from 'src/app/services/language.service';
|
import { LanguageService } from 'src/app/services/language.service';
|
||||||
|
import { EnterpriseService } from 'src/app/services/enterprise.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-master-page',
|
selector: 'app-master-page',
|
||||||
@ -16,10 +17,12 @@ export class MasterPageComponent implements OnInit {
|
|||||||
isMobile = window.innerWidth <= 767.98;
|
isMobile = window.innerWidth <= 767.98;
|
||||||
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
||||||
urlLanguage: string;
|
urlLanguage: string;
|
||||||
|
subdomain = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public stateService: StateService,
|
public stateService: StateService,
|
||||||
private languageService: LanguageService,
|
private languageService: LanguageService,
|
||||||
|
private enterpriseService: EnterpriseService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -27,6 +30,7 @@ export class MasterPageComponent implements OnInit {
|
|||||||
this.connectionState$ = this.stateService.connectionState$;
|
this.connectionState$ = this.stateService.connectionState$;
|
||||||
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
||||||
this.urlLanguage = this.languageService.getLanguageForUrl();
|
this.urlLanguage = this.languageService.getLanguageForUrl();
|
||||||
|
this.subdomain = this.enterpriseService.getSubdomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
collapse(): void {
|
collapse(): void {
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
<span *ngIf="segwitGains.potentialP2shGains" class="badge badge-danger mr-1" i18n-ngbTooltip="ngbTooltip about missed out gains" ngbTooltip="This transaction could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% on fees by upgrading to native SegWit-Bech32 or {{ segwitGains.potentialP2shGains * 100 | number: '1.0-0' }}% by upgrading to SegWit-P2SH" placement="bottom"><del i18n="tx-features.tag.segwit|SegWit">SegWit</del></span>
|
<span *ngIf="segwitGains.potentialP2shGains" class="badge badge-danger mr-1" i18n-ngbTooltip="ngbTooltip about missed out gains" ngbTooltip="This transaction could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% on fees by upgrading to native SegWit-Bech32 or {{ segwitGains.potentialP2shGains * 100 | number: '1.0-0' }}% by upgrading to SegWit-P2SH" placement="bottom"><del i18n="tx-features.tag.segwit|SegWit">SegWit</del></span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<span *ngIf="isTaproot" class="badge badge-success mr-1" i18n-ngbTooltip="Taproot tooltip" ngbTooltip="This transaction uses Taproot" placement="bottom" i18n="tx-features.tag.taproot">Taproot</span>
|
<span *ngIf="isTaproot; else noTaproot" class="badge badge-success mr-1" i18n-ngbTooltip="Taproot tooltip" ngbTooltip="This transaction uses Taproot" placement="bottom" i18n="tx-features.tag.taproot">Taproot</span>
|
||||||
|
<ng-template #noTaproot>
|
||||||
|
<span class="badge badge-danger mr-1" i18n-ngbTooltip="No Taproot tooltip" ngbTooltip="This transaction could save on fees and improve privacy by using Taproot" placement="bottom"><del i18n="tx-features.tag.taproot">Taproot</del></span>
|
||||||
|
</ng-template>
|
||||||
<span *ngIf="isRbfTransaction; else rbfDisabled" class="badge badge-success" i18n-ngbTooltip="RBF tooltip" ngbTooltip="This transaction support Replace-By-Fee (RBF) allowing fee bumping" placement="bottom" i18n="tx-features.tag.rbf|RBF">RBF</span>
|
<span *ngIf="isRbfTransaction; else rbfDisabled" class="badge badge-success" i18n-ngbTooltip="RBF tooltip" ngbTooltip="This transaction support Replace-By-Fee (RBF) allowing fee bumping" placement="bottom" i18n="tx-features.tag.rbf|RBF">RBF</span>
|
||||||
<ng-template #rbfDisabled><span class="badge badge-danger mr-1" i18n-ngbTooltip="RBF disabled tooltip" ngbTooltip="This transaction does NOT support Replace-By-Fee (RBF) and cannot be fee bumped using this method" placement="bottom"><del i18n="tx-features.tag.rbf|RBF">RBF</del></span></ng-template>
|
<ng-template #rbfDisabled><span class="badge badge-danger mr-1" i18n-ngbTooltip="RBF disabled tooltip" ngbTooltip="This transaction does NOT support Replace-By-Fee (RBF) and cannot be fee bumped using this method" placement="bottom"><del i18n="tx-features.tag.rbf|RBF">RBF</del></span></ng-template>
|
||||||
|
@ -23,6 +23,7 @@ import { LightningStatisticsChartComponent } from '../lightning/statistics-chart
|
|||||||
import { NodesPerISPChartComponent } from '../lightning/nodes-per-isp-chart/nodes-per-isp-chart.component';
|
import { NodesPerISPChartComponent } from '../lightning/nodes-per-isp-chart/nodes-per-isp-chart.component';
|
||||||
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
|
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
|
||||||
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
||||||
|
import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component';
|
||||||
|
|
||||||
const browserWindow = window || {};
|
const browserWindow = window || {};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -114,6 +115,10 @@ const routes: Routes = [
|
|||||||
path: 'lightning/nodes-map',
|
path: 'lightning/nodes-map',
|
||||||
component: NodesMap,
|
component: NodesMap,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'lightning/nodes-channels-map',
|
||||||
|
component: NodesChannelsMap,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
redirectTo: 'mempool',
|
redirectTo: 'mempool',
|
||||||
|
@ -23,6 +23,7 @@ import { NodesPerCountry } from './nodes-per-country/nodes-per-country.component
|
|||||||
import { NodesPerISP } from './nodes-per-isp/nodes-per-isp.component';
|
import { NodesPerISP } from './nodes-per-isp/nodes-per-isp.component';
|
||||||
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
|
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
|
||||||
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
||||||
|
import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
LightningDashboardComponent,
|
LightningDashboardComponent,
|
||||||
@ -43,6 +44,7 @@ import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
|||||||
NodesPerISP,
|
NodesPerISP,
|
||||||
NodesPerCountryChartComponent,
|
NodesPerCountryChartComponent,
|
||||||
NodesMap,
|
NodesMap,
|
||||||
|
NodesChannelsMap,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
<div class="full-container">
|
||||||
|
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="d-flex d-md-block align-items-baseline" style="margin-bottom: -5px">
|
||||||
|
<span i18n="lightning.nodes-channels-world-map">Lightning nodes channels world map</span>
|
||||||
|
<button class="btn p-0 pl-2" style="margin: 0 0 4px 0px">
|
||||||
|
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true" (click)="onSaveChart()"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<small style="color: #ffffff66" i18n="lightning.tor-nodes-excluded">(Tor nodes excluded)</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="observable$ | async" class="chart" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||||
|
(chartInit)="onChartInit($event)">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,40 @@
|
|||||||
|
.card-header {
|
||||||
|
border-bottom: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
@media (min-width: 465px) {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-container {
|
||||||
|
padding: 0px 15px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 500px;
|
||||||
|
height: calc(100% - 150px);
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
padding-right: 10px;
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
padding-bottom: 25px;
|
||||||
|
}
|
||||||
|
@media (max-width: 829px) {
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
padding-bottom: 25px;
|
||||||
|
}
|
||||||
|
@media (max-width: 629px) {
|
||||||
|
padding-bottom: 55px;
|
||||||
|
}
|
||||||
|
@media (max-width: 567px) {
|
||||||
|
padding-bottom: 55px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,189 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, NgZone, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
|
import { Observable, tap, zip } from 'rxjs';
|
||||||
|
import { AssetsService } from 'src/app/services/assets.service';
|
||||||
|
import { download } from 'src/app/shared/graphs.utils';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||||
|
import { StateService } from 'src/app/services/state.service';
|
||||||
|
import { EChartsOption, registerMap } from 'echarts';
|
||||||
|
import 'echarts-gl';
|
||||||
|
import { SSL_OP_SSLEAY_080_CLIENT_DH_BUG } from 'constants';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-nodes-channels-map',
|
||||||
|
templateUrl: './nodes-channels-map.component.html',
|
||||||
|
styleUrls: ['./nodes-channels-map.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class NodesChannelsMap implements OnInit, OnDestroy {
|
||||||
|
observable$: Observable<any>;
|
||||||
|
|
||||||
|
chartInstance = undefined;
|
||||||
|
chartOptions: EChartsOption = {color: 'dark'};
|
||||||
|
chartInitOptions = {
|
||||||
|
renderer: 'canvas',
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private seoService: SeoService,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private stateService: StateService,
|
||||||
|
private assetsService: AssetsService,
|
||||||
|
private router: Router,
|
||||||
|
private zone: NgZone,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.seoService.setTitle($localize`Lightning nodes channels world map`);
|
||||||
|
|
||||||
|
this.observable$ = zip(
|
||||||
|
this.assetsService.getWorldMapJson$,
|
||||||
|
this.apiService.getChannelsGeo$(),
|
||||||
|
).pipe(tap((data) => {
|
||||||
|
registerMap('world', data[0]);
|
||||||
|
|
||||||
|
const channelsLoc = [];
|
||||||
|
const nodes = [];
|
||||||
|
for (const channel of data[1]) {
|
||||||
|
channelsLoc.push([[channel[2], channel[3]], [channel[6], channel[7]]]);
|
||||||
|
nodes.push({
|
||||||
|
publicKey: channel[0],
|
||||||
|
name: channel[1],
|
||||||
|
value: [channel[2], channel[3]],
|
||||||
|
});
|
||||||
|
nodes.push({
|
||||||
|
publicKey: channel[4],
|
||||||
|
name: channel[5],
|
||||||
|
value: [channel[6], channel[7]],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prepareChartOptions(nodes, channelsLoc);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareChartOptions(nodes, channels) {
|
||||||
|
let title: object;
|
||||||
|
if (channels.length === 0) {
|
||||||
|
title = {
|
||||||
|
textStyle: {
|
||||||
|
color: 'grey',
|
||||||
|
fontSize: 15
|
||||||
|
},
|
||||||
|
text: $localize`No data to display yet`,
|
||||||
|
left: 'center',
|
||||||
|
top: 'center'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chartOptions = {
|
||||||
|
geo3D: {
|
||||||
|
map: 'world',
|
||||||
|
shading: 'color',
|
||||||
|
silent: true,
|
||||||
|
postEffect: {
|
||||||
|
enable: true,
|
||||||
|
bloom: {
|
||||||
|
intensity: 0.01,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
viewControl: {
|
||||||
|
minDistance: 1,
|
||||||
|
distance: 60,
|
||||||
|
alpha: 89,
|
||||||
|
panMouseButton: 'left',
|
||||||
|
rotateMouseButton: 'right',
|
||||||
|
zoomSensivity: 0.5,
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
opacity: 0.02,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'black',
|
||||||
|
},
|
||||||
|
regionHeight: 0.01,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
type: 'lines3D',
|
||||||
|
coordinateSystem: 'geo3D',
|
||||||
|
blendMode: 'lighter',
|
||||||
|
lineStyle: {
|
||||||
|
width: 1,
|
||||||
|
opacity: 0.025,
|
||||||
|
},
|
||||||
|
data: channels
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
type: 'scatter3D',
|
||||||
|
symbol: 'circle',
|
||||||
|
blendMode: 'lighter',
|
||||||
|
coordinateSystem: 'geo3D',
|
||||||
|
symbolSize: 3,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#BBFFFF',
|
||||||
|
opacity: 1,
|
||||||
|
borderColor: '#FFFFFF00',
|
||||||
|
},
|
||||||
|
data: nodes,
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
position: 'top',
|
||||||
|
// @ts-ignore
|
||||||
|
textStyle: {
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
formatter: function(value) {
|
||||||
|
return value.name;
|
||||||
|
},
|
||||||
|
show: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onChartInit(ec) {
|
||||||
|
if (this.chartInstance !== undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chartInstance = ec;
|
||||||
|
|
||||||
|
this.chartInstance.on('click', (e) => {
|
||||||
|
if (e.data && e.data.publicKey) {
|
||||||
|
this.zone.run(() => {
|
||||||
|
const url = new RelativeUrlPipe(this.stateService).transform(`/lightning/node/${e.data.publicKey}`);
|
||||||
|
this.router.navigate([url]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSaveChart() {
|
||||||
|
// @ts-ignore
|
||||||
|
const prevBottom = this.chartOptions.grid.bottom;
|
||||||
|
const now = new Date();
|
||||||
|
// @ts-ignore
|
||||||
|
this.chartOptions.grid.bottom = 30;
|
||||||
|
this.chartOptions.backgroundColor = '#11131f';
|
||||||
|
this.chartInstance.setOption(this.chartOptions);
|
||||||
|
download(this.chartInstance.getDataURL({
|
||||||
|
pixelRatio: 2,
|
||||||
|
excludeComponents: ['dataZoom'],
|
||||||
|
}), `lightning-nodes-heatmap-clearnet-${Math.round(now.getTime() / 1000)}.svg`);
|
||||||
|
// @ts-ignore
|
||||||
|
this.chartOptions.grid.bottom = prevBottom;
|
||||||
|
this.chartOptions.backgroundColor = 'none';
|
||||||
|
this.chartInstance.setOption(this.chartOptions);
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ import { SeoService } from 'src/app/services/seo.service';
|
|||||||
import { ApiService } from 'src/app/services/api.service';
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
import { combineLatest, Observable, tap } from 'rxjs';
|
import { combineLatest, Observable, tap } from 'rxjs';
|
||||||
import { AssetsService } from 'src/app/services/assets.service';
|
import { AssetsService } from 'src/app/services/assets.service';
|
||||||
import { EChartsOption, MapSeriesOption, registerMap } from 'echarts';
|
import { EChartsOption, registerMap } from 'echarts';
|
||||||
import { download } from 'src/app/shared/graphs.utils';
|
import { download } from 'src/app/shared/graphs.utils';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||||
|
@ -238,6 +238,10 @@ export class ApiService {
|
|||||||
return this.httpClient.get<RewardStats>(this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/reward-stats/${blockCount}`);
|
return this.httpClient.get<RewardStats>(this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/reward-stats/${blockCount}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEnterpriseInfo$(name: string): Observable<any> {
|
||||||
|
return this.httpClient.get<any>(this.apiBaseUrl + this.apiBasePath + `/api/v1/enterprise/info/` + name);
|
||||||
|
}
|
||||||
|
|
||||||
getChannelByTxIds$(txIds: string[]): Observable<{ inputs: any[], outputs: any[] }> {
|
getChannelByTxIds$(txIds: string[]): Observable<{ inputs: any[], outputs: any[] }> {
|
||||||
let params = new HttpParams();
|
let params = new HttpParams();
|
||||||
txIds.forEach((txId: string) => {
|
txIds.forEach((txId: string) => {
|
||||||
@ -266,4 +270,8 @@ export class ApiService {
|
|||||||
getNodesPerCountry(): Observable<any> {
|
getNodesPerCountry(): Observable<any> {
|
||||||
return this.httpClient.get<any[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/countries');
|
return this.httpClient.get<any[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/countries');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getChannelsGeo$(): Observable<any> {
|
||||||
|
return this.httpClient.get<any[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/channels-geo');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
79
frontend/src/app/services/enterprise.service.ts
Normal file
79
frontend/src/app/services/enterprise.service.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
import { ApiService } from './api.service';
|
||||||
|
import { SeoService } from './seo.service';
|
||||||
|
import { StateService } from './state.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class EnterpriseService {
|
||||||
|
exclusiveHostName = '.mempool.space';
|
||||||
|
subdomain: string | null = null;
|
||||||
|
info: object = {};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(DOCUMENT) private document: Document,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private seoService: SeoService,
|
||||||
|
private stateService: StateService,
|
||||||
|
) {
|
||||||
|
const subdomain = this.document.location.hostname.indexOf(this.exclusiveHostName) > -1
|
||||||
|
&& this.document.location.hostname.split(this.exclusiveHostName)[0] || false;
|
||||||
|
if (subdomain && subdomain.match(/^[A-z0-9-_]+$/)) {
|
||||||
|
this.subdomain = subdomain;
|
||||||
|
this.fetchSubdomainInfo();
|
||||||
|
this.disableSubnetworks();
|
||||||
|
} else {
|
||||||
|
this.insertMatomo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubdomain() {
|
||||||
|
return this.subdomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
disableSubnetworks() {
|
||||||
|
this.stateService.env.TESTNET_ENABLED = false;
|
||||||
|
this.stateService.env.LIQUID_ENABLED = false;
|
||||||
|
this.stateService.env.LIQUID_TESTNET_ENABLED = false;
|
||||||
|
this.stateService.env.SIGNET_ENABLED = false;
|
||||||
|
this.stateService.env.BISQ_ENABLED = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchSubdomainInfo() {
|
||||||
|
this.apiService.getEnterpriseInfo$(this.subdomain).subscribe((info) => {
|
||||||
|
this.info = info;
|
||||||
|
this.insertMatomo(info.site_id);
|
||||||
|
this.seoService.setEnterpriseTitle(info.title);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (error.status === 404) {
|
||||||
|
window.location.href = 'https://mempool.space';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
insertMatomo(siteId = 5) {
|
||||||
|
let statsUrl = '//stats.mempool.space/';
|
||||||
|
if (this.document.location.hostname === 'liquid.network') {
|
||||||
|
statsUrl = '//stats.liquid.network/';
|
||||||
|
siteId = 8;
|
||||||
|
} else if (this.document.location.hostname === 'bisq.markets') {
|
||||||
|
statsUrl = '//stats.bisq.markets/';
|
||||||
|
siteId = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const _paq = window._paq = window._paq || [];
|
||||||
|
_paq.push(['disableCookies']);
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
(function() {
|
||||||
|
_paq.push(['setTrackerUrl', statsUrl+'m.php']);
|
||||||
|
_paq.push(['setSiteId', siteId.toString()]);
|
||||||
|
const d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
|
g.type='text/javascript'; g.async=true; g.src=statsUrl+'m.js'; s.parentNode.insertBefore(g,s);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import { StateService } from './state.service';
|
|||||||
})
|
})
|
||||||
export class SeoService {
|
export class SeoService {
|
||||||
network = '';
|
network = '';
|
||||||
|
baseTitle = 'mempool';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private titleService: Title,
|
private titleService: Title,
|
||||||
@ -26,18 +27,23 @@ export class SeoService {
|
|||||||
this.metaService.updateTag({ property: 'og:title', content: this.getTitle()});
|
this.metaService.updateTag({ property: 'og:title', content: this.getTitle()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEnterpriseTitle(title: string) {
|
||||||
|
this.baseTitle = title + ' - ' + this.baseTitle;
|
||||||
|
this.resetTitle();
|
||||||
|
}
|
||||||
|
|
||||||
getTitle(): string {
|
getTitle(): string {
|
||||||
if (this.network === 'testnet')
|
if (this.network === 'testnet')
|
||||||
return 'mempool - Bitcoin Testnet';
|
return this.baseTitle + ' - Bitcoin Testnet';
|
||||||
if (this.network === 'signet')
|
if (this.network === 'signet')
|
||||||
return 'mempool - Bitcoin Signet';
|
return this.baseTitle + ' - Bitcoin Signet';
|
||||||
if (this.network === 'liquid')
|
if (this.network === 'liquid')
|
||||||
return 'mempool - Liquid Network';
|
return this.baseTitle + ' - Liquid Network';
|
||||||
if (this.network === 'liquidtestnet')
|
if (this.network === 'liquidtestnet')
|
||||||
return 'mempool - Liquid Testnet';
|
return this.baseTitle + ' - Liquid Testnet';
|
||||||
if (this.network === 'bisq')
|
if (this.network === 'bisq')
|
||||||
return 'mempool - Bisq Markets';
|
return this.baseTitle + ' - Bisq Markets';
|
||||||
return 'mempool - ' + (this.network ? this.ucfirst(this.network) : 'Bitcoin') + ' Explorer';
|
return this.baseTitle + ' - ' + (this.network ? this.ucfirst(this.network) : 'Bitcoin') + ' Explorer';
|
||||||
}
|
}
|
||||||
|
|
||||||
ucfirst(str: string) {
|
ucfirst(str: string) {
|
||||||
|
@ -37,21 +37,5 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<script type="text/javascript">
|
|
||||||
if (document.location.hostname === "bisq.markets")
|
|
||||||
{
|
|
||||||
var _paq = window._paq = window._paq || [];
|
|
||||||
_paq.push(['disableCookies']);
|
|
||||||
_paq.push(['trackPageView']);
|
|
||||||
_paq.push(['enableLinkTracking']);
|
|
||||||
(function() {
|
|
||||||
var u="//stats.bisq.markets/";
|
|
||||||
_paq.push(['setTrackerUrl', u+'m.php']);
|
|
||||||
_paq.push(['setSiteId', '7']);
|
|
||||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
||||||
g.type='text/javascript'; g.async=true; g.src=u+'m.js'; s.parentNode.insertBefore(g,s);
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -35,21 +35,5 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<script type="text/javascript">
|
|
||||||
if (document.location.hostname === "liquid.network")
|
|
||||||
{
|
|
||||||
var _paq = window._paq = window._paq || [];
|
|
||||||
_paq.push(['disableCookies']);
|
|
||||||
_paq.push(['trackPageView']);
|
|
||||||
_paq.push(['enableLinkTracking']);
|
|
||||||
(function() {
|
|
||||||
var u="//stats.liquid.network/";
|
|
||||||
_paq.push(['setTrackerUrl', u+'m.php']);
|
|
||||||
_paq.push(['setSiteId', '8']);
|
|
||||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
||||||
g.type='text/javascript'; g.async=true; g.src=u+'m.js'; s.parentNode.insertBefore(g,s);
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -34,21 +34,5 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<script type="text/javascript">
|
|
||||||
if (document.location.hostname === "mempool.space")
|
|
||||||
{
|
|
||||||
var _paq = window._paq = window._paq || [];
|
|
||||||
_paq.push(['disableCookies']);
|
|
||||||
_paq.push(['trackPageView']);
|
|
||||||
_paq.push(['enableLinkTracking']);
|
|
||||||
(function() {
|
|
||||||
var u="//stats.mempool.space/";
|
|
||||||
_paq.push(['setTrackerUrl', u+'m.php']);
|
|
||||||
_paq.push(['setSiteId', '5']);
|
|
||||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
||||||
g.type='text/javascript'; g.async=true; g.src=u+'m.js'; s.parentNode.insertBefore(g,s);
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -70,6 +70,30 @@ location /api/v1/translators {
|
|||||||
proxy_hide_header content-security-policy;
|
proxy_hide_header content-security-policy;
|
||||||
proxy_hide_header x-frame-options;
|
proxy_hide_header x-frame-options;
|
||||||
}
|
}
|
||||||
|
location /api/v1/enterprise/images {
|
||||||
|
proxy_pass $mempoolSpaceServices;
|
||||||
|
proxy_cache services;
|
||||||
|
proxy_cache_background_update on;
|
||||||
|
proxy_cache_use_stale updating;
|
||||||
|
proxy_cache_valid 200 10m;
|
||||||
|
expires 10m;
|
||||||
|
proxy_hide_header onion-location;
|
||||||
|
proxy_hide_header strict-transport-security;
|
||||||
|
proxy_hide_header content-security-policy;
|
||||||
|
proxy_hide_header x-frame-options;
|
||||||
|
}
|
||||||
|
location /api/v1/enterprise {
|
||||||
|
proxy_pass $mempoolSpaceServices;
|
||||||
|
proxy_cache services;
|
||||||
|
proxy_cache_background_update on;
|
||||||
|
proxy_cache_use_stale updating;
|
||||||
|
proxy_cache_valid 200 5m;
|
||||||
|
expires 5m;
|
||||||
|
proxy_hide_header onion-location;
|
||||||
|
proxy_hide_header strict-transport-security;
|
||||||
|
proxy_hide_header content-security-policy;
|
||||||
|
proxy_hide_header x-frame-options;
|
||||||
|
}
|
||||||
location /api/v1/assets {
|
location /api/v1/assets {
|
||||||
proxy_pass $mempoolSpaceServices;
|
proxy_pass $mempoolSpaceServices;
|
||||||
proxy_cache services;
|
proxy_cache services;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user