webterm_agent/
config.rs

1use crate::args::Args;
2use crate::models::relay::Relay;
3use gethostname::gethostname;
4use std::sync::Arc;
5use std::time::Duration;
6use tracing::error;
7use webterm_core::random::{random_alphanumeric, random_in_range};
8
9// should be same as in frontend/src/scripts/client/config.ts
10const DEFAULT_RELAYS: [&str; 4] = [
11    "r1.relays.webterm.run",
12    "r2.relays.webterm.run",
13    "r4.relays.webterm.run",
14    "r5.relays.webterm.run",
15];
16pub const RELAY_RECONNECT_INTERVAL: Duration = Duration::from_secs(5);
17pub const DEFAULT_PBKDF2_ITERATIONS: u32 = 100_000;
18
19pub struct Config {
20    args: Args,
21    available_relays: Vec<Arc<Relay>>,
22    original_device_subname: String,
23}
24
25impl Config {
26    pub fn new(args: Args) -> Self {
27        let available_relays = Self::init_available_relays(&args);
28        let device_subname = infer_subname(&args);
29        Self {
30            args,
31            available_relays,
32            original_device_subname: device_subname.clone(),
33        }
34    }
35
36    pub fn device_name(&self) -> &String {
37        &self.args.device_name
38    }
39
40    pub fn original_device_subname(&self) -> &String {
41        &self.original_device_subname
42    }
43
44    pub fn try_new_device_subname(&self) -> String {
45        format!(
46            "{}-{}",
47            self.original_device_subname,
48            random_alphanumeric(4)
49        )
50    }
51
52    pub fn secret_key(&self) -> &String {
53        &self.args.secret_key
54    }
55
56    #[inline]
57    pub fn is_unix(&self) -> bool {
58        cfg!(unix)
59    }
60
61    pub fn wants_daemon(&self) -> bool {
62        self.args.daemon
63    }
64
65    pub fn can_daemon(&self) -> bool {
66        self.is_unix()
67    }
68
69    fn init_available_relays(args: &Args) -> Vec<Arc<Relay>> {
70        let mut result: Vec<Arc<Relay>> = args
71            .relays
72            .as_ref()
73            .map(|relays| {
74                relays
75                    .split(',')
76                    .map(|s| s.trim().to_string())
77                    .filter(|s| !s.is_empty())
78                    .filter_map(|s| match Relay::new(&s) {
79                        Ok(relay) => Some(Arc::new(relay)),
80                        Err(e) => {
81                            error!("Failed to create relay for {}: {}", s, e);
82                            None
83                        }
84                    })
85                    .collect()
86            })
87            .unwrap_or_default();
88
89        if result.is_empty() {
90            result = DEFAULT_RELAYS
91                .iter()
92                .map(|s| Arc::new(Relay::new(s).unwrap()))
93                .collect()
94        }
95
96        result
97    }
98
99    pub fn random_relay(&self) -> Arc<Relay> {
100        let relays = &self.available_relays;
101        let index = random_in_range(0, relays.len());
102        relays[index].clone()
103    }
104}
105
106fn infer_subname(args: &Args) -> String {
107    args.device_subname
108        .clone()
109        .unwrap_or_else(|| get_hostname().unwrap_or_else(random_subname))
110}
111
112fn random_subname() -> String {
113    format!("device-{}", random_alphanumeric(8))
114}
115
116fn get_hostname() -> Option<String> {
117    let name = gethostname().to_string_lossy().to_string();
118    if name.trim().is_empty() {
119        None
120    } else {
121        Some(name)
122    }
123}