mirror of
https://github.com/mempool/mempool.git
synced 2025-03-15 04:11:48 +01:00
Merge branch 'master' into nymkappa/proxy-accel-endpoints
This commit is contained in:
commit
0f26940018
11 changed files with 110 additions and 75 deletions
87
backend/src/api/about.routes.ts
Normal file
87
backend/src/api/about.routes.ts
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import { Application } from "express";
|
||||||
|
import config from "../config";
|
||||||
|
import axios from "axios";
|
||||||
|
import logger from "../logger";
|
||||||
|
|
||||||
|
class AboutRoutes {
|
||||||
|
public initRoutes(app: Application) {
|
||||||
|
app
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'donations', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/donations`, { responseType: 'stream', timeout: 10000 });
|
||||||
|
response.data.pipe(res);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).end();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'donations/images/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/donations/images/${req.params.id}`, {
|
||||||
|
responseType: 'stream', timeout: 10000
|
||||||
|
});
|
||||||
|
response.data.pipe(res);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).end();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'contributors', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/contributors`, { responseType: 'stream', timeout: 10000 });
|
||||||
|
response.data.pipe(res);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).end();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'contributors/images/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/contributors/images/${req.params.id}`, {
|
||||||
|
responseType: 'stream', timeout: 10000
|
||||||
|
});
|
||||||
|
response.data.pipe(res);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).end();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'translators', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/translators`, { responseType: 'stream', timeout: 10000 });
|
||||||
|
response.data.pipe(res);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).end();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'translators/images/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/translators/images/${req.params.id}`, {
|
||||||
|
responseType: 'stream', timeout: 10000
|
||||||
|
});
|
||||||
|
response.data.pipe(res);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).end();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'services/sponsors', async (req, res) => {
|
||||||
|
const url = `${config.MEMPOOL_SERVICES.API}/${req.originalUrl.replace('/api/v1/services/', '')}`;
|
||||||
|
try {
|
||||||
|
const response = await axios.get(url, { responseType: 'stream', timeout: 10000 });
|
||||||
|
response.data.pipe(res);
|
||||||
|
} catch (e) {
|
||||||
|
logger.err(`Unable to fetch sponsors from ${url}. ${e}`, 'About Page');
|
||||||
|
res.status(500).end();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'services/account/images/:username', async (req, res) => {
|
||||||
|
const url = `${config.MEMPOOL_SERVICES.API}/${req.originalUrl.replace('/api/v1/services/', '')}`;
|
||||||
|
try {
|
||||||
|
const response = await axios.get(url, { responseType: 'stream', timeout: 10000 });
|
||||||
|
response.data.pipe(res);
|
||||||
|
} catch (e) {
|
||||||
|
logger.err(`Unable to fetch sponsor profile image from ${url}. ${e}`, 'About Page');
|
||||||
|
res.status(500).end();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new AboutRoutes();
|
|
@ -37,60 +37,6 @@ class BitcoinRoutes {
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'replacements', this.getRbfReplacements)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'replacements', this.getRbfReplacements)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'fullrbf/replacements', this.getFullRbfReplacements)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'fullrbf/replacements', this.getFullRbfReplacements)
|
||||||
.post(config.MEMPOOL.API_URL_PREFIX + 'tx/push', this.$postTransactionForm)
|
.post(config.MEMPOOL.API_URL_PREFIX + 'tx/push', this.$postTransactionForm)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'donations', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/donations`, { responseType: 'stream', timeout: 10000 });
|
|
||||||
response.data.pipe(res);
|
|
||||||
} catch (e) {
|
|
||||||
res.status(500).end();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'donations/images/:id', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/donations/images/${req.params.id}`, {
|
|
||||||
responseType: 'stream', timeout: 10000
|
|
||||||
});
|
|
||||||
response.data.pipe(res);
|
|
||||||
} catch (e) {
|
|
||||||
res.status(500).end();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'contributors', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/contributors`, { responseType: 'stream', timeout: 10000 });
|
|
||||||
response.data.pipe(res);
|
|
||||||
} catch (e) {
|
|
||||||
res.status(500).end();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'contributors/images/:id', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/contributors/images/${req.params.id}`, {
|
|
||||||
responseType: 'stream', timeout: 10000
|
|
||||||
});
|
|
||||||
response.data.pipe(res);
|
|
||||||
} catch (e) {
|
|
||||||
res.status(500).end();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'translators', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/translators`, { responseType: 'stream', timeout: 10000 });
|
|
||||||
response.data.pipe(res);
|
|
||||||
} catch (e) {
|
|
||||||
res.status(500).end();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'translators/images/:id', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${config.EXTERNAL_DATA_SERVER.MEMPOOL_API}/translators/images/${req.params.id}`, {
|
|
||||||
responseType: 'stream', timeout: 10000
|
|
||||||
});
|
|
||||||
response.data.pipe(res);
|
|
||||||
} catch (e) {
|
|
||||||
res.status(500).end();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks', this.getBlocks.bind(this))
|
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks', this.getBlocks.bind(this))
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', this.getBlocks.bind(this))
|
.get(config.MEMPOOL.API_URL_PREFIX + 'blocks/:height', this.getBlocks.bind(this))
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', this.getBlock)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash', this.getBlock)
|
||||||
|
|
|
@ -655,6 +655,7 @@ class DatabaseMigration {
|
||||||
|
|
||||||
await this.$executeQuery('TRUNCATE hashrates');
|
await this.$executeQuery('TRUNCATE hashrates');
|
||||||
await this.$executeQuery('TRUNCATE difficulty_adjustments');
|
await this.$executeQuery('TRUNCATE difficulty_adjustments');
|
||||||
|
await this.$executeQuery(`UPDATE state SET string = NULL WHERE name = 'pools_json_sha'`);
|
||||||
|
|
||||||
await this.updateToSchemaVersion(75);
|
await this.updateToSchemaVersion(75);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ import accelerationApi from './api/services/acceleration';
|
||||||
import bitcoinCoreRoutes from './api/bitcoin/bitcoin-core.routes';
|
import bitcoinCoreRoutes from './api/bitcoin/bitcoin-core.routes';
|
||||||
import bitcoinSecondClient from './api/bitcoin/bitcoin-second-client';
|
import bitcoinSecondClient from './api/bitcoin/bitcoin-second-client';
|
||||||
import accelerationRoutes from './api/acceleration/acceleration.routes';
|
import accelerationRoutes from './api/acceleration/acceleration.routes';
|
||||||
|
import aboutRoutes from './api/about.routes';
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
|
@ -309,6 +310,7 @@ class Server {
|
||||||
if (config.MEMPOOL_SERVICES.ACCELERATIONS) {
|
if (config.MEMPOOL_SERVICES.ACCELERATIONS) {
|
||||||
accelerationRoutes.initRoutes(this.app);
|
accelerationRoutes.initRoutes(this.app);
|
||||||
}
|
}
|
||||||
|
aboutRoutes.initRoutes(this.app);
|
||||||
}
|
}
|
||||||
|
|
||||||
healthCheck(): void {
|
healthCheck(): void {
|
||||||
|
|
|
@ -263,7 +263,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||||
imports: [RouterModule.forRoot(routes, {
|
imports: [RouterModule.forRoot(routes, {
|
||||||
initialNavigation: 'enabledBlocking',
|
initialNavigation: 'enabledBlocking',
|
||||||
scrollPositionRestoration: 'enabled',
|
scrollPositionRestoration: 'enabled',
|
||||||
anchorScrolling: 'enabled',
|
anchorScrolling: 'disabled',
|
||||||
preloadingStrategy: AppPreloadingStrategy
|
preloadingStrategy: AppPreloadingStrategy
|
||||||
})],
|
})],
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
</div>
|
</div>
|
||||||
<div *ngFor="let item of tabData">
|
<div *ngFor="let item of tabData">
|
||||||
<p *ngIf="( item.type === 'category' ) && ( item.showConditions.indexOf(network.val) > -1 ) && ( !item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance ))">{{ item.title }}</p>
|
<p *ngIf="( item.type === 'category' ) && ( item.showConditions.indexOf(network.val) > -1 ) && ( !item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance ))">{{ item.title }}</p>
|
||||||
<a *ngIf="( item.type !== 'category' ) && ( item.showConditions.indexOf(network.val) > -1 ) && ( !item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance ) || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('auditOnly') && item.options.auditOnly && auditEnabled ) )" [routerLink]="['./']" fragment="{{ item.fragment }}" (click)="navLinkClick($event)">{{ item.title }}</a>
|
<a *ngIf="( item.type !== 'category' ) && ( item.showConditions.indexOf(network.val) > -1 ) && ( !item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance ) || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('auditOnly') && item.options.auditOnly && auditEnabled ) )" (click)="navLinkClick($event, item.fragment)">{{ item.title }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,6 +10,7 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#enterprise-cta-desktop {
|
#enterprise-cta-desktop {
|
||||||
|
|
|
@ -33,8 +33,9 @@ export class ApiDocsNavComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
navLinkClick(event) {
|
navLinkClick(event, fragment) {
|
||||||
this.navLinkClickEvent.emit(event);
|
event.preventDefault();
|
||||||
|
this.navLinkClickEvent.emit({event: event, fragment: fragment});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<div *ngIf="!item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance ) || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('auditOnly') && item.options.auditOnly && auditEnabled )">
|
<div *ngIf="!item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance ) || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('auditOnly') && item.options.auditOnly && auditEnabled )">
|
||||||
<h3 *ngIf="item.type === 'category'">{{ item.title }}</h3>
|
<h3 *ngIf="item.type === 'category'">{{ item.title }}</h3>
|
||||||
<div *ngIf="item.type !== 'category'" class="endpoint-container" id="{{ item.fragment }}">
|
<div *ngIf="item.type !== 'category'" class="endpoint-container" id="{{ item.fragment }}">
|
||||||
<a id="{{ item.fragment + '-tab-header' }}" class="section-header" (click)="anchorLinkClick( $event )" [routerLink]="['./']" fragment="{{ item.fragment }}"><table><tr><td>{{ item.title }}</td><td><span>{{ item.category }}</span></td></tr></table></a>
|
<a id="{{ item.fragment + '-tab-header' }}" class="section-header" (click)="anchorLinkClick({event: $event, fragment: item.fragment})"><table><tr><td>{{ item.title }}</td><td><span>{{ item.category }}</span></td></tr></table></a>
|
||||||
<div class="endpoint-content">
|
<div class="endpoint-content">
|
||||||
<ng-container *ngTemplateOutlet="dict[item.fragment]" class="endpoint"></ng-container>
|
<ng-container *ngTemplateOutlet="dict[item.fragment]" class="endpoint"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
<div *ngIf="!item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance )">
|
<div *ngIf="!item.hasOwnProperty('options') || ( item.hasOwnProperty('options') && item.options.hasOwnProperty('officialOnly') && item.options.officialOnly && officialMempoolInstance )">
|
||||||
<h3 *ngIf="( item.type === 'category' ) && ( item.showConditions.indexOf(network.val) > -1 )">{{ item.title }}</h3>
|
<h3 *ngIf="( item.type === 'category' ) && ( item.showConditions.indexOf(network.val) > -1 )">{{ item.title }}</h3>
|
||||||
<div *ngIf="( item.type !== 'category' ) && ( item.showConditions.indexOf(network.val) > -1 )" class="endpoint-container" id="{{ item.fragment }}">
|
<div *ngIf="( item.type !== 'category' ) && ( item.showConditions.indexOf(network.val) > -1 )" class="endpoint-container" id="{{ item.fragment }}">
|
||||||
<a id="{{ item.fragment + '-tab-header' }}" class="section-header" (click)="anchorLinkClick( $event )" [routerLink]="['./']" fragment="{{ item.fragment }}">{{ item.title }} <span>{{ item.category }}</span></a>
|
<a id="{{ item.fragment + '-tab-header' }}" class="section-header" (click)="anchorLinkClick({event: $event, fragment: item.fragment})">{{ item.title }} <span>{{ item.category }}</span></a>
|
||||||
<div class="endpoint-content">
|
<div class="endpoint-content">
|
||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||||
|
|
|
@ -204,6 +204,7 @@ h3 {
|
||||||
margin: 20px 0 20px 0;
|
margin: 20px 0 20px 0;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.endpoint-container .section-header:hover {
|
.endpoint-container .section-header:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -56,7 +56,10 @@ export class ApiDocsComponent implements OnInit, AfterViewInit {
|
||||||
if( this.route.snapshot.fragment ) {
|
if( this.route.snapshot.fragment ) {
|
||||||
this.openEndpointContainer( this.route.snapshot.fragment );
|
this.openEndpointContainer( this.route.snapshot.fragment );
|
||||||
if (document.getElementById( this.route.snapshot.fragment )) {
|
if (document.getElementById( this.route.snapshot.fragment )) {
|
||||||
document.getElementById( this.route.snapshot.fragment ).scrollIntoView();
|
let vOffset = ( window.innerWidth <= 992 ) ? 100 : 60;
|
||||||
|
window.scrollTo({
|
||||||
|
top: document.getElementById( this.route.snapshot.fragment ).offsetTop - vOffset
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.addEventListener('scroll', that.onDocScroll, { passive: true });
|
window.addEventListener('scroll', that.onDocScroll, { passive: true });
|
||||||
|
@ -124,20 +127,13 @@ export class ApiDocsComponent implements OnInit, AfterViewInit {
|
||||||
this.desktopDocsNavPosition = ( window.pageYOffset > 115 ) ? "fixed" : "relative";
|
this.desktopDocsNavPosition = ( window.pageYOffset > 115 ) ? "fixed" : "relative";
|
||||||
}
|
}
|
||||||
|
|
||||||
anchorLinkClick( event: any ) {
|
anchorLinkClick( e ) {
|
||||||
let targetId = "";
|
let targetId = e.fragment;
|
||||||
if( event.target.nodeName === "A" ) {
|
let vOffset = ( window.innerWidth <= 992 ) ? 100 : 60;
|
||||||
targetId = event.target.hash.substring(1);
|
window.scrollTo({
|
||||||
} else {
|
top: document.getElementById( targetId ).offsetTop - vOffset
|
||||||
let element = event.target;
|
});
|
||||||
while( element.nodeName !== "A" ) {
|
window.history.pushState({}, null, document.location.href.split("#")[0] + "#" + targetId);
|
||||||
element = element.parentElement;
|
|
||||||
}
|
|
||||||
targetId = element.hash.substring(1);
|
|
||||||
}
|
|
||||||
if( this.route.snapshot.fragment === targetId && document.getElementById( targetId )) {
|
|
||||||
document.getElementById( targetId ).scrollIntoView();
|
|
||||||
}
|
|
||||||
this.openEndpointContainer( targetId );
|
this.openEndpointContainer( targetId );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue