use alloc::vec::Vec;
use core::net::{IpAddr, SocketAddr};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_capabilityversion::CapabilityVersion;
use ts_keys::{DiscoPublicKey, MachinePublicKey, NodePublicKey};
use crate::{DnsResolver, derp_map::RegionId, host_info::HostInfo, user::UserId};
pub type MarshaledSignature<'a> = &'a [u8];
pub type NodeId = i64;
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct StableNodeId<'a>(#[serde(borrow)] pub &'a str);
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase", default)]
pub struct Node<'a> {
#[serde(rename = "ID")]
pub id: NodeId,
#[serde(rename = "StableID", borrow)]
pub stable_id: StableNodeId<'a>,
#[serde(borrow)]
pub name: &'a str,
pub user: UserId,
pub sharer: UserId,
pub key: NodePublicKey,
pub key_expiry: Option<DateTime<Utc>>,
#[serde(borrow)]
pub key_signature: MarshaledSignature<'a>,
pub machine: Option<MachinePublicKey>,
pub disco_key: Option<DiscoPublicKey>,
pub addresses: (ipnet::Ipv4Net, ipnet::Ipv6Net),
#[serde(rename = "AllowedIPs")]
pub allowed_ips: Option<Vec<ipnet::IpNet>>,
pub endpoints: Vec<SocketAddr>,
#[serde(rename = "DERP", with = "legacy_derp_string")]
#[deprecated = "use Node::home_derp field instead"]
pub legacy_derp_string: Option<RegionId>,
#[serde(rename = "HomeDERP", deserialize_with = "crate::util::derp_region_id")]
pub home_derp: Option<RegionId>,
#[serde(borrow)]
pub host_info: HostInfo<'a>,
pub created: DateTime<Utc>,
pub cap: CapabilityVersion,
#[serde(borrow)]
pub tags: Option<Vec<&'a str>>,
pub primary_routes: Vec<ipnet::IpNet>,
pub last_seen: Option<DateTime<Utc>>,
pub online: Option<bool>,
pub machine_authorized: bool,
#[deprecated = "use Node::cap_map instead"]
#[serde(borrow)]
pub capabilities: Vec<ts_nodecapability::NodeCap<'a>>,
#[serde(borrow)]
pub cap_map: ts_nodecapability::Map<'a>,
#[serde(rename = "UnsignedPeerAPIOnly")]
pub unsigned_peer_api_only: bool,
#[serde(rename = "DataPlaneAuditLogID", borrow)]
pub data_plane_audit_log_id: &'a str,
pub expired: bool,
pub self_node_v4_masq_addr_for_this_peer: Option<IpAddr>,
pub self_node_v6_masq_addr_for_this_peer: Option<IpAddr>,
#[serde(rename = "IsWireGuardOnly")]
pub is_wireguard_only: bool,
pub is_jailed: bool,
#[serde(rename = "ExitNodeDNSResolvers", borrow)]
pub exit_node_dns_resolvers: Vec<DnsResolver<'a>>,
}
pub mod legacy_derp_string {
use core::num::NonZeroU32;
use serde::{Deserialize, Serialize};
use crate::DerpRegionId;
const PREFIX: &str = "127.3.3.40:";
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DerpRegionId>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <&'de str>::deserialize(deserializer)?;
if !s.starts_with(PREFIX) {
return Ok(None);
}
let Some((_pfx, port)) = s.split_at_checked(PREFIX.len()) else {
return Ok(None);
};
let port = match port.parse::<u16>() {
Ok(port) => port,
Err(e) => return Err(serde::de::Error::custom(e)),
};
let Some(region) = NonZeroU32::new(port as _) else {
return Ok(None);
};
Ok(Some(DerpRegionId::from(region)))
}
pub fn serialize<S>(val: &Option<DerpRegionId>, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match val {
&Some(x) => {
let val: u32 = x.into();
alloc::format!("{PREFIX}{val}").serialize(s)
}
None => "".serialize(s),
}
}
}