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#[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#[derive(Debug, Clone, JsonSchema, Deserialize)]
65pub struct Middlewares {
66 pub compression: Option<EnableMiddleware>,
68 pub limit_payload: Option<LimitPayloadMiddleware>,
70 pub logger: Option<TraceLoggerMiddleware>,
73 pub catch_panic: Option<EnableMiddleware>,
75 pub timeout_request: Option<TimeoutRequestMiddleware>,
77 pub cors: Option<CorsMiddleware>,
79 #[serde(rename = "static")]
81 pub static_assets: Option<StaticAssetsMiddleware>,
82}
83
84#[derive(Debug, Clone, JsonSchema, Deserialize)]
86pub struct StaticAssetsMiddleware {
87 pub enable: bool,
89 #[serde(default = "bool::default")]
91 pub must_exist: bool,
92 #[serde(default = "default_fallback")]
95 pub fallback: String,
96 #[serde(default = "bool::default")]
98 pub precompressed: bool,
99 #[serde(default = "default_assets_uri")]
101 pub uri: String,
102 #[serde(default = "default_assets_path")]
104 pub path: String,
105}
106
107#[derive(Debug, Clone, JsonSchema, Deserialize)]
109pub struct TraceLoggerMiddleware {
110 pub enable: bool,
112 pub level: LogLevel,
113}
114
115#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
116pub enum LogLevel {
117 #[serde(rename = "trace")]
119 Trace,
120 #[serde(rename = "debug")]
122 Debug,
123 #[serde(rename = "info")]
125 #[default]
126 Info,
127 #[serde(rename = "warn")]
129 Warn,
130 #[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#[derive(Debug, Clone, JsonSchema, Deserialize)]
150pub struct CorsMiddleware {
151 pub enable: bool,
153 pub allow_origins: Option<Vec<String>>,
155 pub allow_headers: Option<Vec<String>>,
157 pub allow_methods: Option<Vec<String>>,
159 pub max_age: Option<u64>,
161}
162
163#[derive(Debug, Clone, JsonSchema, Deserialize)]
165pub struct TimeoutRequestMiddleware {
166 pub enable: bool,
168 pub timeout: u64,
170}
171
172#[derive(Debug, Clone, JsonSchema, Deserialize)]
174pub struct LimitPayloadMiddleware {
175 pub enable: bool,
177 pub body_limit: String,
179}
180
181#[derive(Debug, PartialEq, Clone, JsonSchema, Deserialize)]
184pub struct EnableMiddleware {
185 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#[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}