Skip to content

Commit 7532d28

Browse files
authored
feat(tauri): add gateway details screen (#3476)
* wip * update gateway type * wip * update readme * cleanup * cleanup
1 parent 565458f commit 7532d28

File tree

20 files changed

+797
-64
lines changed

20 files changed

+797
-64
lines changed

nym-vpn-app/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Show the picked gateway name the tunnel is connected to, when
1313
connecting/connected and the selected node is a country
1414
- Add QUIC mode and Domain-fronting (aka stealth API) settings options
15+
- Add a new screen for gateway details
1516

1617
### Fixed
1718

nym-vpn-app/src-tauri/src/grpc/gateway.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::country::Country;
2+
23
use anyhow::{Result, anyhow};
34
use nym_vpn_proto::proto as p;
45
use serde::{Deserialize, Serialize};
@@ -26,6 +27,46 @@ pub enum Score {
2627
High,
2728
}
2829

30+
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TS, Default)]
31+
#[ts(export)]
32+
#[serde(rename_all = "kebab-case")]
33+
pub enum AsnType {
34+
#[default]
35+
Other,
36+
Residential,
37+
}
38+
39+
#[derive(Serialize, Deserialize, Clone, Debug, TS)]
40+
#[ts(export)]
41+
#[serde(rename_all = "camelCase")]
42+
pub struct Asn {
43+
pub asn: String,
44+
pub name: String,
45+
#[serde(rename = "type")]
46+
pub kind: AsnType,
47+
}
48+
49+
#[derive(Serialize, Deserialize, Clone, Debug, TS)]
50+
#[ts(export)]
51+
#[serde(rename_all = "camelCase")]
52+
pub struct Location {
53+
pub latitude: f64,
54+
pub longitude: f64,
55+
pub city: String,
56+
pub region: String,
57+
}
58+
59+
#[derive(Serialize, Deserialize, Clone, Debug, TS)]
60+
#[ts(export)]
61+
#[serde(rename_all = "camelCase")]
62+
pub struct Performance {
63+
pub score: Score,
64+
pub load: Score,
65+
pub last_updated_utc: String,
66+
/// uptime percentage on the last 24 hours
67+
pub uptime_24h: f32,
68+
}
69+
2970
#[derive(Serialize, Deserialize, Clone, Debug, TS)]
3071
#[ts(export)]
3172
#[serde(rename_all = "camelCase")]
@@ -35,8 +76,14 @@ pub struct Gateway {
3576
pub kind: GatewayType,
3677
pub name: String,
3778
pub country: Country,
79+
pub location: Location,
80+
pub asn: Option<Asn>,
3881
pub mx_score: Score,
3982
pub wg_score: Score,
83+
pub wg_performance: Option<Performance>,
84+
pub exit_ipv4: Option<String>,
85+
pub exit_ipv6: Option<String>,
86+
pub build_version: Option<String>,
4087
}
4188

4289
impl Gateway {
@@ -62,20 +109,31 @@ impl Gateway {
62109

63110
let wg_score = gateway
64111
.wg_performance
112+
.as_ref()
65113
.map(|s| {
66114
p::Score::try_from(s.score)
67115
.inspect_err(|e| error!("failed to parse proto gw wireguard score: {}", e))
68116
})
69117
.transpose()?
70118
.unwrap_or(p::Score::Offline);
71119

120+
let asn = location.asn.clone().map(|a| a.into());
121+
let exit_ipv4 = gateway.exit_ipv4s.first().cloned();
122+
let exit_ipv6 = gateway.exit_ipv6s.first().cloned();
123+
72124
Ok(Self {
73125
id: id.id,
74126
kind: gw_type,
75127
name: gateway.moniker,
76128
country: Country::try_from(&location)?,
129+
location: location.into(),
130+
asn,
77131
mx_score: Score::from(mx_score),
78132
wg_score: Score::from(wg_score),
133+
wg_performance: gateway.wg_performance.map(|p| p.into()),
134+
exit_ipv4,
135+
exit_ipv6,
136+
build_version: gateway.build_version,
79137
})
80138
}
81139
}
@@ -111,6 +169,42 @@ impl From<GatewayType> for p::GatewayType {
111169
}
112170
}
113171

172+
impl From<p::RichLocation> for Location {
173+
fn from(proto: p::RichLocation) -> Self {
174+
Location {
175+
latitude: proto.latitude,
176+
longitude: proto.longitude,
177+
city: proto.city,
178+
region: proto.region,
179+
}
180+
}
181+
}
182+
183+
impl From<p::Asn> for Asn {
184+
fn from(proto: p::Asn) -> Self {
185+
let asn_kind = &proto.kind();
186+
Asn {
187+
asn: proto.asn,
188+
name: proto.name,
189+
kind: match asn_kind {
190+
p::AsnKind::Residential => AsnType::Residential,
191+
p::AsnKind::Other => AsnType::Other,
192+
},
193+
}
194+
}
195+
}
196+
197+
impl From<p::Performance> for Performance {
198+
fn from(proto: p::Performance) -> Self {
199+
Performance {
200+
score: Score::from(proto.score()),
201+
load: Score::from(proto.load()),
202+
last_updated_utc: proto.last_updated_utc,
203+
uptime_24h: proto.uptime_percentage_last_24_hours,
204+
}
205+
}
206+
}
207+
114208
impl fmt::Display for Gateway {
115209
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116210
write!(f, "[{}] ({}) {}", self.id, self.name, self.country)

nym-vpn-app/src-tauri/src/grpc/node.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::country::Country;
1313
#[serde(rename_all = "lowercase")]
1414
#[serde(untagged)]
1515
#[ts(export)]
16+
#[allow(clippy::large_enum_variant)]
1617
pub enum NodeConnect {
1718
Country(Country),
1819
Gateway(Gateway),

nym-vpn-app/src/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,7 @@ export const SentryPrivacyPolicyUrl = 'https://sentry.io/privacy/';
4141
export const AnonNetworkStatsUrl = 'https://nym.com/anonymous-stats';
4242
export const QuicUrl = 'https://nym.com/features/quic';
4343
export const DomainFrontingUrl = 'https://nym.com/features/stealth-api-connect';
44+
export const IpInfoIoUrl = 'https://ipinfo.io';
45+
export const SupportServerLocationUrl =
46+
'https://support.nym.com/hc/en-us/articles/26448676449297-How-is-server-location-determined-by-NymVPN';
47+
export const NetworkExplorerNodeUrl = 'https://nym.com/explorer/nym-node';

0 commit comments

Comments
 (0)