1use std::fs;
2use std::path::Path;
3
4use serde::Deserialize;
5
6use crate::error::{EnigmaRelayError, Result};
7
8#[derive(Clone, Deserialize, Debug, PartialEq, Eq)]
9#[serde(rename_all = "lowercase")]
10pub enum RelayMode {
11 Http,
12 Tls,
13}
14
15#[derive(Clone, Deserialize, Debug, PartialEq, Eq)]
16#[serde(rename_all = "lowercase")]
17pub enum StorageKind {
18 Sled,
19 Memory,
20}
21
22#[derive(Clone, Deserialize, Debug)]
23pub struct TlsConfig {
24 pub cert_pem_path: String,
25 pub key_pem_path: String,
26 #[serde(default)]
27 pub client_ca_pem_path: Option<String>,
28}
29
30#[derive(Clone, Deserialize, Debug)]
31pub struct StorageConfig {
32 pub kind: StorageKind,
33 pub path: String,
34}
35
36impl Default for StorageConfig {
37 fn default() -> Self {
38 StorageConfig {
39 kind: StorageKind::Sled,
40 path: "./relay_db".to_string(),
41 }
42 }
43}
44
45#[derive(Clone, Deserialize, Debug)]
46pub struct RelayLimits {
47 pub max_message_bytes: u64,
48 pub max_queue_bytes_per_user: u64,
49 pub max_messages_per_user: u64,
50 pub message_ttl_seconds: u64,
51 pub gc_interval_seconds: u64,
52 pub pull_batch_max: u64,
53}
54
55impl Default for RelayLimits {
56 fn default() -> Self {
57 RelayLimits {
58 max_message_bytes: 8_388_608,
59 max_queue_bytes_per_user: 1_073_741_824,
60 max_messages_per_user: 200_000,
61 message_ttl_seconds: 14 * 24 * 3600,
62 gc_interval_seconds: 60,
63 pull_batch_max: 256,
64 }
65 }
66}
67
68#[derive(Clone, Deserialize, Debug)]
69pub struct EndpointRateLimit {
70 pub push_rps: u32,
71 pub pull_rps: u32,
72 pub ack_rps: u32,
73}
74
75impl Default for EndpointRateLimit {
76 fn default() -> Self {
77 EndpointRateLimit {
78 push_rps: 10,
79 pull_rps: 5,
80 ack_rps: 10,
81 }
82 }
83}
84
85#[derive(Clone, Deserialize, Debug)]
86pub struct RateLimitConfig {
87 pub enabled: bool,
88 pub per_ip_rps: u32,
89 pub burst: u32,
90 pub ban_seconds: u64,
91 #[serde(default)]
92 pub endpoints: EndpointRateLimit,
93}
94
95impl Default for RateLimitConfig {
96 fn default() -> Self {
97 RateLimitConfig {
98 enabled: true,
99 per_ip_rps: 20,
100 burst: 40,
101 ban_seconds: 300,
102 endpoints: EndpointRateLimit::default(),
103 }
104 }
105}
106
107#[derive(Clone, Deserialize, Debug)]
108pub struct RelayConfig {
109 pub address: String,
110 #[serde(default = "RelayConfig::default_mode")]
111 pub mode: RelayMode,
112 #[serde(default)]
113 pub tls: Option<TlsConfig>,
114 #[serde(default)]
115 pub storage: StorageConfig,
116 #[serde(default)]
117 pub relay: RelayLimits,
118 #[serde(default)]
119 pub rate_limit: RateLimitConfig,
120}
121
122impl Default for RelayConfig {
123 fn default() -> Self {
124 RelayConfig {
125 address: "127.0.0.1:0".to_string(),
126 mode: RelayMode::Http,
127 tls: None,
128 storage: StorageConfig::default(),
129 relay: RelayLimits::default(),
130 rate_limit: RateLimitConfig::default(),
131 }
132 }
133}
134
135impl RelayConfig {
136 fn default_mode() -> RelayMode {
137 RelayMode::Http
138 }
139
140 pub fn load(path: impl AsRef<Path>) -> Result<Self> {
141 let raw = fs::read_to_string(&path).map_err(|e| EnigmaRelayError::Config(e.to_string()))?;
142 let cfg: RelayConfig =
143 toml::from_str(&raw).map_err(|e| EnigmaRelayError::Config(e.to_string()))?;
144 cfg.validate()?;
145 Ok(cfg)
146 }
147
148 pub fn validate(&self) -> Result<()> {
149 if self.address.trim().is_empty() {
150 return Err(EnigmaRelayError::Config("address is required".to_string()));
151 }
152 match self.mode {
153 RelayMode::Http => {}
154 RelayMode::Tls => {
155 if self.tls.is_none() {
156 return Err(EnigmaRelayError::Config(
157 "tls mode requires tls config".to_string(),
158 ));
159 }
160 #[cfg(not(feature = "tls"))]
161 {
162 return Err(EnigmaRelayError::Disabled(
163 "tls feature not enabled".to_string(),
164 ));
165 }
166 }
167 }
168 if self.relay.max_message_bytes == 0 {
169 return Err(EnigmaRelayError::Config(
170 "max_message_bytes must be positive".to_string(),
171 ));
172 }
173 if self.relay.max_queue_bytes_per_user == 0 {
174 return Err(EnigmaRelayError::Config(
175 "max_queue_bytes_per_user must be positive".to_string(),
176 ));
177 }
178 if self.relay.max_messages_per_user == 0 {
179 return Err(EnigmaRelayError::Config(
180 "max_messages_per_user must be positive".to_string(),
181 ));
182 }
183 if self.relay.pull_batch_max == 0 {
184 return Err(EnigmaRelayError::Config(
185 "pull_batch_max must be positive".to_string(),
186 ));
187 }
188 if self.relay.gc_interval_seconds == 0 {
189 return Err(EnigmaRelayError::Config(
190 "gc_interval_seconds must be positive".to_string(),
191 ));
192 }
193 if self.rate_limit.enabled {
194 if self.rate_limit.per_ip_rps == 0 {
195 return Err(EnigmaRelayError::Config(
196 "rate_limit.per_ip_rps must be positive".to_string(),
197 ));
198 }
199 if self.rate_limit.burst == 0 {
200 return Err(EnigmaRelayError::Config(
201 "rate_limit.burst must be positive".to_string(),
202 ));
203 }
204 if self.rate_limit.endpoints.push_rps == 0
205 || self.rate_limit.endpoints.pull_rps == 0
206 || self.rate_limit.endpoints.ack_rps == 0
207 {
208 return Err(EnigmaRelayError::Config(
209 "endpoint rate limits must be positive".to_string(),
210 ));
211 }
212 }
213 match self.storage.kind {
214 StorageKind::Sled => {
215 #[cfg(not(feature = "persistence"))]
216 {
217 return Err(EnigmaRelayError::Disabled(
218 "persistence feature not enabled".to_string(),
219 ));
220 }
221 }
222 StorageKind::Memory => {}
223 }
224 Ok(())
225 }
226}