unifly_api/convert/
client.rs1use chrono::Utc;
2
3use crate::integration_types;
4use crate::model::client::{Client, ClientType, GuestAuth, WirelessInfo};
5use crate::model::common::DataSource;
6use crate::model::entity_id::{EntityId, MacAddress};
7use crate::session::models::SessionClientEntry;
8
9use super::helpers::{parse_ip, parse_iso};
10
11fn channel_to_frequency(channel: Option<i32>) -> Option<f32> {
14 channel.map(|ch| match ch {
15 1..=14 => 2.4,
16 32..=68 | 96..=177 => 5.0,
17 _ => 6.0,
18 })
19}
20
21impl From<SessionClientEntry> for Client {
22 fn from(c: SessionClientEntry) -> Self {
23 let is_wired = c.is_wired.unwrap_or(false);
24 let client_type = if is_wired {
25 ClientType::Wired
26 } else {
27 ClientType::Wireless
28 };
29
30 let wireless = if is_wired {
31 None
32 } else {
33 Some(WirelessInfo {
34 ssid: c.essid.clone(),
35 bssid: c.bssid.as_deref().map(MacAddress::new),
36 channel: c.channel.and_then(|ch| ch.try_into().ok()),
37 frequency_ghz: channel_to_frequency(c.channel),
38 signal_dbm: c.signal.or(c.rssi),
39 noise_dbm: c.noise,
40 satisfaction: c.satisfaction.and_then(|s| s.try_into().ok()),
41 tx_rate_kbps: c.tx_rate.and_then(|r| r.try_into().ok()),
42 rx_rate_kbps: c.rx_rate.and_then(|r| r.try_into().ok()),
43 })
44 };
45
46 let is_guest = c.is_guest.unwrap_or(false);
47 let guest_auth = if is_guest {
48 Some(GuestAuth {
49 authorized: c.authorized.unwrap_or(false),
50 method: None,
51 expires_at: None,
52 tx_bytes: c.tx_bytes.and_then(|b| b.try_into().ok()),
53 rx_bytes: c.rx_bytes.and_then(|b| b.try_into().ok()),
54 elapsed_minutes: None,
55 })
56 } else {
57 None
58 };
59
60 let uplink_device_mac = if is_wired {
61 c.sw_mac.as_deref().map(MacAddress::new)
62 } else {
63 c.ap_mac.as_deref().map(MacAddress::new)
64 };
65
66 let connected_at = c.uptime.and_then(|secs| {
67 let duration = chrono::Duration::seconds(secs);
68 Utc::now().checked_sub_signed(duration)
69 });
70
71 Client {
72 id: EntityId::from(c.id),
73 mac: MacAddress::new(&c.mac),
74 ip: parse_ip(c.ip.as_ref()),
75 name: c.name,
76 hostname: c.hostname,
77 client_type,
78 connected_at,
79 uplink_device_id: None,
80 uplink_device_mac,
81 network_id: c.network_id.map(EntityId::from),
82 vlan: None,
83 wireless,
84 guest_auth,
85 is_guest,
86 tx_bytes: c.tx_bytes.and_then(|b| b.try_into().ok()),
87 rx_bytes: c.rx_bytes.and_then(|b| b.try_into().ok()),
88 bandwidth: None,
89 os_name: None,
90 device_class: None,
91 use_fixedip: false,
92 fixed_ip: None,
93 blocked: c.blocked.unwrap_or(false),
94 source: DataSource::SessionApi,
95 updated_at: Utc::now(),
96 }
97 }
98}
99
100impl From<integration_types::ClientResponse> for Client {
103 fn from(c: integration_types::ClientResponse) -> Self {
104 let client_type = match c.client_type.as_str() {
105 "WIRED" => ClientType::Wired,
106 "WIRELESS" => ClientType::Wireless,
107 "VPN" => ClientType::Vpn,
108 "TELEPORT" => ClientType::Teleport,
109 _ => ClientType::Unknown,
110 };
111
112 let uuid_fallback = c.id.to_string();
113 let mac_str = c
114 .mac_address
115 .as_deref()
116 .filter(|s| !s.is_empty())
117 .unwrap_or(&uuid_fallback);
118
119 Client {
120 id: EntityId::Uuid(c.id),
121 mac: MacAddress::new(mac_str),
122 ip: c.ip_address.as_deref().and_then(|s| s.parse().ok()),
123 name: Some(c.name),
124 hostname: None,
125 client_type,
126 connected_at: c.connected_at.as_deref().and_then(parse_iso),
127 uplink_device_id: None,
128 uplink_device_mac: None,
129 network_id: None,
130 vlan: None,
131 wireless: None,
132 guest_auth: None,
133 is_guest: false,
134 tx_bytes: None,
135 rx_bytes: None,
136 bandwidth: None,
137 os_name: None,
138 device_class: None,
139 use_fixedip: false,
140 fixed_ip: None,
141 blocked: false,
142 source: DataSource::IntegrationApi,
143 updated_at: Utc::now(),
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn channel_frequency_bands() {
154 assert_eq!(channel_to_frequency(Some(6)), Some(2.4));
155 assert_eq!(channel_to_frequency(Some(36)), Some(5.0));
156 assert_eq!(channel_to_frequency(Some(149)), Some(5.0));
157 assert_eq!(channel_to_frequency(None), None);
158 }
159}