Merge branch 'master' into unfurler-refactor
This commit is contained in:
		
						commit
						b24f253f84
					
				@ -228,34 +228,75 @@ export class Common {
 | 
			
		||||
    return d.toISOString().split('T')[0] + ' ' + d.toTimeString().split(' ')[0];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static formatSocket(publicKey: string, socket: {network: string, addr: string}): NodeSocket {
 | 
			
		||||
  static findSocketNetwork(addr: string): {network: string | null, url: string} {
 | 
			
		||||
    let network: string | null = null;
 | 
			
		||||
    let url = addr.split('://')[1];
 | 
			
		||||
 | 
			
		||||
    if (config.LIGHTNING.BACKEND === 'cln') {
 | 
			
		||||
      network = socket.network;
 | 
			
		||||
    } else if (config.LIGHTNING.BACKEND === 'lnd') {
 | 
			
		||||
      if (socket.addr.indexOf('onion') !== -1) {
 | 
			
		||||
        if (socket.addr.split('.')[0].length >= 56) {
 | 
			
		||||
          network = 'torv3';
 | 
			
		||||
        } else {
 | 
			
		||||
          network = 'torv2';
 | 
			
		||||
        }
 | 
			
		||||
      } else if (socket.addr.indexOf('i2p') !== -1) {
 | 
			
		||||
        network = 'i2p';
 | 
			
		||||
    if (!url) {
 | 
			
		||||
      return {
 | 
			
		||||
        network: null,
 | 
			
		||||
        url: addr,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (addr.indexOf('onion') !== -1) {
 | 
			
		||||
      if (url.split('.')[0].length >= 56) {
 | 
			
		||||
        network = 'torv3';
 | 
			
		||||
      } else {
 | 
			
		||||
        const ipv = isIP(socket.addr.split(':')[0]);
 | 
			
		||||
        if (ipv === 4) {
 | 
			
		||||
          network = 'ipv4';
 | 
			
		||||
        } else if (ipv === 6) {
 | 
			
		||||
          network = 'ipv6';
 | 
			
		||||
        }
 | 
			
		||||
        network = 'torv2';
 | 
			
		||||
      }
 | 
			
		||||
    } else if (addr.indexOf('i2p') !== -1) {
 | 
			
		||||
      network = 'i2p';
 | 
			
		||||
    } else if (addr.indexOf('ipv4') !== -1) {
 | 
			
		||||
      const ipv = isIP(url.split(':')[0]);
 | 
			
		||||
      if (ipv === 4) {
 | 
			
		||||
        network = 'ipv4';
 | 
			
		||||
      } else {
 | 
			
		||||
        return {
 | 
			
		||||
          network: null,
 | 
			
		||||
          url: addr,
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
    } else if (addr.indexOf('ipv6') !== -1) {
 | 
			
		||||
      url = url.split('[')[1].split(']')[0];
 | 
			
		||||
      const ipv = isIP(url);
 | 
			
		||||
      if (ipv === 6) {
 | 
			
		||||
        const parts = addr.split(':');
 | 
			
		||||
        network = 'ipv6';
 | 
			
		||||
        url = `[${url}]:${parts[parts.length - 1]}`;
 | 
			
		||||
      } else {
 | 
			
		||||
        return {
 | 
			
		||||
          network: null,
 | 
			
		||||
          url: addr,
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      return {
 | 
			
		||||
        network: null,
 | 
			
		||||
        url: addr,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      publicKey: publicKey,
 | 
			
		||||
      network: network,
 | 
			
		||||
      addr: socket.addr,
 | 
			
		||||
      url: url,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static formatSocket(publicKey: string, socket: {network: string, addr: string}): NodeSocket {
 | 
			
		||||
    if (config.LIGHTNING.BACKEND === 'cln') {
 | 
			
		||||
      return {
 | 
			
		||||
        publicKey: publicKey,
 | 
			
		||||
        network: socket.network,
 | 
			
		||||
        addr: socket.addr,
 | 
			
		||||
      };
 | 
			
		||||
    } else /* if (config.LIGHTNING.BACKEND === 'lnd') */ {
 | 
			
		||||
      const formatted = this.findSocketNetwork(socket.addr);
 | 
			
		||||
      return {
 | 
			
		||||
        publicKey: publicKey,
 | 
			
		||||
        network: formatted.network,
 | 
			
		||||
        addr: formatted.url,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -503,6 +503,18 @@ class NodesApi {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Update node sockets
 | 
			
		||||
   */
 | 
			
		||||
  public async $updateNodeSockets(publicKey: string, sockets: {network: string; addr: string}[]): Promise<void> {
 | 
			
		||||
    const formattedSockets = (sockets.map(a => a.addr).join(',')) ?? '';
 | 
			
		||||
    try {
 | 
			
		||||
      await DB.query(`UPDATE nodes SET sockets = ? WHERE public_key = ?`, [formattedSockets, publicKey]);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      logger.err(`Cannot update node sockets for ${publicKey}. Reason: ${e instanceof Error ? e.message : e}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Set all nodes not in `nodesPubkeys` as inactive (status = 0)
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
@ -57,6 +57,8 @@ class LightningStatsImporter {
 | 
			
		||||
          features: node.features,
 | 
			
		||||
        });
 | 
			
		||||
        nodesInDb[node.pub_key] = node;
 | 
			
		||||
      } else {
 | 
			
		||||
        await nodesApi.$updateNodeSockets(node.pub_key, node.addresses);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      let hasOnion = false;
 | 
			
		||||
@ -369,7 +371,7 @@ class LightningStatsImporter {
 | 
			
		||||
          graph = JSON.parse(fileContent);
 | 
			
		||||
          graph = await this.cleanupTopology(graph);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          logger.debug(`Invalid topology file ${this.topologiesFolder}/${filename}, cannot parse the content`);
 | 
			
		||||
          logger.debug(`Invalid topology file ${this.topologiesFolder}/${filename}, cannot parse the content. Reason: ${e instanceof Error ? e.message : e}`);
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
@ -419,9 +421,10 @@ class LightningStatsImporter {
 | 
			
		||||
      const addressesParts = (node.addresses ?? '').split(',');
 | 
			
		||||
      const addresses: any[] = [];
 | 
			
		||||
      for (const address of addressesParts) {
 | 
			
		||||
        const formatted = Common.findSocketNetwork(address);
 | 
			
		||||
        addresses.push({
 | 
			
		||||
          network: '',
 | 
			
		||||
          addr: address
 | 
			
		||||
          network: formatted.network,
 | 
			
		||||
          addr: formatted.url
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';
 | 
			
		||||
import { Routes, RouterModule } from '@angular/router';
 | 
			
		||||
import { AppPreloadingStrategy } from './app.preloading-strategy'
 | 
			
		||||
import { StartComponent } from './components/start/start.component';
 | 
			
		||||
import { TransactionComponent } from './components/transaction/transaction.component';
 | 
			
		||||
import { TransactionPreviewComponent } from './components/transaction/transaction-preview.component';
 | 
			
		||||
@ -25,6 +26,10 @@ import { AssetsComponent } from './components/assets/assets.component';
 | 
			
		||||
import { AssetComponent } from './components/asset/asset.component';
 | 
			
		||||
import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component';
 | 
			
		||||
 | 
			
		||||
const browserWindow = window || {};
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
const browserWindowEnv = browserWindow.__env || {};
 | 
			
		||||
 | 
			
		||||
let routes: Routes = [
 | 
			
		||||
  {
 | 
			
		||||
    path: 'testnet',
 | 
			
		||||
@ -32,7 +37,8 @@ let routes: Routes = [
 | 
			
		||||
      {
 | 
			
		||||
        path: '',
 | 
			
		||||
        pathMatch: 'full',
 | 
			
		||||
        loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule)
 | 
			
		||||
        loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule),
 | 
			
		||||
        data: { preload: true },
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '',
 | 
			
		||||
@ -109,7 +115,8 @@ let routes: Routes = [
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            path: 'docs',
 | 
			
		||||
            loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
 | 
			
		||||
            loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule),
 | 
			
		||||
            data: { preload: true },
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            path: 'api',
 | 
			
		||||
@ -117,7 +124,8 @@ let routes: Routes = [
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            path: 'lightning',
 | 
			
		||||
            loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule)
 | 
			
		||||
            loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule),
 | 
			
		||||
            data: { preload: browserWindowEnv && browserWindowEnv.LIGHTNING === true },
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
@ -410,10 +418,6 @@ let routes: Routes = [
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const browserWindow = window || {};
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
const browserWindowEnv = browserWindow.__env || {};
 | 
			
		||||
 | 
			
		||||
if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'bisq') {
 | 
			
		||||
  routes = [{
 | 
			
		||||
    path: '',
 | 
			
		||||
@ -691,7 +695,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
 | 
			
		||||
    initialNavigation: 'enabled',
 | 
			
		||||
    scrollPositionRestoration: 'enabled',
 | 
			
		||||
    anchorScrolling: 'enabled',
 | 
			
		||||
    preloadingStrategy: PreloadAllModules
 | 
			
		||||
    preloadingStrategy: AppPreloadingStrategy
 | 
			
		||||
  })],
 | 
			
		||||
})
 | 
			
		||||
export class AppRoutingModule { }
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ import { LanguageService } from './services/language.service';
 | 
			
		||||
import { FiatShortenerPipe } from './shared/pipes/fiat-shortener.pipe';
 | 
			
		||||
import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe';
 | 
			
		||||
import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe';
 | 
			
		||||
import { AppPreloadingStrategy } from './app.preloading-strategy';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@ -44,6 +45,7 @@ import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe
 | 
			
		||||
    ShortenStringPipe,
 | 
			
		||||
    FiatShortenerPipe,
 | 
			
		||||
    CapAddressPipe,
 | 
			
		||||
    AppPreloadingStrategy,
 | 
			
		||||
    { provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true }
 | 
			
		||||
  ],
 | 
			
		||||
  bootstrap: [AppComponent]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								frontend/src/app/app.preloading-strategy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								frontend/src/app/app.preloading-strategy.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
import { PreloadingStrategy, Route } from '@angular/router';
 | 
			
		||||
import { Observable, timer, mergeMap, of } from 'rxjs';
 | 
			
		||||
 | 
			
		||||
export class AppPreloadingStrategy implements PreloadingStrategy {
 | 
			
		||||
  preload(route: Route, load: Function): Observable<any> {
 | 
			
		||||
      return route.data && route.data.preload 
 | 
			
		||||
          ? timer(1500).pipe(mergeMap(() => load()))
 | 
			
		||||
          : of(null);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -61,7 +61,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 1130px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 1130px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@
 | 
			
		||||
          </div>  
 | 
			
		||||
 | 
			
		||||
          <form [formGroup]="radioGroupForm" class="formRadioGroup"
 | 
			
		||||
            [class]="stateService.env.MINING_DASHBOARD ? 'mining' : ''" (click)="saveGraphPreference()">
 | 
			
		||||
            [class]="(stateService.env.MINING_DASHBOARD || stateService.env.LIGHTNING) ? 'mining' : 'no-menu'" (click)="saveGraphPreference()">
 | 
			
		||||
            <div *ngIf="!isMobile()" class="btn-group btn-group-toggle">
 | 
			
		||||
              <label ngbButtonLabel class="btn-primary btn-sm mr-2">
 | 
			
		||||
                <a [routerLink]="['/tv' | relativeUrl]" style="color: white" id="btn-tv">
 | 
			
		||||
 | 
			
		||||
@ -55,13 +55,19 @@
 | 
			
		||||
.formRadioGroup.mining {
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: 0px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.formRadioGroup.no-menu {
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -33px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.loading{
 | 
			
		||||
  display: flex;
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
<div *ngIf="channels$ | async as response; else skeleton">
 | 
			
		||||
  <form [formGroup]="channelStatusForm" class="formRadioGroup float-right">
 | 
			
		||||
<div *ngIf="channels$ | async as response; else skeleton" style="position: relative;">
 | 
			
		||||
  <form [formGroup]="channelStatusForm" class="formRadioGroup">
 | 
			
		||||
    <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="status">
 | 
			
		||||
      <label ngbButtonLabel class="btn-primary btn-sm">
 | 
			
		||||
        <input ngbButton type="radio" [value]="'open'" fragment="open" i18n="open">Open
 | 
			
		||||
@ -10,7 +10,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
  </form>
 | 
			
		||||
 | 
			
		||||
  <table class="table table-borderless" *ngIf="response.channels.length > 0">
 | 
			
		||||
  <table class="table table-borderless" *ngIf="response.channels.length > 0" [style]="isLoading ? 'opacity: 0.75' : ''">
 | 
			
		||||
    <ng-container *ngTemplateOutlet="tableHeader"></ng-container>
 | 
			
		||||
    <tbody>
 | 
			
		||||
      <tr *ngFor="let channel of response.channels; let i = index;">
 | 
			
		||||
 | 
			
		||||
@ -7,3 +7,20 @@
 | 
			
		||||
  font-size: 12px;
 | 
			
		||||
  top: 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.formRadioGroup {
 | 
			
		||||
  @media (min-width: 435px) {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    top: -46px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (max-width: 435px) {
 | 
			
		||||
    display: flex;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn-group {
 | 
			
		||||
  @media (max-width: 435px) {
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -14,6 +14,7 @@ import { LightningApiService } from '../lightning-api.service';
 | 
			
		||||
export class ChannelsListComponent implements OnInit, OnChanges {
 | 
			
		||||
  @Input() publicKey: string;
 | 
			
		||||
  @Output() channelsStatusChangedEvent = new EventEmitter<string>();
 | 
			
		||||
  @Output() loadingEvent = new EventEmitter<boolean>(false);
 | 
			
		||||
  channels$: Observable<any>;
 | 
			
		||||
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
@ -26,6 +27,7 @@ export class ChannelsListComponent implements OnInit, OnChanges {
 | 
			
		||||
  defaultStatus = 'open';
 | 
			
		||||
  status = 'open';
 | 
			
		||||
  publicKeySize = 25;
 | 
			
		||||
  isLoading = false;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private lightningApiService: LightningApiService,
 | 
			
		||||
@ -56,6 +58,8 @@ export class ChannelsListComponent implements OnInit, OnChanges {
 | 
			
		||||
    )
 | 
			
		||||
    .pipe(
 | 
			
		||||
      tap((val) => {
 | 
			
		||||
        this.isLoading = true;
 | 
			
		||||
        this.loadingEvent.emit(true);
 | 
			
		||||
        if (typeof val === 'string') {
 | 
			
		||||
          this.status = val;
 | 
			
		||||
          this.page = 1;
 | 
			
		||||
@ -64,10 +68,12 @@ export class ChannelsListComponent implements OnInit, OnChanges {
 | 
			
		||||
        }
 | 
			
		||||
      }),
 | 
			
		||||
      switchMap(() => {
 | 
			
		||||
          this.channelsStatusChangedEvent.emit(this.status);
 | 
			
		||||
          return this.lightningApiService.getChannelsByNodeId$(this.publicKey, (this.page - 1) * this.itemsPerPage, this.status);
 | 
			
		||||
        this.channelsStatusChangedEvent.emit(this.status);
 | 
			
		||||
        return this.lightningApiService.getChannelsByNodeId$(this.publicKey, (this.page - 1) * this.itemsPerPage, this.status);
 | 
			
		||||
      }),
 | 
			
		||||
      map((response) => {
 | 
			
		||||
        this.isLoading = false;
 | 
			
		||||
        this.loadingEvent.emit(false);
 | 
			
		||||
        return {
 | 
			
		||||
          channels: response.body,
 | 
			
		||||
          totalItems: parseInt(response.headers.get('x-total-count'), 10)
 | 
			
		||||
 | 
			
		||||
@ -133,7 +133,7 @@
 | 
			
		||||
 | 
			
		||||
    <app-node-channels style="display:block;margin-bottom: 40px" [publicKey]="node.public_key"></app-node-channels>
 | 
			
		||||
 | 
			
		||||
    <div class="d-flex justify-content-between">
 | 
			
		||||
    <div class="d-flex">
 | 
			
		||||
      <h2 *ngIf="channelsListStatus === 'open'">
 | 
			
		||||
        <span i18n="lightning.open-channels">Open channels</span>
 | 
			
		||||
        <span> ({{ node.opened_channel_count }})</span>
 | 
			
		||||
@ -142,10 +142,13 @@
 | 
			
		||||
        <span i18n="lightning.open-channels">Closed channels</span>
 | 
			
		||||
        <span> ({{ node.closed_channel_count }})</span>
 | 
			
		||||
      </h2>
 | 
			
		||||
      <div *ngIf="channelListLoading" class="spinner-border ml-3" role="status"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <app-channels-list [publicKey]="node.public_key"
 | 
			
		||||
      (channelsStatusChangedEvent)="onChannelsListStatusChanged($event)"></app-channels-list>
 | 
			
		||||
      (channelsStatusChangedEvent)="onChannelsListStatusChanged($event)"
 | 
			
		||||
      (loadingEvent)="onLoadingEvent($event)"
 | 
			
		||||
    ></app-channels-list>
 | 
			
		||||
  </div>
 | 
			
		||||
    
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -56,4 +56,17 @@ app-fiat {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.spinner-border {
 | 
			
		||||
  @media (min-width: 768px) {
 | 
			
		||||
    margin-top: 6.5px;
 | 
			
		||||
    width: 1.75rem;
 | 
			
		||||
    height: 1.75rem;
 | 
			
		||||
  }
 | 
			
		||||
  @media (max-width: 768px) {
 | 
			
		||||
    margin-top: 2.3px;
 | 
			
		||||
    width: 1.5rem;
 | 
			
		||||
    height: 1.5rem;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -22,7 +22,7 @@ export class NodeComponent implements OnInit {
 | 
			
		||||
  channelsListStatus: string;
 | 
			
		||||
  error: Error;
 | 
			
		||||
  publicKey: string;
 | 
			
		||||
 | 
			
		||||
  channelListLoading = false;
 | 
			
		||||
  publicKeySize = 99;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
@ -97,4 +97,8 @@ export class NodeComponent implements OnInit {
 | 
			
		||||
  onChannelsListStatusChanged(e) {
 | 
			
		||||
    this.channelsListStatus = e;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onLoadingEvent(e) {
 | 
			
		||||
    this.channelListLoading = e;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -44,13 +44,13 @@ export class NodeChannels implements OnChanges {
 | 
			
		||||
        switchMap((response) => {
 | 
			
		||||
          this.isLoading = true;
 | 
			
		||||
          if ((response.body?.length ?? 0) <= 0) {
 | 
			
		||||
            return [];
 | 
			
		||||
            this.isLoading = false;
 | 
			
		||||
            return [''];
 | 
			
		||||
          }
 | 
			
		||||
          return [response.body];
 | 
			
		||||
        }),
 | 
			
		||||
        tap((body: any[]) => {
 | 
			
		||||
          if (body.length === 0) {
 | 
			
		||||
            this.isLoading = false;
 | 
			
		||||
          if (body.length === 0 || body[0].length === 0) {
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          const biggestCapacity = body[0].capacity;
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -300,7 +300,7 @@ export class NodesNetworksChartComponent implements OnInit {
 | 
			
		||||
            { offset: 1, color: '#D81B60AA' },
 | 
			
		||||
          ]),
 | 
			
		||||
 | 
			
		||||
          smooth: true,
 | 
			
		||||
          smooth: false,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          zlevel: 1,
 | 
			
		||||
@ -321,7 +321,7 @@ export class NodesNetworksChartComponent implements OnInit {
 | 
			
		||||
            { offset: 0, color: '#FFB300' },
 | 
			
		||||
            { offset: 1, color: '#FFB300AA' },
 | 
			
		||||
          ]),
 | 
			
		||||
          smooth: true,
 | 
			
		||||
          smooth: false,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          zlevel: 1,
 | 
			
		||||
@ -342,7 +342,7 @@ export class NodesNetworksChartComponent implements OnInit {
 | 
			
		||||
            { offset: 0, color: '#7D4698' },
 | 
			
		||||
            { offset: 1, color: '#7D4698AA' },
 | 
			
		||||
          ]),
 | 
			
		||||
          smooth: true,
 | 
			
		||||
          smooth: false,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      dataZoom: this.widget ? null : [{
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  @media (min-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    top: -65px;
 | 
			
		||||
    top: -100px;
 | 
			
		||||
  }
 | 
			
		||||
  @media (min-width: 830px) and (max-width: 991px) {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
@ -274,7 +274,7 @@ export class LightningStatisticsChartComponent implements OnInit {
 | 
			
		||||
              width: 1,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          smooth: true,
 | 
			
		||||
          smooth: false,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          zlevel: 0,
 | 
			
		||||
@ -288,7 +288,7 @@ export class LightningStatisticsChartComponent implements OnInit {
 | 
			
		||||
            opacity: 0.5,
 | 
			
		||||
          },
 | 
			
		||||
          type: 'line',
 | 
			
		||||
          smooth: true,
 | 
			
		||||
          smooth: false,
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      dataZoom: this.widget ? null : [{
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@
 | 
			
		||||
  <title>mempool - Bisq Markets</title>
 | 
			
		||||
  <base href="/">
 | 
			
		||||
 | 
			
		||||
  <meta name="description" content="The Mempool Open Source Project™ - our self-hosted explorer for the Bisq Network.">
 | 
			
		||||
  <meta name="description" content="The Mempool Open Source Project™ - Explore the full Bitcoin ecosystem.">
 | 
			
		||||
 | 
			
		||||
  <meta property="og:image" content="https://bisq.markets/resources/bisq/bisq-markets-preview.png" />
 | 
			
		||||
  <meta property="og:image:type" content="image/jpeg" />
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
  <meta property="twitter:site" content="https://bisq.markets/">
 | 
			
		||||
  <meta property="twitter:creator" content="@bisq_network">
 | 
			
		||||
  <meta property="twitter:title" content="The Mempool Open Source Project™">
 | 
			
		||||
  <meta property="twitter:description" content="Our self-hosted markets explorer for the Bisq community.">
 | 
			
		||||
  <meta property="twitter:description" content="Explore the full Bitcoin ecosystem with mempool.space™" />
 | 
			
		||||
  <meta property="twitter:image:src" content="https://bisq.markets/resources/bisq/bisq-markets-preview.png" />
 | 
			
		||||
  <meta property="twitter:domain" content="bisq.markets">
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@
 | 
			
		||||
  <title>mempool - Liquid Network</title>
 | 
			
		||||
  <base href="/">
 | 
			
		||||
 | 
			
		||||
  <meta name="description" content="The Mempool Open Source Project™ - our self-hosted explorer for the Liquid Network.">
 | 
			
		||||
  <meta name="description" content="The Mempool Open Source Project™ - Explore the full Bitcoin ecosystem.">
 | 
			
		||||
  <meta property="og:image" content="https://liquid.network/resources/liquid/liquid-network-preview.png" />
 | 
			
		||||
  <meta property="og:image:type" content="image/png" />
 | 
			
		||||
  <meta property="og:image:width" content="1000" />
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
  <meta property="twitter:site" content="@mempool">
 | 
			
		||||
  <meta property="twitter:creator" content="@mempool">
 | 
			
		||||
  <meta property="twitter:title" content="The Mempool Open Source Project™">
 | 
			
		||||
  <meta property="twitter:description" content="Our self-hosted network explorer for the Liquid community.">
 | 
			
		||||
  <meta property="twitter:description" content="Explore the full Bitcoin ecosystem with mempool.space™" />
 | 
			
		||||
  <meta property="twitter:image:src" content="https://liquid.network/resources/liquid/liquid-network-preview.png" />
 | 
			
		||||
  <meta property="twitter:domain" content="liquid.network">
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@
 | 
			
		||||
  <title>mempool - Bitcoin Explorer</title>
 | 
			
		||||
  <base href="/">
 | 
			
		||||
 | 
			
		||||
  <meta name="description" content="The Mempool Open Source Project™ - our self-hosted explorer for the Bitcoin community." />
 | 
			
		||||
  <meta name="description" content="The Mempool Open Source Project™ - Explore the full Bitcoin ecosystem." />
 | 
			
		||||
  <meta property="og:image" content="https://mempool.space/resources/mempool-space-preview.png" />
 | 
			
		||||
  <meta property="og:image:type" content="image/png" />
 | 
			
		||||
  <meta property="og:image:width" content="1000" />
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
  <meta property="twitter:site" content="@mempool">
 | 
			
		||||
  <meta property="twitter:creator" content="@mempool">
 | 
			
		||||
  <meta property="twitter:title" content="The Mempool Open Source Project™">
 | 
			
		||||
  <meta property="twitter:description" content="Our self-hosted mempool explorer for the Bitcoin community." />
 | 
			
		||||
  <meta property="twitter:description" content="Explore the full Bitcoin ecosystem with mempool.space™" />
 | 
			
		||||
  <meta property="twitter:image:src" content="https://mempool.space/resources/mempool-space-preview.png" />
 | 
			
		||||
  <meta property="twitter:domain" content="mempool.space">
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
@ -705,6 +705,10 @@ th {
 | 
			
		||||
.locktime { color: #ff8c00 }
 | 
			
		||||
.reserved { color: #ff8c00 }
 | 
			
		||||
 | 
			
		||||
.shortable-address {
 | 
			
		||||
  font-family: monospace;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rtl-layout {
 | 
			
		||||
 | 
			
		||||
  .navbar-brand {
 | 
			
		||||
@ -881,6 +885,7 @@ th {
 | 
			
		||||
 | 
			
		||||
  .shortable-address {
 | 
			
		||||
    direction: ltr;
 | 
			
		||||
    font-family: monospace;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .lastest-blocks-table {
 | 
			
		||||
 | 
			
		||||
@ -48,6 +48,9 @@ BITCOIN_MAINNET_ENABLE=ON
 | 
			
		||||
BITCOIN_MAINNET_MINFEE_ENABLE=ON
 | 
			
		||||
BITCOIN_TESTNET_ENABLE=ON
 | 
			
		||||
BITCOIN_SIGNET_ENABLE=ON
 | 
			
		||||
LN_BITCOIN_MAINNET_ENABLE=ON
 | 
			
		||||
LN_BITCOIN_TESTNET_ENABLE=ON
 | 
			
		||||
LN_BITCOIN_SIGNET_ENABLE=ON
 | 
			
		||||
BISQ_MAINNET_ENABLE=ON
 | 
			
		||||
ELEMENTS_LIQUID_ENABLE=ON
 | 
			
		||||
ELEMENTS_LIQUIDTESTNET_ENABLE=ON
 | 
			
		||||
@ -227,6 +230,9 @@ MYSQL_GROUP=mysql
 | 
			
		||||
MEMPOOL_MAINNET_USER='mempool'
 | 
			
		||||
MEMPOOL_TESTNET_USER='mempool_testnet'
 | 
			
		||||
MEMPOOL_SIGNET_USER='mempool_signet'
 | 
			
		||||
LN_MEMPOOL_MAINNET_USER='mempool_mainnet_lightning'
 | 
			
		||||
LN_MEMPOOL_TESTNET_USER='mempool_testnet_lightning'
 | 
			
		||||
LN_MEMPOOL_SIGNET_USER='mempool_signet_lightning'
 | 
			
		||||
MEMPOOL_LIQUID_USER='mempool_liquid'
 | 
			
		||||
MEMPOOL_LIQUIDTESTNET_USER='mempool_liquidtestnet'
 | 
			
		||||
MEMPOOL_BISQ_USER='mempool_bisq'
 | 
			
		||||
@ -234,6 +240,9 @@ MEMPOOL_BISQ_USER='mempool_bisq'
 | 
			
		||||
MEMPOOL_MAINNET_PASS=$(head -150 /dev/urandom | ${MD5} | awk '{print $1}')
 | 
			
		||||
MEMPOOL_TESTNET_PASS=$(head -150 /dev/urandom | ${MD5} | awk '{print $1}')
 | 
			
		||||
MEMPOOL_SIGNET_PASS=$(head -150 /dev/urandom | ${MD5} | awk '{print $1}')
 | 
			
		||||
LN_MEMPOOL_MAINNET_PASS=$(head -150 /dev/urandom | ${MD5} | awk '{print $1}')
 | 
			
		||||
LN_MEMPOOL_TESTNET_PASS=$(head -150 /dev/urandom | ${MD5} | awk '{print $1}')
 | 
			
		||||
LN_MEMPOOL_SIGNET_PASS=$(head -150 /dev/urandom | ${MD5} | awk '{print $1}')
 | 
			
		||||
MEMPOOL_LIQUID_PASS=$(head -150 /dev/urandom | ${MD5} | awk '{print $1}')
 | 
			
		||||
MEMPOOL_LIQUIDTESTNET_PASS=$(head -150 /dev/urandom | ${MD5} | awk '{print $1}')
 | 
			
		||||
MEMPOOL_BISQ_PASS=$(head -150 /dev/urandom | ${MD5} | awk '{print $1}')
 | 
			
		||||
@ -747,6 +756,9 @@ $CUT >$input <<-EOF
 | 
			
		||||
Tor:Enable Tor v3 HS Onion:ON
 | 
			
		||||
Mainnet:Enable Bitcoin Mainnet:ON
 | 
			
		||||
Mainnet-Minfee:Enable Bitcoin Mainnet Minfee:ON
 | 
			
		||||
LN-Mainnet:Enable Bitcoin Mainnet Lightning:ON
 | 
			
		||||
LN-Testnet:Enable Bitcoin Testnet Lightning:ON
 | 
			
		||||
LN-Signet:Enable Bitcoin Signet Lightning:ON
 | 
			
		||||
Testnet:Enable Bitcoin Testnet:ON
 | 
			
		||||
Signet:Enable Bitcoin Signet:ON
 | 
			
		||||
Liquid:Enable Elements Liquid:ON
 | 
			
		||||
@ -809,6 +821,24 @@ else
 | 
			
		||||
    BITCOIN_INSTALL=OFF
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if grep LN-Mainnet $tempfile >/dev/null 2>&1;then
 | 
			
		||||
    LN_BITCOIN_MAINNET_ENABLE=ON
 | 
			
		||||
else
 | 
			
		||||
    LN_BITCOIN_MAINNET_ENABLE=OFF
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if grep LN-Testnet $tempfile >/dev/null 2>&1;then
 | 
			
		||||
    LN_BITCOIN_TESTNET_ENABLE=ON
 | 
			
		||||
else
 | 
			
		||||
    LN_BITCOIN_TESTNET_ENABLE=OFF
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if grep LN-Signet $tempfile >/dev/null 2>&1;then
 | 
			
		||||
    LN_BITCOIN_SIGNET_ENABLE=ON
 | 
			
		||||
else
 | 
			
		||||
    LN_BITCOIN_SIGNET_ENABLE=OFF
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if grep Liquid $tempfile >/dev/null 2>&1;then
 | 
			
		||||
    ELEMENTS_LIQUID_ENABLE=ON
 | 
			
		||||
else
 | 
			
		||||
@ -831,6 +861,7 @@ if grep CoreLN $tempfile >/dev/null 2>&1;then
 | 
			
		||||
    CLN_INSTALL=ON
 | 
			
		||||
else
 | 
			
		||||
    CLN_INSTALL=OFF
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${BITCOIN_MAINNET_ENABLE}" = ON -o "${BITCOIN_TESTNET_ENABLE}" = ON -o "${BITCOIN_SIGNET_ENABLE}" = ON ];then
 | 
			
		||||
    BITCOIN_ELECTRS_INSTALL=ON
 | 
			
		||||
@ -1279,8 +1310,11 @@ case $OS in
 | 
			
		||||
        echo "[*] Creating Core Lightning user"
 | 
			
		||||
        osGroupCreate "${CLN_GROUP}"
 | 
			
		||||
        osUserCreate "${CLN_USER}" "${CLN_HOME}" "${CLN_GROUP}"
 | 
			
		||||
        osSudo "${ROOT_USER}" pw usermod ${MEMPOOL_USER} -G "${CLN_GROUP}"
 | 
			
		||||
        osSudo "${ROOT_USER}" chsh -s `which zsh` "${CLN_USER}"
 | 
			
		||||
        echo "export PATH=$PATH:$HOME/.local/bin" >> "${CLN_HOME}/.zshrc"
 | 
			
		||||
        osSudo "${ROOT_USER}" mkdir -p "${CLN_HOME}/.lightning/{bitcoin,signet,testnet}"
 | 
			
		||||
        osSudo "${ROOT_USER}" chmod 750 "${CLN_HOME}" "${CLN_HOME}/.lightning" "${CLN_HOME}/.lightning/{bitcoin,signet,testnet}"
 | 
			
		||||
        osSudo "${ROOT_USER}" chown -R "${CLN_USER}:${CLN_GROUP}" "${CLN_HOME}"
 | 
			
		||||
 | 
			
		||||
        echo "[*] Installing Core Lightning package"
 | 
			
		||||
@ -1671,7 +1705,16 @@ if [ "${BITCOIN_MAINNET_ENABLE}" = ON -o "${BITCOIN_TESTNET_ENABLE}" = ON -o "${
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/mainnet"
 | 
			
		||||
 | 
			
		||||
    echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Bitcoin Mainnet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME} && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/mainnet && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${LN_BITCOIN_MAINNET_ENABLE}" = ON ];then
 | 
			
		||||
    echo "[*] Creating Mempool instance for Lightning Network on Bitcoin Mainnet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git config --global advice.detachedHead false
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/mainnet-lightning"
 | 
			
		||||
 | 
			
		||||
    echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Lightning Network on Bitcoin Mainnet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/mainnet-lightning && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${BITCOIN_TESTNET_ENABLE}" = ON ];then
 | 
			
		||||
@ -1680,7 +1723,16 @@ if [ "${BITCOIN_TESTNET_ENABLE}" = ON ];then
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/testnet"
 | 
			
		||||
 | 
			
		||||
    echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Bitcoin Testnet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME} && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/testnet && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${LN_BITCOIN_TESTNET_ENABLE}" = ON ];then
 | 
			
		||||
    echo "[*] Creating Mempool instance for Lightning Network on Bitcoin Testnet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git config --global advice.detachedHead false
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/testnet-lightning"
 | 
			
		||||
 | 
			
		||||
    echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Lightning Network on Bitcoin Testnet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/testnet-lightning && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${BITCOIN_SIGNET_ENABLE}" = ON ];then
 | 
			
		||||
@ -1689,7 +1741,16 @@ if [ "${BITCOIN_SIGNET_ENABLE}" = ON ];then
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/signet"
 | 
			
		||||
 | 
			
		||||
    echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Bitcoin Signet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME} && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/signet && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${LN_BITCOIN_SIGNET_ENABLE}" = ON ];then
 | 
			
		||||
    echo "[*] Creating Mempool instance for Lightning Network on Bitcoin Signet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git config --global advice.detachedHead false
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/signet-lightning"
 | 
			
		||||
 | 
			
		||||
    echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Lightning Network on Bitcoin Signet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/signet-lightning && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${ELEMENTS_LIQUID_ENABLE}" = ON ];then
 | 
			
		||||
@ -1698,7 +1759,7 @@ if [ "${ELEMENTS_LIQUID_ENABLE}" = ON ];then
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/liquid"
 | 
			
		||||
 | 
			
		||||
    echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Liquid"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME} && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/liquid && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${ELEMENTS_LIQUIDTESTNET_ENABLE}" = ON ];then
 | 
			
		||||
@ -1707,7 +1768,7 @@ if [ "${ELEMENTS_LIQUIDTESTNET_ENABLE}" = ON ];then
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/liquidtestnet"
 | 
			
		||||
 | 
			
		||||
    echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Liquid Testnet"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME} && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/liquidtestnet && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${BISQ_INSTALL}" = ON ];then
 | 
			
		||||
@ -1716,7 +1777,7 @@ if [ "${BISQ_INSTALL}" = ON ];then
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" git clone --branch "${MEMPOOL_REPO_BRANCH}" "${MEMPOOL_REPO_URL}" "${MEMPOOL_HOME}/bisq"
 | 
			
		||||
 | 
			
		||||
    echo "[*] Checking out Mempool ${MEMPOOL_LATEST_RELEASE} for Bisq"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME} && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
    osSudo "${MEMPOOL_USER}" sh -c "cd ${MEMPOOL_HOME}/bisq && git checkout ${MEMPOOL_LATEST_RELEASE}"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
##### mariadb
 | 
			
		||||
@ -1742,6 +1803,15 @@ grant all on mempool_testnet.* to '${MEMPOOL_TESTNET_USER}'@'localhost' identifi
 | 
			
		||||
create database mempool_signet;
 | 
			
		||||
grant all on mempool_signet.* to '${MEMPOOL_SIGNET_USER}'@'localhost' identified by '${MEMPOOL_SIGNET_PASS}';
 | 
			
		||||
 | 
			
		||||
create database mempool_mainnet_lightning;
 | 
			
		||||
grant all on mempool_mainnet_lightning.* to '${LN_MEMPOOL_MAINNET_USER}'@'%' identified by '${LN_MEMPOOL_MAINNET_PASS}';
 | 
			
		||||
 | 
			
		||||
create database mempool_testnet_lightning;
 | 
			
		||||
grant all on mempool_testnet_lightning.* to '${LN_MEMPOOL_TESTNET_USER}'@'%' identified by '${LN_MEMPOOL_TESTNET_PASS}';
 | 
			
		||||
 | 
			
		||||
create database mempool_signet_lightning;
 | 
			
		||||
grant all on mempool_signet_lightning.* to '${LN_MEMPOOL_SIGNET_USER}'@'%' identified by '${LN_MEMPOOL_SIGNET_PASS}';
 | 
			
		||||
 | 
			
		||||
create database mempool_liquid;
 | 
			
		||||
grant all on mempool_liquid.* to '${MEMPOOL_LIQUID_USER}'@'localhost' identified by '${MEMPOOL_LIQUID_PASS}';
 | 
			
		||||
 | 
			
		||||
@ -1760,6 +1830,12 @@ declare -x MEMPOOL_TESTNET_USER="${MEMPOOL_TESTNET_USER}"
 | 
			
		||||
declare -x MEMPOOL_TESTNET_PASS="${MEMPOOL_TESTNET_PASS}"
 | 
			
		||||
declare -x MEMPOOL_SIGNET_USER="${MEMPOOL_SIGNET_USER}"
 | 
			
		||||
declare -x MEMPOOL_SIGNET_PASS="${MEMPOOL_SIGNET_PASS}"
 | 
			
		||||
declare -x LN_MEMPOOL_MAINNET_USER="${LN_MEMPOOL_MAINNET_USER}"
 | 
			
		||||
declare -x LN_MEMPOOL_MAINNET_PASS="${LN_MEMPOOL_MAINNET_PASS}"
 | 
			
		||||
declare -x LN_MEMPOOL_TESTNET_USER="${LN_MEMPOOL_TESTNET_USER}"
 | 
			
		||||
declare -x LN_MEMPOOL_TESTNET_PASS="${LN_MEMPOOL_TESTNET_PASS}"
 | 
			
		||||
declare -x LN_MEMPOOL_SIGNET_USER="${LN_MEMPOOL_SIGNET_USER}"
 | 
			
		||||
declare -x LN_MEMPOOL_SIGNET_PASS="${LN_MEMPOOL_SIGNET_PASS}"
 | 
			
		||||
declare -x MEMPOOL_LIQUID_USER="${MEMPOOL_LIQUID_USER}"
 | 
			
		||||
declare -x MEMPOOL_LIQUID_PASS="${MEMPOOL_LIQUID_PASS}"
 | 
			
		||||
declare -x MEMPOOL_LIQUIDTESTNET_USER="${MEMPOOL_LIQUIDTESTNET_USER}"
 | 
			
		||||
@ -1770,24 +1846,32 @@ _EOF_
 | 
			
		||||
 | 
			
		||||
##### nginx
 | 
			
		||||
 | 
			
		||||
echo "[*] Adding Nginx configuration"
 | 
			
		||||
osSudo "${ROOT_USER}" install -c -o "${ROOT_USER}" -g "${ROOT_GROUP}" -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/nginx/nginx.conf" "${NGINX_CONFIGURATION}"
 | 
			
		||||
mkdir -p /var/cache/nginx/services /var/cache/nginx/api
 | 
			
		||||
chown ${NGINX_USER}: /var/cache/nginx/services /var/cache/nginx/api
 | 
			
		||||
ln -s /mempool/mempool /etc/nginx/mempool
 | 
			
		||||
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_USER__!${NGINX_USER}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_ETC_FOLDER__!${NGINX_ETC_FOLDER}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
if [ "${TOR_INSTALL}" = ON ];then
 | 
			
		||||
echo "[*] Read tor v3 onion hostnames"
 | 
			
		||||
    NGINX_MEMPOOL_ONION=$(cat "${TOR_RESOURCES}/mempool/hostname")
 | 
			
		||||
    NGINX_BISQ_ONION=$(cat "${TOR_RESOURCES}/bisq/hostname")
 | 
			
		||||
    NGINX_LIQUID_ONION=$(cat "${TOR_RESOURCES}/liquid/hostname")
 | 
			
		||||
    osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_MEMPOOL_ONION__!${NGINX_MEMPOOL_ONION%.onion}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
    osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_BISQ_ONION__!${NGINX_BISQ_ONION%.onion}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
    osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_LIQUID_ONION__!${NGINX_LIQUID_ONIONi%.onion}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
fi
 | 
			
		||||
echo "[*] Restarting Nginx"
 | 
			
		||||
osSudo "${ROOT_USER}" service nginx restart
 | 
			
		||||
case $OS in
 | 
			
		||||
 | 
			
		||||
    FreeBSD)
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
Debian)
 | 
			
		||||
        echo "[*] Adding Nginx configuration"
 | 
			
		||||
        osSudo "${ROOT_USER}" install -c -o "${ROOT_USER}" -g "${ROOT_GROUP}" -m 644 "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/nginx/nginx.conf" "${NGINX_CONFIGURATION}"
 | 
			
		||||
        mkdir -p /var/cache/nginx/services /var/cache/nginx/api
 | 
			
		||||
        chown ${NGINX_USER}: /var/cache/nginx/services /var/cache/nginx/api
 | 
			
		||||
        ln -s /mempool/mempool /etc/nginx/mempool
 | 
			
		||||
        osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_USER__!${NGINX_USER}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
        osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_ETC_FOLDER__!${NGINX_ETC_FOLDER}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
        if [ "${TOR_INSTALL}" = ON ];then
 | 
			
		||||
        echo "[*] Read tor v3 onion hostnames"
 | 
			
		||||
            NGINX_MEMPOOL_ONION=$(cat "${TOR_RESOURCES}/mempool/hostname")
 | 
			
		||||
            NGINX_BISQ_ONION=$(cat "${TOR_RESOURCES}/bisq/hostname")
 | 
			
		||||
            NGINX_LIQUID_ONION=$(cat "${TOR_RESOURCES}/liquid/hostname")
 | 
			
		||||
            osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_MEMPOOL_ONION__!${NGINX_MEMPOOL_ONION%.onion}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
            osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_BISQ_ONION__!${NGINX_BISQ_ONION%.onion}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
            osSudo "${ROOT_USER}" sed -i.orig "s!__NGINX_LIQUID_ONION__!${NGINX_LIQUID_ONIONi%.onion}!" "${NGINX_CONFIGURATION}"
 | 
			
		||||
        fi
 | 
			
		||||
        echo "[*] Restarting Nginx"
 | 
			
		||||
        osSudo "${ROOT_USER}" service nginx restart
 | 
			
		||||
    ;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
##### OS systemd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -98,6 +98,12 @@ build_backend()
 | 
			
		||||
        -e "s!__MEMPOOL_TESTNET_PASS__!${MEMPOOL_TESTNET_PASS}!" \
 | 
			
		||||
        -e "s!__MEMPOOL_SIGNET_USER__!${MEMPOOL_SIGNET_USER}!" \
 | 
			
		||||
        -e "s!__MEMPOOL_SIGNET_PASS__!${MEMPOOL_SIGNET_PASS}!" \
 | 
			
		||||
        -e "s!__LN_MEMPOOL_MAINNET_USER__!${LN_MEMPOOL_MAINNET_USER}!" \
 | 
			
		||||
        -e "s!__LN_MEMPOOL_MAINNET_PASS__!${LN_MEMPOOL_MAINNET_PASS}!" \
 | 
			
		||||
        -e "s!__LN_MEMPOOL_TESTNET_USER__!${LN_MEMPOOL_TESTNET_USER}!" \
 | 
			
		||||
        -e "s!__LN_MEMPOOL_TESTNET_PASS__!${LN_MEMPOOL_TESTNET_PASS}!" \
 | 
			
		||||
        -e "s!__LN_MEMPOOL_SIGNET_USER__!${LN_MEMPOOL_SIGNET_USER}!" \
 | 
			
		||||
        -e "s!__LN_MEMPOOL_SIGNET_PASS__!${LN_MEMPOOL_SIGNET_PASS}!" \
 | 
			
		||||
        -e "s!__MEMPOOL_LIQUID_USER__!${MEMPOOL_LIQUID_USER}!" \
 | 
			
		||||
        -e "s!__MEMPOOL_LIQUID_PASS__!${MEMPOOL_LIQUID_PASS}!" \
 | 
			
		||||
        -e "s!__MEMPOOL_LIQUIDTESTNET_USER__!${LIQUIDTESTNET_USER}!" \
 | 
			
		||||
@ -145,7 +151,7 @@ for repo in $backend_repos;do
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# build unfurlers
 | 
			
		||||
for repo in mainnet liquid;do
 | 
			
		||||
for repo in mainnet liquid bisq;do
 | 
			
		||||
    build_unfurler "${repo}"
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,21 @@
 | 
			
		||||
#!/usr/bin/env zsh
 | 
			
		||||
 | 
			
		||||
# kill "while true" loops
 | 
			
		||||
killall sh
 | 
			
		||||
 | 
			
		||||
# kill actual node backends
 | 
			
		||||
killall node
 | 
			
		||||
 | 
			
		||||
# kill unfurler chrome instances
 | 
			
		||||
killall chrome
 | 
			
		||||
 | 
			
		||||
# kill xorg
 | 
			
		||||
killall xinit
 | 
			
		||||
 | 
			
		||||
# kill nginx cache warmer scripts
 | 
			
		||||
for pid in `ps uaxww|grep warmer|grep zsh|awk '{print $2}'`;do
 | 
			
		||||
    kill $pid
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# always exit successfully despite above errors
 | 
			
		||||
exit 0
 | 
			
		||||
 | 
			
		||||
@ -9,22 +9,24 @@ for site in mainnet mainnet-lightning testnet testnet-lightning signet signet-li
 | 
			
		||||
    screen -dmS "${site}" sh -c 'while true;do npm run start-production;sleep 1;done'
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# only start unfurler if GPU present
 | 
			
		||||
# only start xorg if GPU present
 | 
			
		||||
if pciconf -lv|grep -i nvidia >/dev/null 2>&1;then
 | 
			
		||||
    export DISPLAY=:0
 | 
			
		||||
    screen -dmS x startx
 | 
			
		||||
    sleep 3
 | 
			
		||||
    for site in mainnet liquid;do
 | 
			
		||||
        cd "$HOME/${site}/unfurler" && \
 | 
			
		||||
        echo "starting mempool unfurler: ${site}" && \
 | 
			
		||||
        screen -dmS "unfurler-${site}" sh -c 'while true;do npm run unfurler;sleep 2;done'
 | 
			
		||||
    done
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# start unfurlers for each frontend
 | 
			
		||||
for site in mainnet liquid bisq;do
 | 
			
		||||
    cd "$HOME/${site}/unfurler" && \
 | 
			
		||||
    echo "starting mempool unfurler: ${site}" && \
 | 
			
		||||
    screen -dmS "unfurler-${site}" sh -c 'while true;do npm run unfurler;sleep 2;done'
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# start nginx warm cacher
 | 
			
		||||
for site in mainnet;do
 | 
			
		||||
    echo "starting mempool cache warmer: ${site}"
 | 
			
		||||
    screen -dmS "warmer-${site}" $HOME/mempool/production/nginx-cache-warmer
 | 
			
		||||
    screen -dmS "warmer-${site}" $HOME/mainnet/production/nginx-cache-warmer
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
exit 0
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,6 @@
 | 
			
		||||
# start on reboot
 | 
			
		||||
@reboot sleep 10 ; $HOME/start
 | 
			
		||||
 | 
			
		||||
# start cache warmer on reboot
 | 
			
		||||
@reboot sleep 180 ; /mempool/mempool/production/nginx-cache-warmer >/dev/null 2>&1 &
 | 
			
		||||
 | 
			
		||||
# daily backup
 | 
			
		||||
37 13 * * * sleep 30 ; /mempool/mempool.space/backup >/dev/null 2>&1 &
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -85,6 +85,38 @@ do for url in / \
 | 
			
		||||
	'/api/v1/mining/difficulty-adjustments/all' \
 | 
			
		||||
	'/api/v1/lightning/channels-geo?style=widget' \
 | 
			
		||||
	'/api/v1/lightning/channels-geo?style=graph' \
 | 
			
		||||
	'/api/v1/lightning/statistics/latest' \
 | 
			
		||||
	'/api/v1/lightning/statistics/1m' \
 | 
			
		||||
	'/api/v1/lightning/statistics/3m' \
 | 
			
		||||
	'/api/v1/lightning/statistics/6m' \
 | 
			
		||||
	'/api/v1/lightning/statistics/1y' \
 | 
			
		||||
	'/api/v1/lightning/statistics/2y' \
 | 
			
		||||
	'/api/v1/lightning/statistics/3y' \
 | 
			
		||||
	'/api/v1/lightning/statistics/all' \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp-ranking' \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/396982,15169' `# Google` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/14618,16509' `# Amazon` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/39572' `# DataWeb` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/14061' `# Digital Ocean` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/24940,213230' `# Hetzner` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/174' `# LunaNode` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/45102' `# Alibaba` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/3209' `# Vodafone Germany` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/7922' `# Comcast Cable` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/34197' `# SHRD SARL` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/42275' `# Three Fourteen SASU` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/16276' `# OVH SAS` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/11426,11427,20001,20115,11351,10796,33363,12271' `# Spectrum` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/701' `# Verizon` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/12876' `# Scaleway` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/33915' `# Ziggo` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/3320' `# Deutsche Telekom AG` \
 | 
			
		||||
	'/api/v1/lightning/nodes/isp/8075' `# Microsoft Azure` \
 | 
			
		||||
	'/api/v1/lightning/nodes/countries' \
 | 
			
		||||
	'/api/v1/lightning/nodes/rankings' \
 | 
			
		||||
	'/api/v1/lightning/nodes/rankings/liquidity' \
 | 
			
		||||
	'/api/v1/lightning/nodes/rankings/connectivity' \
 | 
			
		||||
	'/api/v1/lightning/nodes/rankings/age' \
 | 
			
		||||
 | 
			
		||||
	do
 | 
			
		||||
		warm "https://${hostname}${url}"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								production/unfurler-config.bisq.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								production/unfurler-config.bisq.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
{
 | 
			
		||||
  "SERVER": {
 | 
			
		||||
    "HOST": "https://bisq.fra.mempool.space",
 | 
			
		||||
    "HTTP_PORT": 8002
 | 
			
		||||
  },
 | 
			
		||||
  "MEMPOOL": {
 | 
			
		||||
    "HTTP_HOST": "http://127.0.0.1",
 | 
			
		||||
    "HTTP_PORT": 82,
 | 
			
		||||
    "NETWORK": "bisq"
 | 
			
		||||
  },
 | 
			
		||||
  "PUPPETEER": {
 | 
			
		||||
    "CLUSTER_SIZE": 8,
 | 
			
		||||
    "EXEC_PATH": "/usr/local/bin/chrome",
 | 
			
		||||
    "MAX_PAGE_AGE": 86400,
 | 
			
		||||
    "RENDER_TIMEOUT": 3000
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "SERVER": {
 | 
			
		||||
    "HOST": "https://liquid.network",
 | 
			
		||||
    "HTTP_PORT": 8002
 | 
			
		||||
    "HOST": "https://liquid.fra.mempool.space",
 | 
			
		||||
    "HTTP_PORT": 8003
 | 
			
		||||
  },
 | 
			
		||||
  "MEMPOOL": {
 | 
			
		||||
    "HTTP_HOST": "https://liquid.network",
 | 
			
		||||
    "HTTP_PORT": 443,
 | 
			
		||||
    "NETWORK": "liquid"
 | 
			
		||||
    "HTTP_HOST": "http://127.0.0.1",
 | 
			
		||||
    "HTTP_PORT": 83,
 | 
			
		||||
    "NETWORK": "bitcoin"
 | 
			
		||||
  },
 | 
			
		||||
  "PUPPETEER": {
 | 
			
		||||
    "CLUSTER_SIZE": 8,
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
{
 | 
			
		||||
  "SERVER": {
 | 
			
		||||
    "HOST": "https://mempool.space",
 | 
			
		||||
    "HOST": "https://mempool.fra.mempool.space",
 | 
			
		||||
    "HTTP_PORT": 8001
 | 
			
		||||
  },
 | 
			
		||||
  "MEMPOOL": {
 | 
			
		||||
    "HTTP_HOST": "https://mempool.space",
 | 
			
		||||
    "HTTP_PORT": 443,
 | 
			
		||||
    "HTTP_HOST": "http://127.0.0.1",
 | 
			
		||||
    "HTTP_PORT": 81,
 | 
			
		||||
    "NETWORK": "bitcoin"
 | 
			
		||||
  },
 | 
			
		||||
  "PUPPETEER": {
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@
 | 
			
		||||
    "NETWORK": "bitcoin" // "bitcoin" | "liquid" | "bisq" (optional - defaults to "bitcoin")
 | 
			
		||||
  },
 | 
			
		||||
  "PUPPETEER": {
 | 
			
		||||
    "DISABLE": false, // optional, boolean, disables puppeteer and /render endpoints
 | 
			
		||||
    "CLUSTER_SIZE": 2,
 | 
			
		||||
    "EXEC_PATH": "/usr/local/bin/chrome", // optional
 | 
			
		||||
    "MAX_PAGE_AGE": 86400, // maximum lifetime of a page session (in seconds)
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ interface IConfig {
 | 
			
		||||
    NETWORK?: string;
 | 
			
		||||
  };
 | 
			
		||||
  PUPPETEER: {
 | 
			
		||||
    DISABLE: boolean;
 | 
			
		||||
    CLUSTER_SIZE: number;
 | 
			
		||||
    EXEC_PATH?: string;
 | 
			
		||||
    MAX_PAGE_AGE?: number;
 | 
			
		||||
@ -28,6 +29,7 @@ const defaults: IConfig = {
 | 
			
		||||
    'HTTP_PORT': 4200,
 | 
			
		||||
  },
 | 
			
		||||
  'PUPPETEER': {
 | 
			
		||||
    'DISABLE': false,
 | 
			
		||||
    'CLUSTER_SIZE': 1,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -39,12 +39,14 @@ class Server {
 | 
			
		||||
      .use(express.text())
 | 
			
		||||
      ;
 | 
			
		||||
 | 
			
		||||
    this.cluster = await Cluster.launch({
 | 
			
		||||
        concurrency: ReusablePage,
 | 
			
		||||
        maxConcurrency: config.PUPPETEER.CLUSTER_SIZE,
 | 
			
		||||
        puppeteerOptions: puppeteerConfig,
 | 
			
		||||
    });
 | 
			
		||||
    await this.cluster?.task(async (args) => { return this.clusterTask(args) });
 | 
			
		||||
    if (!config.PUPPETEER.DISABLE) {
 | 
			
		||||
      this.cluster = await Cluster.launch({
 | 
			
		||||
          concurrency: ReusablePage,
 | 
			
		||||
          maxConcurrency: config.PUPPETEER.CLUSTER_SIZE,
 | 
			
		||||
          puppeteerOptions: puppeteerConfig,
 | 
			
		||||
      });
 | 
			
		||||
      await this.cluster?.task(async (args) => { return this.clusterTask(args) });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.setUpRoutes();
 | 
			
		||||
 | 
			
		||||
@ -66,7 +68,11 @@ class Server {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setUpRoutes() {
 | 
			
		||||
    this.app.get('/render*', async (req, res) => { return this.renderPreview(req, res) })
 | 
			
		||||
    if (!config.PUPPETEER.DISABLE) {
 | 
			
		||||
      this.app.get('/render*', async (req, res) => { return this.renderPreview(req, res) })
 | 
			
		||||
    } else {
 | 
			
		||||
      this.app.get('/render*', async (req, res) => { return this.renderDisabled(req, res) })
 | 
			
		||||
    }
 | 
			
		||||
    this.app.get('*', (req, res) => { return this.renderHTML(req, res) })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -113,6 +119,10 @@ class Server {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async renderDisabled(req, res) {
 | 
			
		||||
    res.status(500).send("preview rendering disabled");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async renderPreview(req, res) {
 | 
			
		||||
    try {
 | 
			
		||||
      const rawPath = req.params[0];
 | 
			
		||||
@ -169,7 +179,7 @@ class Server {
 | 
			
		||||
      <head>
 | 
			
		||||
        <meta charset="utf-8">
 | 
			
		||||
        <title>${ogTitle}</title>
 | 
			
		||||
        <meta name="description" content="The Mempool Open Source Project™ - our self-hosted explorer for the ${capitalize(this.network)} community."/>
 | 
			
		||||
        <meta name="description" content="The Mempool Open Source Project™ - Explore the full Bitcoin ecosystem with mempool.space™"/>
 | 
			
		||||
        <meta property="og:image" content="${ogImageUrl}"/>
 | 
			
		||||
        <meta property="og:image:type" content="image/png"/>
 | 
			
		||||
        <meta property="og:image:width" content="${matchedRoute.render ? 1200 : 1000}"/>
 | 
			
		||||
@ -179,7 +189,7 @@ class Server {
 | 
			
		||||
        <meta property="twitter:site" content="@mempool">
 | 
			
		||||
        <meta property="twitter:creator" content="@mempool">
 | 
			
		||||
        <meta property="twitter:title" content="${ogTitle}">
 | 
			
		||||
        <meta property="twitter:description" content="Our self-hosted mempool explorer for the ${capitalize(this.network)} community."/>
 | 
			
		||||
        <meta property="twitter:description" content="Explore the full Bitcoin ecosystem with mempool.space"/>
 | 
			
		||||
        <meta property="twitter:image:src" content="${ogImageUrl}"/>
 | 
			
		||||
        <meta property="twitter:domain" content="mempool.space">
 | 
			
		||||
      <body></body>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user