spring_web/
config.rs

1use schemars::JsonSchema;
2use serde::Deserialize;
3use spring::config::Configurable;
4use std::net::{IpAddr, Ipv4Addr};
5use tracing::Level;
6
7spring::submit_config_schema!("web", WebConfig);
8
9#[cfg(feature = "socket_io")]
10spring::submit_config_schema!("socket_io", SocketIOConfig);
11
12/// spring-web Config
13#[derive(Debug, Configurable, JsonSchema, Deserialize)]
14#[config_prefix = "web"]
15pub struct WebConfig {
16    #[serde(flatten)]
17    pub(crate) server: ServerConfig,
18    #[cfg(feature = "openapi")]
19    pub(crate) openapi: OpenApiConfig,
20    pub(crate) middlewares: Option<Middlewares>,
21}
22
23#[derive(Debug, Clone, JsonSchema, Deserialize)]
24pub struct ServerConfig {
25    #[serde(default = "default_binding")]
26    pub(crate) binding: IpAddr,
27    #[serde(default = "default_port")]
28    pub(crate) port: u16,
29    #[serde(default)]
30    pub(crate) connect_info: bool,
31    #[serde(default = "default_true")]
32    pub(crate) graceful: bool,
33    #[serde(default)]
34    pub(crate) global_prefix: String,
35}
36
37#[cfg(feature = "openapi")]
38#[derive(Debug, Clone, JsonSchema, Deserialize)]
39pub struct OpenApiConfig {
40    #[serde(default = "default_doc_prefix")]
41    pub(crate) doc_prefix: String,
42    #[serde(default)]
43    pub(crate) info: aide::openapi::Info,
44}
45
46fn default_binding() -> IpAddr {
47    IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
48}
49
50fn default_port() -> u16 {
51    8080
52}
53
54fn default_true() -> bool {
55    true
56}
57
58#[cfg(feature = "openapi")]
59fn default_doc_prefix() -> String {
60    "/docs".into()
61}
62
63/// Server middleware configuration structure.
64#[derive(Debug, Clone, JsonSchema, Deserialize)]
65pub struct Middlewares {
66    /// Middleware that enable compression for the response.
67    pub compression: Option<EnableMiddleware>,
68    /// Middleware that limit the payload request.
69    pub limit_payload: Option<LimitPayloadMiddleware>,
70    /// Middleware that improve the tracing logger and adding trace id for each
71    /// request.
72    pub logger: Option<TraceLoggerMiddleware>,
73    /// catch any code panic and log the error.
74    pub catch_panic: Option<EnableMiddleware>,
75    /// Setting a global timeout for the requests
76    pub timeout_request: Option<TimeoutRequestMiddleware>,
77    /// Setting cors configuration
78    pub cors: Option<CorsMiddleware>,
79    /// Serving static assets
80    #[serde(rename = "static")]
81    pub static_assets: Option<StaticAssetsMiddleware>,
82}
83
84/// Static asset middleware configuration
85#[derive(Debug, Clone, JsonSchema, Deserialize)]
86pub struct StaticAssetsMiddleware {
87    /// toggle enable
88    pub enable: bool,
89    /// Check that assets must exist on disk
90    #[serde(default = "bool::default")]
91    pub must_exist: bool,
92    /// Fallback page for a case when no asset exists (404). Useful for SPA
93    /// (single page app) where routes are virtual.
94    #[serde(default = "default_fallback")]
95    pub fallback: String,
96    /// Enable `precompressed_gzip`
97    #[serde(default = "bool::default")]
98    pub precompressed: bool,
99    /// Uri for the assets
100    #[serde(default = "default_assets_uri")]
101    pub uri: String,
102    /// Path for the assets
103    #[serde(default = "default_assets_path")]
104    pub path: String,
105}
106
107/// CORS middleware configuration
108#[derive(Debug, Clone, JsonSchema, Deserialize)]
109pub struct TraceLoggerMiddleware {
110    /// toggle enable
111    pub enable: bool,
112    pub level: LogLevel,
113}
114
115#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
116pub enum LogLevel {
117    /// The "trace" level.
118    #[serde(rename = "trace")]
119    Trace,
120    /// The "debug" level.
121    #[serde(rename = "debug")]
122    Debug,
123    /// The "info" level.
124    #[serde(rename = "info")]
125    #[default]
126    Info,
127    /// The "warn" level.
128    #[serde(rename = "warn")]
129    Warn,
130    /// The "error" level.
131    #[serde(rename = "error")]
132    Error,
133}
134
135#[allow(clippy::from_over_into)]
136impl Into<Level> for LogLevel {
137    fn into(self) -> Level {
138        match self {
139            Self::Trace => Level::TRACE,
140            Self::Debug => Level::DEBUG,
141            Self::Info => Level::INFO,
142            Self::Warn => Level::WARN,
143            Self::Error => Level::ERROR,
144        }
145    }
146}
147
148/// CORS middleware configuration
149#[derive(Debug, Clone, JsonSchema, Deserialize)]
150pub struct CorsMiddleware {
151    /// toggle enable
152    pub enable: bool,
153    /// Allow origins
154    pub allow_origins: Option<Vec<String>>,
155    /// Allow headers
156    pub allow_headers: Option<Vec<String>>,
157    /// Allow methods
158    pub allow_methods: Option<Vec<String>>,
159    /// Max age
160    pub max_age: Option<u64>,
161}
162
163/// Timeout middleware configuration
164#[derive(Debug, Clone, JsonSchema, Deserialize)]
165pub struct TimeoutRequestMiddleware {
166    /// toggle enable
167    pub enable: bool,
168    /// Timeout request in milliseconds
169    pub timeout: u64,
170}
171
172/// Limit payload size middleware configuration
173#[derive(Debug, Clone, JsonSchema, Deserialize)]
174pub struct LimitPayloadMiddleware {
175    /// toggle enable
176    pub enable: bool,
177    /// Body limit. for example: 5mb
178    pub body_limit: String,
179}
180
181/// A generic middleware configuration that can be enabled or
182/// disabled.
183#[derive(Debug, PartialEq, Clone, JsonSchema, Deserialize)]
184pub struct EnableMiddleware {
185    /// toggle enable
186    pub enable: bool,
187}
188
189fn default_assets_path() -> String {
190    "static".to_string()
191}
192
193fn default_assets_uri() -> String {
194    "/static".to_string()
195}
196
197fn default_fallback() -> String {
198    "index.html".to_string()
199}
200
201/// SocketIO configuration
202#[cfg(feature = "socket_io")]
203#[derive(Debug, Configurable, JsonSchema, Deserialize)]
204#[config_prefix = "socket_io"]
205pub struct SocketIOConfig {
206    #[serde(default = "default_namespace")]
207    pub default_namespace: String,
208}
209
210#[cfg(feature = "socket_io")]
211fn default_namespace() -> String {
212    "/".to_string()
213}