1use std::net::SocketAddr;
9use std::time::{SystemTime, UNIX_EPOCH};
10
11use rand::rngs::OsRng;
12use rand::RngCore;
13use serde::{Deserialize, Serialize};
14
15use crate::CoreError;
16use base64::Engine;
17
18const INVITE_PREFIX: &str = "rift://join/";
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct Invite {
23 pub channel_name: String,
25 pub password: Option<String>,
27 pub channel_key: [u8; 32],
29 pub known_peers: Vec<SocketAddr>,
31 #[serde(default)]
33 pub candidates: Vec<SocketAddr>,
34 #[serde(default)]
36 pub relay_candidates: Vec<SocketAddr>,
37 pub version: u8,
39 pub created_at: u64,
41}
42
43pub fn generate_invite(
45 channel_name: &str,
46 password: Option<&str>,
47 known_peers: Vec<SocketAddr>,
48 candidates: Vec<SocketAddr>,
49) -> Invite {
50 let mut channel_key = [0u8; 32];
51 OsRng.fill_bytes(&mut channel_key);
52 Invite {
53 channel_name: channel_name.to_string(),
54 password: password.map(|s| s.to_string()),
55 channel_key,
56 known_peers,
57 candidates,
58 relay_candidates: Vec::new(),
59 version: 2,
60 created_at: now_timestamp(),
61 }
62}
63
64pub fn encode_invite(invite: &Invite) -> String {
66 let bytes = bincode::serialize(invite).expect("serialize invite");
67 let encoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes);
68 format!("{}{}", INVITE_PREFIX, encoded)
69}
70
71pub fn decode_invite(url: &str) -> Result<Invite, CoreError> {
73 if !url.starts_with(INVITE_PREFIX) {
74 return Err(CoreError::InvalidInvite);
75 }
76 let payload = &url[INVITE_PREFIX.len()..];
77 let bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD
78 .decode(payload)
79 .map_err(|_| CoreError::InvalidInvite)?;
80 let invite: Invite = bincode::deserialize(&bytes)?;
81 Ok(invite)
82}
83
84fn now_timestamp() -> u64 {
86 SystemTime::now()
87 .duration_since(UNIX_EPOCH)
88 .map(|d| d.as_secs())
89 .unwrap_or(0)
90}