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}
34
35#[cfg(feature = "openapi")]
36#[derive(Debug, Clone, JsonSchema, Deserialize)]
37pub struct OpenApiConfig {
38 #[serde(default = "default_doc_prefix")]
39 pub(crate) doc_prefix: String,
40 #[serde(default)]
41 pub(crate) info: aide::openapi::Info,
42}
43
44fn default_binding() -> IpAddr {
45 IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
46}
47
48fn default_port() -> u16 {
49 8080
50}
51
52fn default_true() -> bool {
53 true
54}
55
56#[cfg(feature = "openapi")]
57fn default_doc_prefix() -> String {
58 "/docs".into()
59}
60
61#[derive(Debug, Clone, JsonSchema, Deserialize)]
63pub struct Middlewares {
64 pub compression: Option<EnableMiddleware>,
66 pub limit_payload: Option<LimitPayloadMiddleware>,
68 pub logger: Option<TraceLoggerMiddleware>,
71 pub catch_panic: Option<EnableMiddleware>,
73 pub timeout_request: Option<TimeoutRequestMiddleware>,
75 pub cors: Option<CorsMiddleware>,
77 #[serde(rename = "static")]
79 pub static_assets: Option<StaticAssetsMiddleware>,
80}
81
82#[derive(Debug, Clone, JsonSchema, Deserialize)]
84pub struct StaticAssetsMiddleware {
85 pub enable: bool,
87 #[serde(default = "bool::default")]
89 pub must_exist: bool,
90 #[serde(default = "default_fallback")]
93 pub fallback: String,
94 #[serde(default = "bool::default")]
96 pub precompressed: bool,
97 #[serde(default = "default_assets_uri")]
99 pub uri: String,
100 #[serde(default = "default_assets_path")]
102 pub path: String,
103}
104
105#[derive(Debug, Clone, JsonSchema, Deserialize)]
107pub struct TraceLoggerMiddleware {
108 pub enable: bool,
110 pub level: LogLevel,
111}
112
113#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
114pub enum LogLevel {
115 #[serde(rename = "trace")]
117 Trace,
118 #[serde(rename = "debug")]
120 Debug,
121 #[serde(rename = "info")]
123 #[default]
124 Info,
125 #[serde(rename = "warn")]
127 Warn,
128 #[serde(rename = "error")]
130 Error,
131}
132
133#[allow(clippy::from_over_into)]
134impl Into<Level> for LogLevel {
135 fn into(self) -> Level {
136 match self {
137 Self::Trace => Level::TRACE,
138 Self::Debug => Level::DEBUG,
139 Self::Info => Level::INFO,
140 Self::Warn => Level::WARN,
141 Self::Error => Level::ERROR,
142 }
143 }
144}
145
146#[derive(Debug, Clone, JsonSchema, Deserialize)]
148pub struct CorsMiddleware {
149 pub enable: bool,
151 pub allow_origins: Option<Vec<String>>,
153 pub allow_headers: Option<Vec<String>>,
155 pub allow_methods: Option<Vec<String>>,
157 pub max_age: Option<u64>,
159}
160
161#[derive(Debug, Clone, JsonSchema, Deserialize)]
163pub struct TimeoutRequestMiddleware {
164 pub enable: bool,
166 pub timeout: u64,
168}
169
170#[derive(Debug, Clone, JsonSchema, Deserialize)]
172pub struct LimitPayloadMiddleware {
173 pub enable: bool,
175 pub body_limit: String,
177}
178
179#[derive(Debug, PartialEq, Clone, JsonSchema, Deserialize)]
182pub struct EnableMiddleware {
183 pub enable: bool,
185}
186
187fn default_assets_path() -> String {
188 "static".to_string()
189}
190
191fn default_assets_uri() -> String {
192 "/static".to_string()
193}
194
195fn default_fallback() -> String {
196 "index.html".to_string()
197}
198
199#[cfg(feature = "socket_io")]
201#[derive(Debug, Configurable, JsonSchema, Deserialize)]
202#[config_prefix = "socket_io"]
203pub struct SocketIOConfig {
204 #[serde(default = "default_namespace")]
205 pub default_namespace: String,
206}
207
208#[cfg(feature = "socket_io")]
209fn default_namespace() -> String {
210 "/".to_string()
211}