ravenrs_report_server/
config.rs1use std::collections::BTreeMap;
5use std::path::{Path, PathBuf};
6
7use serde::Deserialize;
8
9const MIN_TOKEN_LENGTH: usize = 16;
11
12#[derive(Clone, Debug, Deserialize)]
14#[serde(rename_all = "camelCase", deny_unknown_fields)]
15pub struct Config {
16 #[serde(default = "default_listen")]
18 pub listen: String,
19 pub data_dir: PathBuf,
21 #[serde(default = "default_token_env")]
23 pub api_token_env: String,
24 #[serde(default)]
26 pub output_dir: Option<PathBuf>,
27 #[serde(default)]
29 pub smtp: Option<SmtpConfig>,
30 #[serde(default)]
32 pub data_hosts: Vec<DataHost>,
33 #[serde(default = "default_concurrency")]
35 pub max_concurrent_renders: usize,
36 #[serde(default = "default_retry_delay")]
38 pub retry_delay_seconds: u64,
39 #[serde(default = "default_misfire_grace")]
41 pub misfire_grace_minutes: u64,
42 #[serde(default = "default_history")]
44 pub history_limit: usize,
45 #[serde(default = "default_body_limit")]
47 pub max_body_megabytes: usize,
48}
49
50#[derive(Clone, Debug, Deserialize)]
52#[serde(rename_all = "camelCase", deny_unknown_fields)]
53pub struct SmtpConfig {
54 pub host: String,
56 #[serde(default)]
58 pub port: Option<u16>,
59 #[serde(default)]
61 pub security: SmtpSecurity,
62 pub from: String,
64 #[serde(default)]
66 pub username_env: Option<String>,
67 #[serde(default)]
69 pub password_env: Option<String>,
70}
71
72#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize)]
74#[serde(rename_all = "camelCase")]
75pub enum SmtpSecurity {
76 #[default]
78 Starttls,
79 Tls,
81 None,
83}
84
85#[derive(Clone, Debug, Deserialize)]
87#[serde(rename_all = "camelCase", deny_unknown_fields)]
88pub struct DataHost {
89 pub host: String,
91 #[serde(default)]
93 pub headers_env: BTreeMap<String, String>,
94}
95
96fn default_listen() -> String {
97 "127.0.0.1:8090".to_owned()
98}
99
100fn default_token_env() -> String {
101 "RAVENRS_SERVER_TOKEN".to_owned()
102}
103
104fn default_concurrency() -> usize {
105 4
106}
107
108fn default_retry_delay() -> u64 {
109 60
110}
111
112fn default_misfire_grace() -> u64 {
113 60
114}
115
116fn default_history() -> usize {
117 1000
118}
119
120fn default_body_limit() -> usize {
121 32
122}
123
124impl Config {
125 pub fn new(data_dir: impl Into<PathBuf>) -> Self {
127 Self {
128 listen: default_listen(),
129 data_dir: data_dir.into(),
130 api_token_env: default_token_env(),
131 output_dir: None,
132 smtp: None,
133 data_hosts: Vec::new(),
134 max_concurrent_renders: default_concurrency(),
135 retry_delay_seconds: default_retry_delay(),
136 misfire_grace_minutes: default_misfire_grace(),
137 history_limit: default_history(),
138 max_body_megabytes: default_body_limit(),
139 }
140 }
141
142 pub fn load(path: &Path) -> Result<Self, String> {
144 let text = std::fs::read_to_string(path).map_err(|e| format!("{}: {e}", path.display()))?;
145 serde_json::from_str(&text).map_err(|e| format!("{}: {e}", path.display()))
146 }
147
148 pub fn api_token(&self) -> Result<String, String> {
150 let token = std::env::var(&self.api_token_env)
151 .map_err(|_| format!("set the API token in the environment variable {}", self.api_token_env))?;
152 if token.len() < MIN_TOKEN_LENGTH {
153 return Err(format!("the API token must have at least {MIN_TOKEN_LENGTH} characters"));
154 }
155 Ok(token)
156 }
157}