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