unifly_api/config.rs
1// ── Runtime connection configuration ──
2//
3// These types describe *how* to connect to a UniFi controller.
4// They carry credential data and connection tuning, but never touch disk.
5// The CLI/TUI constructs a `ControllerConfig` and hands it in.
6
7use secrecy::SecretString;
8use url::Url;
9
10/// How to authenticate with a controller.
11///
12/// Named `AuthCredentials` (not `AuthMethod`) to avoid collision with
13/// `unifly_api::AuthStrategy` which is a zero-data marker enum.
14/// This type carries the actual credential data.
15#[derive(Debug, Clone)]
16pub enum AuthCredentials {
17 /// Integration API key (preferred).
18 ApiKey(SecretString),
19 /// Legacy cookie-based auth.
20 Credentials {
21 username: String,
22 password: SecretString,
23 },
24 /// Hybrid: API key for Integration API + credentials for Legacy API.
25 ///
26 /// Gives full access to both APIs in a single session — Integration API
27 /// for CRUD and reads, Legacy API for stats, events, alarms, and admin.
28 Hybrid {
29 api_key: SecretString,
30 username: String,
31 password: SecretString,
32 },
33 /// Cloud connector via api.ui.com.
34 Cloud {
35 api_key: SecretString,
36 host_id: String,
37 },
38}
39
40/// TLS verification strategy.
41#[derive(Debug, Clone, Default)]
42pub enum TlsVerification {
43 /// System CA store (strict).
44 SystemDefaults,
45 /// Custom CA certificate file.
46 CustomCa(std::path::PathBuf),
47 /// Skip verification (self-signed certs). Default for local controllers.
48 #[default]
49 DangerAcceptInvalid,
50}
51
52impl PartialEq for TlsVerification {
53 fn eq(&self, other: &Self) -> bool {
54 match (self, other) {
55 (Self::CustomCa(a), Self::CustomCa(b)) => a == b,
56 (Self::SystemDefaults, Self::SystemDefaults)
57 | (Self::DangerAcceptInvalid, Self::DangerAcceptInvalid) => true,
58 _ => false,
59 }
60 }
61}
62
63impl Eq for TlsVerification {}
64
65/// Configuration for connecting to a single controller.
66///
67/// Built by CLI/TUI, passed to `Controller` -- core never reads config files.
68#[derive(Debug, Clone)]
69pub struct ControllerConfig {
70 /// Controller URL (e.g., `https://192.168.1.1`).
71 pub url: Url,
72 /// Authentication method and credentials.
73 pub auth: AuthCredentials,
74 /// Site to operate on (defaults to "default").
75 pub site: String,
76 /// TLS verification strategy.
77 pub tls: TlsVerification,
78 /// Request timeout.
79 pub timeout: std::time::Duration,
80 /// How often to perform a full refresh (seconds). 0 = never.
81 pub refresh_interval_secs: u64,
82 /// Enable WebSocket event stream.
83 pub websocket_enabled: bool,
84 /// Polling interval when WebSocket is unavailable (seconds).
85 pub polling_interval_secs: u64,
86}
87
88impl Default for ControllerConfig {
89 fn default() -> Self {
90 Self {
91 url: "https://192.168.1.1:8443"
92 .parse()
93 .expect("default controller URL is valid"),
94 auth: AuthCredentials::Credentials {
95 username: "admin".into(),
96 password: SecretString::from(String::new()),
97 },
98 site: "default".into(),
99 tls: TlsVerification::default(),
100 timeout: std::time::Duration::from_secs(30),
101 refresh_interval_secs: 300,
102 websocket_enabled: true,
103 polling_interval_secs: 10,
104 }
105 }
106}