1use serde::{Deserialize, Serialize};
6use core::default::Default;
7use std::prelude::v1::*;
8use std::collections::BTreeMap;
9
10use product_os_configuration::{ProductOSConfig, ConfigError};
11#[cfg(feature = "core")]
12use product_os_configuration::logging::LogLevel;
13use product_os_configuration::logging::Logging;
14use product_os_net::SocketAddr;
15
16#[derive(Clone, Debug, Deserialize, Serialize)]
24#[serde(rename_all = "camelCase")]
25pub struct ServerConfig {
26 pub environment: String,
28 pub root_path: String,
30 pub network: Network,
32 pub logging: Logging,
34 pub certificate: Option<Certificate>,
36 pub compression: Option<Compression>,
38 pub command_control: Option<serde_json::Value>,
40 pub security: Option<serde_json::Value>,
42 pub oidc: Option<serde_json::Value>,
44 pub store: Option<serde_json::Value>,
46 pub authentication: Option<serde_json::Value>,
48 pub content: Option<serde_json::Value>,
50 pub proxy: Option<serde_json::Value>,
52 pub browser: Option<serde_json::Value>,
54 pub crawler: Option<serde_json::Value>,
56 pub vpn: Option<serde_json::Value>,
58 pub connectors: Option<BTreeMap<String, serde_json::Value>>,
60}
61
62impl Default for ServerConfig {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68impl ServerConfig {
69 #[must_use]
71 pub fn new() -> Self {
72 Self {
73 environment: String::from("development"),
74 root_path: String::from("/"),
75 network: Network::default(),
76 logging: Logging::default(),
77 certificate: None,
78 compression: Some(Compression {
79 enable: false,
80 gzip: true,
81 deflate: false,
82 brotli: true,
83 }),
84 command_control: None,
85 security: None,
86 oidc: None,
87 store: None,
88 authentication: None,
89 content: None,
90 proxy: None,
91 browser: None,
92 crawler: None,
93 vpn: None,
94 connectors: None,
95 }
96 }
97
98 #[must_use]
100 pub fn environment(&self) -> &str {
101 &self.environment
102 }
103
104 #[cfg(feature = "core")]
108 #[must_use]
109 pub fn log_level(&self) -> tracing::Level {
110 match self.logging.level {
111 LogLevel::WARN => tracing::Level::WARN,
112 LogLevel::INFO => tracing::Level::INFO,
113 LogLevel::DEBUG => tracing::Level::DEBUG,
114 LogLevel::TRACE => tracing::Level::TRACE,
115 LogLevel::ERROR | LogLevel::OFF => tracing::Level::ERROR,
116 }
117 }
118
119 #[must_use]
121 pub fn get_host(&self) -> String {
122 self.network.host.clone()
123 }
124
125 #[must_use]
131 #[deprecated(since = "0.0.53", note = "Use try_url_address which returns Result instead of panicking")]
132 pub fn url_address(&self) -> url::Url {
133 self.try_url_address()
134 .unwrap_or_else(|e| panic!("Failed to build URL from config: {}", e))
135 }
136
137 pub fn try_url_address(&self) -> crate::error::Result<url::Url> {
144 let mut url_string = self.network.protocol.clone();
145 url_string.push_str("://");
146 url_string.push_str(&self.network.host);
147 url_string.push(':');
148 url_string.push_str(&self.network.port.to_string());
149 let root = self.root_path.strip_suffix('/').unwrap_or(&self.root_path);
150 url_string.push_str(root);
151
152 url::Url::parse(&url_string).map_err(|e| crate::error::ProductOSServerError::ConfigurationError {
153 component: "url_address".to_string(),
154 message: format!("Failed to parse URL '{}': {}", url_string, e),
155 })
156 }
157
158 #[must_use]
160 pub fn socket_address(&self, port: Option<u16>, default_all: bool) -> SocketAddr {
161 let socket_port = port.unwrap_or(self.network.port);
162 Self::get_socket_address(&self.network.host, socket_port, default_all)
163 }
164
165 #[must_use]
167 pub fn get_socket_address(host: &str, port: u16, default_all: bool) -> SocketAddr {
168 product_os_utilities::ProductOSUtilities::get_socket_address(host, port, default_all)
169 }
170
171 #[must_use]
173 pub fn is_secure(&self) -> bool {
174 self.network.secure
175 }
176
177 #[must_use]
179 pub fn all_insecure(&self) -> bool {
180 self.network.allow_insecure
181 }
182
183 #[must_use]
185 pub fn insecure_port(&self) -> u16 {
186 self.network.insecure_port
187 }
188
189 #[must_use]
191 pub fn insecure_force_secure(&self) -> bool {
192 self.network.insecure_force_secure
193 }
194
195 #[must_use]
197 pub fn is_compression_gzip(&self) -> bool {
198 self.compression.as_ref().map_or(false, |c| c.gzip)
199 }
200
201 #[must_use]
203 pub fn is_compression_deflate(&self) -> bool {
204 self.compression.as_ref().map_or(false, |c| c.deflate)
205 }
206
207 #[must_use]
209 pub fn is_compression_brotli(&self) -> bool {
210 self.compression.as_ref().map_or(false, |c| c.brotli)
211 }
212}
213
214impl ProductOSConfig for ServerConfig {
215 const SECTION_KEY: &'static str = "server";
216
217 fn validate(&self) -> Result<(), ConfigError> {
218 let mut errors = Vec::new();
219 if self.network.host.is_empty() && self.network.port == 0 {
220 errors.push("network host and port must be configured".into());
221 }
222 if errors.is_empty() {
223 Ok(())
224 } else {
225 Err(ConfigError::ValidationError(errors))
226 }
227 }
228}
229
230
231#[derive(Clone, Debug, Deserialize, Serialize, Default)]
233#[serde(rename_all = "camelCase")]
234pub struct Network {
235 pub protocol: String,
237 pub secure: bool,
239 pub host: String,
241 pub port: u16,
243 pub listen_all_interfaces: bool,
245 pub allow_insecure: bool,
247 pub insecure_port: u16,
249 pub insecure_use_different_port: bool,
251 pub insecure_force_secure: bool,
253}
254
255#[derive(Clone, Debug, Deserialize, Serialize, Default)]
257#[serde(rename_all = "camelCase")]
258pub struct Certificate {
259 pub files: Option<CertificateFiles>,
261 pub file_kind: Option<CertificateFilesKind>,
263 pub entries: Option<Vec<BTreeMap<String, serde_json::Value>>>,
265 pub names: Option<Vec<String>>,
267 pub serial: Option<u32>,
269 pub valid_for: Option<u32>,
271}
272
273#[derive(Clone, Debug, Deserialize, Serialize, Default)]
275#[serde(rename_all = "camelCase")]
276pub struct CertificateFiles {
277 pub cert_file: String,
279 pub key_file: String,
281}
282
283#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq, Default)]
285#[serde(rename_all = "lowercase")]
286pub enum CertificateFilesKind {
287 #[default]
289 SelfSigned,
290 CA,
292}
293
294#[derive(Clone, Debug, Deserialize, Serialize, Default)]
296#[serde(rename_all = "camelCase")]
297pub struct Compression {
298 pub enable: bool,
300 pub gzip: bool,
302 pub deflate: bool,
304 pub brotli: bool,
306}