Node and channel API
This commit is contained in:
		
							parent
							
								
									8d622e3606
								
							
						
					
					
						commit
						f5325b3a6d
					
				@ -12,6 +12,14 @@ export class LightningApiService {
 | 
				
			|||||||
    private httpClient: HttpClient,
 | 
					    private httpClient: HttpClient,
 | 
				
			||||||
  ) { }
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getNode$(publicKey: string): Observable<any> {
 | 
				
			||||||
 | 
					    return this.httpClient.get<any>(API_BASE_URL + '/nodes/' + publicKey);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getChannelsByNodeId$(publicKey: string): Observable<any> {
 | 
				
			||||||
 | 
					    return this.httpClient.get<any>(API_BASE_URL + '/channels/' + publicKey);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getLatestStatistics$(): Observable<any> {
 | 
					  getLatestStatistics$(): Observable<any> {
 | 
				
			||||||
    return this.httpClient.get<any>(API_BASE_URL + '/statistics/latest');
 | 
					    return this.httpClient.get<any>(API_BASE_URL + '/statistics/latest');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,16 +6,20 @@ import { LightningApiService } from './lightning-api.service';
 | 
				
			|||||||
import { NodesListComponent } from './nodes-list/nodes-list.component';
 | 
					import { NodesListComponent } from './nodes-list/nodes-list.component';
 | 
				
			||||||
import { RouterModule } from '@angular/router';
 | 
					import { RouterModule } from '@angular/router';
 | 
				
			||||||
import { NodeStatisticsComponent } from './node-statistics/node-statistics.component';
 | 
					import { NodeStatisticsComponent } from './node-statistics/node-statistics.component';
 | 
				
			||||||
 | 
					import { NodeComponent } from './node/node.component';
 | 
				
			||||||
 | 
					import { LightningRoutingModule } from './lightning.routing.module';
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
  declarations: [
 | 
					  declarations: [
 | 
				
			||||||
    LightningDashboardComponent,
 | 
					    LightningDashboardComponent,
 | 
				
			||||||
    NodesListComponent,
 | 
					    NodesListComponent,
 | 
				
			||||||
    NodeStatisticsComponent,
 | 
					    NodeStatisticsComponent,
 | 
				
			||||||
 | 
					    NodeComponent,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  imports: [
 | 
					  imports: [
 | 
				
			||||||
    CommonModule,
 | 
					    CommonModule,
 | 
				
			||||||
    SharedModule,
 | 
					    SharedModule,
 | 
				
			||||||
    RouterModule,
 | 
					    RouterModule,
 | 
				
			||||||
 | 
					    LightningRoutingModule,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  providers: [
 | 
					  providers: [
 | 
				
			||||||
    LightningApiService,
 | 
					    LightningApiService,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										25
									
								
								frontend/src/app/lightning/lightning.routing.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								frontend/src/app/lightning/lightning.routing.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import { NgModule } from '@angular/core';
 | 
				
			||||||
 | 
					import { RouterModule, Routes } from '@angular/router';
 | 
				
			||||||
 | 
					import { LightningDashboardComponent } from './lightning-dashboard/lightning-dashboard.component';
 | 
				
			||||||
 | 
					import { NodeComponent } from './node/node.component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes: Routes = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      path: '',
 | 
				
			||||||
 | 
					      component: LightningDashboardComponent,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      path: 'node/:public_key',
 | 
				
			||||||
 | 
					      component: NodeComponent,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      path: '**',
 | 
				
			||||||
 | 
					      redirectTo: ''
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					  imports: [RouterModule.forChild(routes)],
 | 
				
			||||||
 | 
					  exports: [RouterModule]
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class LightningRoutingModule { }
 | 
				
			||||||
@ -1,5 +1,15 @@
 | 
				
			|||||||
<div class="fee-estimation-wrapper" *ngIf="statistics$ | async as statistics; else loadingReward">
 | 
					<div class="fee-estimation-wrapper" *ngIf="statistics$ | async as statistics; else loadingReward">
 | 
				
			||||||
  <div class="fee-estimation-container">
 | 
					  <div class="fee-estimation-container">
 | 
				
			||||||
 | 
					    <div class="item">
 | 
				
			||||||
 | 
					      <h5 class="card-title" i18n="mining.average-fee">Capacity</h5>
 | 
				
			||||||
 | 
					      <div class="card-text" i18n-ngbTooltip="mining.average-fee"
 | 
				
			||||||
 | 
					        ngbTooltip="Fee paid on average for each transaction in the past 144 blocks" placement="bottom">
 | 
				
			||||||
 | 
					        <app-amount [satoshis]="statistics.latest.total_capacity" digitsInfo="1.2-3"></app-amount>
 | 
				
			||||||
 | 
					        <span class="fiat">
 | 
				
			||||||
 | 
					          <app-change [current]="statistics.latest.total_capacity" [previous]="statistics.previous.total_capacity"></app-change>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
    <div class="item">
 | 
					    <div class="item">
 | 
				
			||||||
      <h5 class="card-title" i18n="mining.rewards">Nodes</h5>
 | 
					      <h5 class="card-title" i18n="mining.rewards">Nodes</h5>
 | 
				
			||||||
      <div class="card-text" i18n-ngbTooltip="mining.rewards-desc"
 | 
					      <div class="card-text" i18n-ngbTooltip="mining.rewards-desc"
 | 
				
			||||||
@ -24,6 +34,7 @@
 | 
				
			|||||||
        </span>
 | 
					        </span>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    <!--
 | 
				
			||||||
    <div class="item">
 | 
					    <div class="item">
 | 
				
			||||||
      <h5 class="card-title" i18n="mining.average-fee">Average Channel</h5>
 | 
					      <h5 class="card-title" i18n="mining.average-fee">Average Channel</h5>
 | 
				
			||||||
      <div class="card-text" i18n-ngbTooltip="mining.average-fee"
 | 
					      <div class="card-text" i18n-ngbTooltip="mining.average-fee"
 | 
				
			||||||
@ -34,6 +45,7 @@
 | 
				
			|||||||
        </span>
 | 
					        </span>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    -->
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								frontend/src/app/lightning/node/node.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								frontend/src/app/lightning/node/node.component.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					<p>node works!</p>
 | 
				
			||||||
							
								
								
									
										0
									
								
								frontend/src/app/lightning/node/node.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								frontend/src/app/lightning/node/node.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										29
									
								
								frontend/src/app/lightning/node/node.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								frontend/src/app/lightning/node/node.component.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					import { Component, OnInit } from '@angular/core';
 | 
				
			||||||
 | 
					import { ActivatedRoute, ParamMap } from '@angular/router';
 | 
				
			||||||
 | 
					import { Observable } from 'rxjs';
 | 
				
			||||||
 | 
					import { switchMap } from 'rxjs/operators';
 | 
				
			||||||
 | 
					import { LightningApiService } from '../lightning-api.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					  selector: 'app-node',
 | 
				
			||||||
 | 
					  templateUrl: './node.component.html',
 | 
				
			||||||
 | 
					  styleUrls: ['./node.component.scss']
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class NodeComponent implements OnInit {
 | 
				
			||||||
 | 
					  node$: Observable<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private lightningApiService: LightningApiService,
 | 
				
			||||||
 | 
					    private activatedRoute: ActivatedRoute,
 | 
				
			||||||
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngOnInit(): void {
 | 
				
			||||||
 | 
					    this.node$ = this.activatedRoute.paramMap
 | 
				
			||||||
 | 
					      .pipe(
 | 
				
			||||||
 | 
					        switchMap((params: ParamMap) => {
 | 
				
			||||||
 | 
					          return this.lightningApiService.getNode$(params.get('id'));
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -9,7 +9,7 @@
 | 
				
			|||||||
    <tbody *ngIf="nodes$ | async as nodes; else skeleton">
 | 
					    <tbody *ngIf="nodes$ | async as nodes; else skeleton">
 | 
				
			||||||
      <tr *ngFor="let node of nodes; let i = index;">
 | 
					      <tr *ngFor="let node of nodes; let i = index;">
 | 
				
			||||||
        <td class="alias text-left">
 | 
					        <td class="alias text-left">
 | 
				
			||||||
          <a [routerLink]="['/lightning/nodes/:public_key' | relativeUrl, node.public]">{{ node.alias }}</a>
 | 
					          <a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">{{ node.alias }}</a>
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td class="capacity text-right">
 | 
					        <td class="capacity text-right">
 | 
				
			||||||
          <app-amount [satoshis]="node.capacity_left + node.capacity_right" digitsInfo="1.2-2"></app-amount>
 | 
					          <app-amount [satoshis]="node.capacity_left + node.capacity_right" digitsInfo="1.2-2"></app-amount>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										17
									
								
								lightning-backend/src/api/nodes/channels.api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lightning-backend/src/api/nodes/channels.api.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import logger from '../../logger';
 | 
				
			||||||
 | 
					import DB from '../../database';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChannelsApi {
 | 
				
			||||||
 | 
					  public async $getChannelsForNode(public_key: string): Promise<any> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const query = `SELECT * FROM channels WHERE node1_public_key = ? OR node2_public_key = ?`;
 | 
				
			||||||
 | 
					      const [rows]: any = await DB.query(query, [public_key, public_key]);
 | 
				
			||||||
 | 
					      return rows;
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err('$getChannelsForNode error: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					      throw e;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default new ChannelsApi();
 | 
				
			||||||
							
								
								
									
										23
									
								
								lightning-backend/src/api/nodes/channels.routes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								lightning-backend/src/api/nodes/channels.routes.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import config from '../../config';
 | 
				
			||||||
 | 
					import { Express, Request, Response } from 'express';
 | 
				
			||||||
 | 
					import channelsApi from './channels.api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChannelsRoutes {
 | 
				
			||||||
 | 
					  constructor(app: Express) {
 | 
				
			||||||
 | 
					    app
 | 
				
			||||||
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'channels/:public_key', this.$getChannels)
 | 
				
			||||||
 | 
					    ;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private async $getChannels(req: Request, res: Response) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const channels = await channelsApi.$getChannelsForNode(req.params.public_key);
 | 
				
			||||||
 | 
					      res.json(channels);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      res.status(500).send(e instanceof Error ? e.message : e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ChannelsRoutes;
 | 
				
			||||||
@ -2,9 +2,20 @@ import logger from '../../logger';
 | 
				
			|||||||
import DB from '../../database';
 | 
					import DB from '../../database';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NodesApi {
 | 
					class NodesApi {
 | 
				
			||||||
 | 
					  public async $getNode(public_key: string): Promise<any> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const query = `SELECT * FROM nodes WHERE public_key = ?`;
 | 
				
			||||||
 | 
					      const [rows]: any = await DB.query(query, [public_key]);
 | 
				
			||||||
 | 
					      return rows[0];
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err('$getNode error: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
					      throw e;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async $getTopCapacityNodes(): Promise<any> {
 | 
					  public async $getTopCapacityNodes(): Promise<any> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const query = `SELECT nodes.*, nodes_stats.capacity_left, nodes_stats.capacity_right, nodes_stats.channels_left, nodes_stats.channels_right FROM nodes LEFT JOIN nodes_stats ON nodes_stats.public_key = nodes.public_key ORDER BY nodes_stats.capacity_left + nodes_stats.capacity_right DESC LIMIT 10`;
 | 
					      const query = `SELECT nodes.*, nodes_stats.capacity_left, nodes_stats.capacity_right, nodes_stats.channels_left, nodes_stats.channels_right FROM nodes LEFT JOIN nodes_stats ON nodes_stats.public_key = nodes.public_key ORDER BY nodes_stats.added DESC, nodes_stats.capacity_left + nodes_stats.capacity_right DESC LIMIT 10`;
 | 
				
			||||||
      const [rows]: any = await DB.query(query);
 | 
					      const [rows]: any = await DB.query(query);
 | 
				
			||||||
      return rows;
 | 
					      return rows;
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
@ -15,7 +26,7 @@ class NodesApi {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  public async $getTopChannelsNodes(): Promise<any> {
 | 
					  public async $getTopChannelsNodes(): Promise<any> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const query = `SELECT nodes.*, nodes_stats.capacity_left, nodes_stats.capacity_right, nodes_stats.channels_left, nodes_stats.channels_right FROM nodes LEFT JOIN nodes_stats ON nodes_stats.public_key = nodes.public_key ORDER BY nodes_stats.channels_left + nodes_stats.channels_right DESC LIMIT 10`;
 | 
					      const query = `SELECT nodes.*, nodes_stats.capacity_left, nodes_stats.capacity_right, nodes_stats.channels_left, nodes_stats.channels_right FROM nodes LEFT JOIN nodes_stats ON nodes_stats.public_key = nodes.public_key ORDER BY nodes_stats.added DESC, nodes_stats.channels_left + nodes_stats.channels_right DESC LIMIT 10`;
 | 
				
			||||||
      const [rows]: any = await DB.query(query);
 | 
					      const [rows]: any = await DB.query(query);
 | 
				
			||||||
      return rows;
 | 
					      return rows;
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
@ -27,13 +38,13 @@ class NodesApi {
 | 
				
			|||||||
  public async $getLatestStatistics(): Promise<any> {
 | 
					  public async $getLatestStatistics(): Promise<any> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const [rows]: any = await DB.query(`SELECT * FROM statistics ORDER BY id DESC LIMIT 1`);
 | 
					      const [rows]: any = await DB.query(`SELECT * FROM statistics ORDER BY id DESC LIMIT 1`);
 | 
				
			||||||
      const [rows2]: any = await DB.query(`SELECT * FROM statistics ORDER BY id DESC LIMIT 1 OFFSET 24`);
 | 
					      const [rows2]: any = await DB.query(`SELECT * FROM statistics ORDER BY id DESC LIMIT 1 OFFSET 71`);
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        latest: rows[0],
 | 
					        latest: rows[0],
 | 
				
			||||||
        previous: rows2[0],
 | 
					        previous: rows2[0],
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      logger.err('$getTopChannelsNodes error: ' + (e instanceof Error ? e.message : e));
 | 
					      logger.err('$getLatestStatistics error: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
      throw e;
 | 
					      throw e;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,29 @@
 | 
				
			|||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import { Express, Request, Response } from 'express';
 | 
					import { Express, Request, Response } from 'express';
 | 
				
			||||||
import nodesApi from './nodes.api';
 | 
					import nodesApi from './nodes.api';
 | 
				
			||||||
 | 
					import channelsApi from './channels.api';
 | 
				
			||||||
class NodesRoutes {
 | 
					class NodesRoutes {
 | 
				
			||||||
  constructor(app: Express) {
 | 
					  constructor(app: Express) {
 | 
				
			||||||
    app
 | 
					    app
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/latest', this.$getGeneralStats)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'statistics/latest', this.$getGeneralStats)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'nodes/top', this.$getTopNodes)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'nodes/top', this.$getTopNodes)
 | 
				
			||||||
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'nodes/:public_key', this.$getNode)
 | 
				
			||||||
    ;
 | 
					    ;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private async $getNode(req: Request, res: Response) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const node = await nodesApi.$getNode(req.params.public_key);
 | 
				
			||||||
 | 
					      if (!node) {
 | 
				
			||||||
 | 
					        res.status(404).send('Node not found');
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      res.json(node);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      res.status(500).send(e instanceof Error ? e.message : e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private async $getGeneralStats(req: Request, res: Response) {
 | 
					  private async $getGeneralStats(req: Request, res: Response) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const statistics = await nodesApi.$getLatestStatistics();
 | 
					      const statistics = await nodesApi.$getLatestStatistics();
 | 
				
			||||||
 | 
				
			|||||||
@ -125,6 +125,7 @@ class DatabaseMigration {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // Set initial values
 | 
					      // Set initial values
 | 
				
			||||||
      await this.$executeQuery(`INSERT INTO state VALUES('schema_version', 0, NULL);`);
 | 
					      await this.$executeQuery(`INSERT INTO state VALUES('schema_version', 0, NULL);`);
 | 
				
			||||||
 | 
					      await this.$executeQuery(`INSERT INTO state VALUES('last_node_stats', 0, '1970-01-01');`);
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      throw e;
 | 
					      throw e;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import databaseMigration from './database-migration';
 | 
				
			|||||||
import statsUpdater from './tasks/stats-updater.service';
 | 
					import statsUpdater from './tasks/stats-updater.service';
 | 
				
			||||||
import nodeSyncService from './tasks/node-sync.service';
 | 
					import nodeSyncService from './tasks/node-sync.service';
 | 
				
			||||||
import NodesRoutes from './api/nodes/nodes.routes';
 | 
					import NodesRoutes from './api/nodes/nodes.routes';
 | 
				
			||||||
 | 
					import ChannelsRoutes from './api/nodes/channels.routes';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger.notice(`Mempool Server is running on port ${config.MEMPOOL.HTTP_PORT}`);
 | 
					logger.notice(`Mempool Server is running on port ${config.MEMPOOL.HTTP_PORT}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -46,6 +47,7 @@ class LightningServer {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const nodeRoutes = new NodesRoutes(this.app);
 | 
					    const nodeRoutes = new NodesRoutes(this.app);
 | 
				
			||||||
 | 
					    const channelsRoutes = new ChannelsRoutes(this.app);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -15,22 +15,35 @@ class LightningStatsUpdater {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
      this.$logLightningStats();
 | 
					      this.$logLightningStats();
 | 
				
			||||||
 | 
					      this.$logNodeStatsDaily();
 | 
				
			||||||
      setInterval(() => {
 | 
					      setInterval(() => {
 | 
				
			||||||
        this.$logLightningStats();
 | 
					        this.$logLightningStats();
 | 
				
			||||||
 | 
					        this.$logNodeStatsDaily();
 | 
				
			||||||
      }, 1000 * 60 * 60);
 | 
					      }, 1000 * 60 * 60);
 | 
				
			||||||
    }, difference);
 | 
					    }, difference);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // this.$logNodeStatsDaily();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private async $logNodeStatsDaily() {
 | 
					  private async $logNodeStatsDaily() {
 | 
				
			||||||
    const query = `SELECT nodes.public_key, COUNT(DISTINCT c1.id) AS channels_count_left, COUNT(DISTINCT c2.id) AS channels_count_right, SUM(DISTINCT c1.capacity) AS channels_capacity_left, SUM(DISTINCT c2.capacity) AS channels_capacity_right FROM nodes LEFT JOIN channels AS c1 ON c1.node1_public_key = nodes.public_key LEFT JOIN channels AS c2 ON c2.node2_public_key = nodes.public_key GROUP BY nodes.public_key`;
 | 
					    const currentDate = new Date().toISOString().split('T')[0];
 | 
				
			||||||
    const [nodes]: any = await DB.query(query);
 | 
					    try {
 | 
				
			||||||
 | 
					      const [state]: any = await DB.query(`SELECT string FROM state WHERE name = 'last_node_stats'`);
 | 
				
			||||||
 | 
					      // Only store once per day
 | 
				
			||||||
 | 
					      if (state[0] === currentDate) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const node of nodes) {
 | 
					      const query = `SELECT nodes.public_key, c1.channels_count_left, c2.channels_count_right, c1.channels_capacity_left, c2.channels_capacity_right FROM nodes LEFT JOIN (SELECT node1_public_key, COUNT(id) AS channels_count_left, SUM(capacity) AS channels_capacity_left FROM channels GROUP BY node1_public_key) c1 ON c1.node1_public_key = nodes.public_key LEFT JOIN (SELECT node2_public_key, COUNT(id) AS channels_count_right, SUM(capacity) AS channels_capacity_right FROM channels GROUP BY node2_public_key) c2 ON c2.node2_public_key = nodes.public_key`;
 | 
				
			||||||
      await DB.query(
 | 
					      const [nodes]: any = await DB.query(query);
 | 
				
			||||||
        `INSERT INTO nodes_stats(public_key, added, capacity_left, capacity_right, channels_left, channels_right) VALUES (?, NOW(), ?, ?, ?, ?)`,
 | 
					
 | 
				
			||||||
        [node.public_key, node.channels_capacity_left, node.channels_capacity_right, node.channels_count_left, node.channels_count_right]);
 | 
					      for (const node of nodes) {
 | 
				
			||||||
 | 
					        await DB.query(
 | 
				
			||||||
 | 
					          `INSERT INTO nodes_stats(public_key, added, capacity_left, capacity_right, channels_left, channels_right) VALUES (?, NOW(), ?, ?, ?, ?)`,
 | 
				
			||||||
 | 
					          [node.public_key, node.channels_capacity_left, node.channels_capacity_right,
 | 
				
			||||||
 | 
					            node.channels_count_left, node.channels_count_right]);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      await DB.query(`UPDATE state SET string = ? WHERE name = 'last_node_stats'`, [currentDate]);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      logger.err('$logNodeStatsDaily() error: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user