1use std::{collections::HashMap, fs::read_to_string, net::SocketAddr, str::FromStr};
2
3use anyhow::Result;
4use clap::Parser;
5use serde::{Deserialize, Serialize};
6
7use crate::service::session::ports::PortRange;
8
9#[derive(Deserialize, Serialize, Debug, Clone)]
11#[serde(rename_all = "kebab-case")]
12pub struct Ssl {
13 pub private_key: String,
17 pub certificate_chain: String,
21}
22
23#[derive(Deserialize, Serialize, Debug, Clone)]
24#[serde(tag = "transport", rename_all = "kebab-case")]
25pub enum Interface {
26 Tcp {
27 listen: SocketAddr,
28 external: SocketAddr,
37 #[serde(default = "Interface::idle_timeout")]
44 idle_timeout: u32,
45 #[serde(default)]
49 ssl: Option<Ssl>,
50 },
51 Udp {
52 listen: SocketAddr,
53 external: SocketAddr,
62 #[serde(default = "Interface::idle_timeout")]
69 idle_timeout: u32,
70 #[serde(default = "Interface::mtu")]
74 mtu: usize,
75 },
76}
77
78impl Interface {
79 fn mtu() -> usize {
80 1500
81 }
82
83 fn idle_timeout() -> u32 {
84 20
85 }
86}
87
88#[derive(Deserialize, Debug, Clone)]
89#[serde(rename_all = "kebab-case")]
90pub struct Server {
91 #[serde(default = "Server::port_range")]
95 pub port_range: PortRange,
96 #[serde(default = "Server::max_threads")]
100 pub max_threads: usize,
101 #[serde(default = "Server::realm")]
110 pub realm: String,
111 #[serde(default)]
119 pub interfaces: Vec<Interface>,
120}
121
122impl Server {
123 pub fn get_externals(&self) -> Vec<SocketAddr> {
124 self.interfaces
125 .iter()
126 .map(|item| match item {
127 Interface::Tcp { external, .. } => *external,
128 Interface::Udp { external, .. } => *external,
129 })
130 .collect()
131 }
132}
133
134impl Server {
135 fn realm() -> String {
136 "localhost".to_string()
137 }
138
139 fn port_range() -> PortRange {
140 PortRange::default()
141 }
142
143 fn max_threads() -> usize {
144 num_cpus::get()
145 }
146}
147
148impl Default for Server {
149 fn default() -> Self {
150 Self {
151 realm: Self::realm(),
152 interfaces: Default::default(),
153 port_range: Self::port_range(),
154 max_threads: Self::max_threads(),
155 }
156 }
157}
158
159#[derive(Deserialize, Debug, Clone)]
160#[serde(rename_all = "kebab-case")]
161pub struct Hooks {
162 #[serde(default = "Hooks::max_channel_size")]
163 pub max_channel_size: usize,
164 pub endpoint: String,
165 #[serde(default)]
166 pub ssl: Option<Ssl>,
167 #[serde(default = "Hooks::timeout")]
168 pub timeout: u32,
169}
170
171impl Hooks {
172 fn max_channel_size() -> usize {
173 1024
174 }
175
176 fn timeout() -> u32 {
177 5
178 }
179}
180
181#[derive(Deserialize, Debug, Clone)]
182#[serde(rename_all = "kebab-case")]
183pub struct Api {
184 #[serde(default = "Api::bind")]
191 pub listen: SocketAddr,
192 #[serde(default)]
193 pub ssl: Option<Ssl>,
194 #[serde(default = "Api::timeout")]
195 pub timeout: u32,
196}
197
198impl Api {
199 fn bind() -> SocketAddr {
200 "127.0.0.1:3000".parse().unwrap()
201 }
202
203 fn timeout() -> u32 {
204 5
205 }
206}
207
208impl Default for Api {
209 fn default() -> Self {
210 Self {
211 timeout: Self::timeout(),
212 listen: Self::bind(),
213 ssl: None,
214 }
215 }
216}
217
218#[derive(Deserialize, Debug, Clone, Copy)]
219#[serde(rename_all = "lowercase")]
220pub enum LogLevel {
221 Error,
222 Warn,
223 Info,
224 Debug,
225 Trace,
226}
227
228impl FromStr for LogLevel {
229 type Err = String;
230
231 fn from_str(value: &str) -> Result<Self, Self::Err> {
232 Ok(match value {
233 "trace" => Self::Trace,
234 "debug" => Self::Debug,
235 "info" => Self::Info,
236 "warn" => Self::Warn,
237 "error" => Self::Error,
238 _ => return Err(format!("unknown log level: {value}")),
239 })
240 }
241}
242
243impl Default for LogLevel {
244 fn default() -> Self {
245 Self::Info
246 }
247}
248
249impl LogLevel {
250 pub fn as_level(&self) -> log::Level {
251 match *self {
252 Self::Error => log::Level::Error,
253 Self::Debug => log::Level::Debug,
254 Self::Trace => log::Level::Trace,
255 Self::Warn => log::Level::Warn,
256 Self::Info => log::Level::Info,
257 }
258 }
259}
260
261#[derive(Deserialize, Debug, Default, Clone)]
262#[serde(rename_all = "kebab-case")]
263pub struct Log {
264 #[serde(default)]
270 pub level: LogLevel,
271}
272
273#[derive(Deserialize, Debug, Default, Clone)]
274#[serde(rename_all = "kebab-case")]
275pub struct Auth {
276 #[serde(default)]
287 pub static_credentials: HashMap<String, String>,
288 pub static_auth_secret: Option<String>,
296 #[serde(default)]
297 pub enable_hooks_auth: bool,
298}
299
300#[derive(Deserialize, Debug, Default, Clone)]
301#[serde(rename_all = "kebab-case")]
302pub struct Config {
303 #[serde(default)]
304 pub server: Server,
305 #[serde(default)]
306 pub api: Option<Api>,
307 #[serde(default)]
308 pub hooks: Option<Hooks>,
309 #[serde(default)]
310 pub log: Log,
311 #[serde(default)]
312 pub auth: Auth,
313}
314
315#[derive(Parser, Debug)]
316#[command(
317 about = env!("CARGO_PKG_DESCRIPTION"),
318 version = env!("CARGO_PKG_VERSION"),
319 author = env!("CARGO_PKG_AUTHORS"),
320)]
321struct Cli {
322 #[arg(long, short)]
328 config: String,
329}
330
331impl Config {
332 pub fn load() -> Result<Self> {
340 Ok(toml::from_str::<Self>(&read_to_string(
341 &Cli::parse().config,
342 )?)?)
343 }
344}