diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts
index 4abad3e4e..3814da73d 100644
--- a/backend/src/api/explorer/nodes.api.ts
+++ b/backend/src/api/explorer/nodes.api.ts
@@ -127,12 +127,27 @@ class NodesApi {
public async $getNodesPerCountry(country: string) {
try {
- const query = `SELECT nodes.* FROM nodes
- JOIN geo_names ON geo_names.id = nodes.country_id
- WHERE LOWER(json_extract(names, '$.en')) = ?
+ const query = `
+ SELECT node_stats.public_key, node_stats.capacity, node_stats.channels, nodes.alias,
+ UNIX_TIMESTAMP(nodes.first_seen) as first_seen, UNIX_TIMESTAMP(nodes.updated_at) as updated_at,
+ geo_names_city.names as city
+ FROM node_stats
+ JOIN (
+ SELECT public_key, MAX(added) as last_added
+ FROM node_stats
+ GROUP BY public_key
+ ) as b ON b.public_key = node_stats.public_key AND b.last_added = node_stats.added
+ LEFT JOIN nodes ON nodes.public_key = node_stats.public_key
+ LEFT JOIN geo_names geo_names_country ON geo_names_country.id = nodes.country_id
+ LEFT JOIN geo_names geo_names_city ON geo_names_city.id = nodes.city_id
+ WHERE LOWER(JSON_EXTRACT(geo_names_country.names, '$.en')) = ?
+ ORDER BY capacity DESC
`;
const [rows]: any = await DB.query(query, [`"${country}"`]);
+ for (let i = 0; i < rows.length; ++i) {
+ rows[i].city = JSON.parse(rows[i].city);
+ }
return rows;
} catch (e) {
logger.err(`Cannot get nodes for country ${country}. Reason: ${e instanceof Error ? e.message : e}`);
diff --git a/frontend/src/app/lightning/lightning.module.ts b/frontend/src/app/lightning/lightning.module.ts
index 1cf9992f6..c4fa1bfb0 100644
--- a/frontend/src/app/lightning/lightning.module.ts
+++ b/frontend/src/app/lightning/lightning.module.ts
@@ -19,6 +19,7 @@ import { GraphsModule } from '../graphs/graphs.module';
import { NodesNetworksChartComponent } from './nodes-networks-chart/nodes-networks-chart.component';
import { ChannelsStatisticsComponent } from './channels-statistics/channels-statistics.component';
import { NodesPerAsChartComponent } from '../lightning/nodes-per-as-chart/nodes-per-as-chart.component';
+import { NodesPerCountry } from './nodes-per-country/nodes-per-country.component';
@NgModule({
declarations: [
LightningDashboardComponent,
@@ -35,6 +36,7 @@ import { NodesPerAsChartComponent } from '../lightning/nodes-per-as-chart/nodes-
NodesNetworksChartComponent,
ChannelsStatisticsComponent,
NodesPerAsChartComponent,
+ NodesPerCountry,
],
imports: [
CommonModule,
diff --git a/frontend/src/app/lightning/lightning.routing.module.ts b/frontend/src/app/lightning/lightning.routing.module.ts
index e56a527f9..be6de3afd 100644
--- a/frontend/src/app/lightning/lightning.routing.module.ts
+++ b/frontend/src/app/lightning/lightning.routing.module.ts
@@ -4,6 +4,7 @@ import { LightningDashboardComponent } from './lightning-dashboard/lightning-das
import { LightningWrapperComponent } from './lightning-wrapper/lightning-wrapper.component';
import { NodeComponent } from './node/node.component';
import { ChannelComponent } from './channel/channel.component';
+import { NodesPerCountry } from './nodes-per-country/nodes-per-country.component';
const routes: Routes = [
{
@@ -22,6 +23,10 @@ const routes: Routes = [
path: 'channel/:short_id',
component: ChannelComponent,
},
+ {
+ path: 'nodes/country/:country',
+ component: NodesPerCountry,
+ },
{
path: '**',
redirectTo: ''
diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html
new file mode 100644
index 000000000..90e6ed1bd
--- /dev/null
+++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.html
@@ -0,0 +1,42 @@
+
+
Nodes in {{ country }}
+
+
+
+
+ Alias |
+ First seen |
+ Last update |
+ Capacity |
+ Channels |
+ City |
+
+
+
+
+ {{ node.alias }}
+ |
+
+
+ |
+
+
+ |
+
+ 100000000; else smallchannel" [satoshis]="node.capacity" [digitsInfo]="'1.2-2'" [noFiat]="true">
+
+ {{ node.capacity | amountShortener: 1 }}
+ sats
+
+ |
+
+ {{ node.channels }}
+ |
+
+ {{ node?.city?.en ?? '-' }}
+ |
+
+
+
+
+
diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss
new file mode 100644
index 000000000..02b47e8be
--- /dev/null
+++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.scss
@@ -0,0 +1,62 @@
+.container-xl {
+ max-width: 1400px;
+ padding-bottom: 100px;
+}
+
+.sats {
+ color: #ffffff66;
+ font-size: 12px;
+ top: 0px;
+}
+
+.alias {
+ width: 30%;
+ max-width: 400px;
+ padding-right: 70px;
+
+ @media (max-width: 576px) {
+ width: 50%;
+ max-width: 150px;
+ padding-right: 0px;
+ }
+}
+
+.timestamp-first {
+ width: 20%;
+
+ @media (max-width: 576px) {
+ display: none
+ }
+}
+
+.timestamp-update {
+ width: 16%;
+
+ @media (max-width: 576px) {
+ display: none
+ }
+}
+
+.capacity {
+ width: 10%;
+
+ @media (max-width: 576px) {
+ width: 25%;
+ }
+}
+
+.channels {
+ width: 10%;
+
+ @media (max-width: 576px) {
+ width: 25%;
+ }
+}
+
+.city {
+ max-width: 150px;
+
+ @media (max-width: 576px) {
+ display: none
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts
new file mode 100644
index 000000000..db9bc5809
--- /dev/null
+++ b/frontend/src/app/lightning/nodes-per-country/nodes-per-country.component.ts
@@ -0,0 +1,37 @@
+import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { map, Observable } from 'rxjs';
+import { ApiService } from 'src/app/services/api.service';
+
+@Component({
+ selector: 'app-nodes-per-country',
+ templateUrl: './nodes-per-country.component.html',
+ styleUrls: ['./nodes-per-country.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class NodesPerCountry implements OnInit {
+ nodes$: Observable;
+ country: string;
+
+ constructor(
+ private apiService: ApiService,
+ private route: ActivatedRoute,
+ ) { }
+
+ ngOnInit(): void {
+ this.country = this.route.snapshot.params.country;
+ this.country = this.country.charAt(0).toUpperCase() + this.country.slice(1);
+
+ this.nodes$ = this.apiService.getNodeForCountry$(this.route.snapshot.params.country)
+ .pipe(
+ map(nodes => {
+ console.log(nodes);
+ return nodes;
+ })
+ );
+ }
+
+ trackByPublicKey(index: number, node: any) {
+ return node.public_key;
+ }
+}
diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts
index 48f23a94f..6f83ce7e8 100644
--- a/frontend/src/app/services/api.service.ts
+++ b/frontend/src/app/services/api.service.ts
@@ -254,4 +254,8 @@ export class ApiService {
getNodesPerAs(): Observable {
return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/asShare');
}
+
+ getNodeForCountry$(country: string): Observable {
+ return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/country/' + country);
+ }
}
diff --git a/frontend/src/app/shared/components/timestamp/timestamp.component.html b/frontend/src/app/shared/components/timestamp/timestamp.component.html
index b37ff065a..769b292d4 100644
--- a/frontend/src/app/shared/components/timestamp/timestamp.component.html
+++ b/frontend/src/app/shared/components/timestamp/timestamp.component.html
@@ -1,4 +1,4 @@
-{{ seconds * 1000 | date:'yyyy-MM-dd HH:mm' }}
+{{ seconds * 1000 | date: customFormat ?? 'yyyy-MM-dd HH:mm' }}
diff --git a/frontend/src/app/shared/components/timestamp/timestamp.component.ts b/frontend/src/app/shared/components/timestamp/timestamp.component.ts
index a0c9861f0..dc577a185 100644
--- a/frontend/src/app/shared/components/timestamp/timestamp.component.ts
+++ b/frontend/src/app/shared/components/timestamp/timestamp.component.ts
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/c
export class TimestampComponent implements OnChanges {
@Input() unixTime: number;
@Input() dateString: string;
+ @Input() customFormat: string;
seconds: number;