1use schemars::JsonSchema;
2use serde::Deserialize;
3use std::net::{IpAddr, Ipv4Addr};
4use summer::config::Configurable;
5use tracing::Level;
6
7summer::submit_config_schema!("web", WebConfig);
8
9#[cfg(feature = "socket_io")]
10summer::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")]
21 #[serde(default)]
22 pub(crate) openapi: OpenApiConfig,
23 pub(crate) middlewares: Option<Middlewares>,
24}
25
26#[derive(Debug, Clone, JsonSchema, Deserialize)]
27pub struct ServerConfig {
28 #[serde(default = "default_binding")]
29 pub binding: IpAddr,
30 #[serde(default = "default_port")]
31 pub port: u16,
32 #[serde(default)]
33 pub connect_info: bool,
34 #[serde(default = "default_true")]
35 pub graceful: bool,
36 #[serde(default)]
37 pub global_prefix: String,
38}
39
40#[cfg(feature = "openapi")]
41#[derive(Debug, Clone, JsonSchema, Deserialize)]
42pub struct OpenApiConfig {
43 #[serde(default = "default_doc_prefix")]
44 pub(crate) doc_prefix: String,
45 #[serde(default)]
46 pub(crate) info: aide::openapi::Info,
47}
48
49#[cfg(feature = "openapi")]
50impl Default for OpenApiConfig {
51 fn default() -> Self {
52 Self {
53 doc_prefix: default_doc_prefix(),
54 info: aide::openapi::Info::default(),
55 }
56 }
57}
58
59fn normalize_prefix(prefix: &mut String) {
62 if !prefix.is_empty() {
63 if !prefix.starts_with('/') {
64 prefix.insert(0, '/');
65 }
66 while prefix.ends_with('/') {
67 prefix.pop();
68 }
69 }
70}
71
72impl WebConfig {
73 pub(crate) fn normalize_prefixes(&mut self) {
74 normalize_prefix(&mut self.server.global_prefix);
75 #[cfg(feature = "openapi")]
76 normalize_prefix(&mut self.openapi.doc_prefix);
77 }
78}
79
80fn default_binding() -> IpAddr {
81 IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
82}
83
84fn default_port() -> u16 {
85 8080
86}
87
88fn default_true() -> bool {
89 true
90}
91
92#[cfg(feature = "openapi")]
93fn default_doc_prefix() -> String {
94 "/docs".into()
95}
96
97#[derive(Debug, Clone, JsonSchema, Deserialize)]
99pub struct Middlewares {
100 pub compression: Option<EnableMiddleware>,
102 pub limit_payload: Option<LimitPayloadMiddleware>,
104 pub logger: Option<TraceLoggerMiddleware>,
107 pub catch_panic: Option<EnableMiddleware>,
109 pub timeout_request: Option<TimeoutRequestMiddleware>,
111 pub cors: Option<CorsMiddleware>,
113 #[serde(rename = "static")]
115 pub static_assets: Option<StaticAssetsMiddleware>,
116}
117
118#[derive(Debug, Clone, JsonSchema, Deserialize)]
120pub struct StaticAssetsMiddleware {
121 pub enable: bool,
123 #[serde(default = "bool::default")]
125 pub must_exist: bool,
126 #[serde(default = "default_fallback")]
129 pub fallback: String,
130 #[serde(default = "bool::default")]
132 pub precompressed: bool,
133 #[serde(default = "default_assets_uri")]
135 pub uri: String,
136 #[serde(default = "default_assets_path")]
138 pub path: String,
139}
140
141#[derive(Debug, Clone, JsonSchema, Deserialize)]
143pub struct TraceLoggerMiddleware {
144 pub enable: bool,
146 pub level: LogLevel,
147}
148
149#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
150pub enum LogLevel {
151 #[serde(rename = "trace")]
153 Trace,
154 #[serde(rename = "debug")]
156 Debug,
157 #[serde(rename = "info")]
159 #[default]
160 Info,
161 #[serde(rename = "warn")]
163 Warn,
164 #[serde(rename = "error")]
166 Error,
167}
168
169#[allow(clippy::from_over_into)]
170impl Into<Level> for LogLevel {
171 fn into(self) -> Level {
172 match self {
173 Self::Trace => Level::TRACE,
174 Self::Debug => Level::DEBUG,
175 Self::Info => Level::INFO,
176 Self::Warn => Level::WARN,
177 Self::Error => Level::ERROR,
178 }
179 }
180}
181
182#[derive(Debug, Clone, JsonSchema, Deserialize)]
184pub struct CorsMiddleware {
185 pub enable: bool,
187 pub allow_origins: Option<Vec<String>>,
189 pub allow_headers: Option<Vec<String>>,
191 pub allow_methods: Option<Vec<String>>,
193 pub max_age: Option<u64>,
195}
196
197#[derive(Debug, Clone, JsonSchema, Deserialize)]
199pub struct TimeoutRequestMiddleware {
200 pub enable: bool,
202 pub timeout: u64,
204}
205
206#[derive(Debug, Clone, JsonSchema, Deserialize)]
208pub struct LimitPayloadMiddleware {
209 pub enable: bool,
211 pub body_limit: String,
213}
214
215#[derive(Debug, PartialEq, Clone, JsonSchema, Deserialize)]
218pub struct EnableMiddleware {
219 pub enable: bool,
221}
222
223fn default_assets_path() -> String {
224 "static".to_string()
225}
226
227fn default_assets_uri() -> String {
228 "/static".to_string()
229}
230
231fn default_fallback() -> String {
232 "index.html".to_string()
233}
234
235#[cfg(feature = "socket_io")]
237#[derive(Debug, Configurable, JsonSchema, Deserialize)]
238#[config_prefix = "socket_io"]
239pub struct SocketIOConfig {
240 #[serde(default = "default_namespace")]
241 pub default_namespace: String,
242}
243
244#[cfg(feature = "socket_io")]
245fn default_namespace() -> String {
246 "/".to_string()
247}