spring_web/
config.rs

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