Skip to main content

api_gateway/
config.rs

1use serde::{Deserialize, Serialize};
2
3fn default_require_auth_by_default() -> bool {
4    true
5}
6
7fn default_body_limit_bytes() -> usize {
8    16 * 1024 * 1024
9}
10
11/// API gateway configuration - reused from `api_gateway` module
12#[derive(Debug, Clone, Deserialize, Serialize, Default)]
13#[serde(deny_unknown_fields)]
14#[allow(clippy::struct_excessive_bools)]
15pub struct ApiGatewayConfig {
16    pub bind_addr: String,
17    #[serde(default)]
18    pub enable_docs: bool,
19    #[serde(default)]
20    pub cors_enabled: bool,
21    /// Optional detailed CORS configuration
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub cors: Option<CorsConfig>,
24
25    /// `OpenAPI` document metadata
26    #[serde(default)]
27    pub openapi: OpenApiConfig,
28
29    /// Global defaults
30    #[serde(default)]
31    pub defaults: Defaults,
32
33    /// Disable authentication and authorization completely.
34    /// When true, middleware automatically injects a default `SecurityContext` for all requests,
35    /// providing access with no tenant filtering.
36    /// This bypasses all tenant isolation and should only be used for single-user on-premise installations.
37    /// Default: false (authentication required via `AuthN` Resolver).
38    #[serde(default)]
39    pub auth_disabled: bool,
40
41    /// If true, routes without explicit security requirement still require authentication (AuthN-only).
42    #[serde(default = "default_require_auth_by_default")]
43    pub require_auth_by_default: bool,
44
45    /// Optional URL path prefix prepended to every route (e.g. `"/cf"` → `/cf/users`).
46    /// Must start with a leading slash; trailing slashes are stripped automatically.
47    /// Empty string (the default) means no prefix.
48    #[serde(default)]
49    pub prefix_path: String,
50}
51
52#[derive(Debug, Clone, Deserialize, Serialize)]
53#[serde(deny_unknown_fields, default)]
54pub struct Defaults {
55    /// Fallback rate limit when operation does not specify one
56    pub rate_limit: RateLimitDefaults,
57    /// Global request body size limit in bytes
58    pub body_limit_bytes: usize,
59}
60
61impl Default for Defaults {
62    fn default() -> Self {
63        Self {
64            rate_limit: RateLimitDefaults::default(),
65            body_limit_bytes: default_body_limit_bytes(),
66        }
67    }
68}
69
70#[derive(Debug, Clone, Deserialize, Serialize)]
71#[serde(deny_unknown_fields, default)]
72pub struct RateLimitDefaults {
73    pub rps: u32,
74    pub burst: u32,
75    pub in_flight: u32,
76}
77
78impl Default for RateLimitDefaults {
79    fn default() -> Self {
80        Self {
81            rps: 50,
82            burst: 100,
83            in_flight: 64,
84        }
85    }
86}
87
88#[derive(Debug, Clone, Deserialize, Serialize)]
89#[serde(deny_unknown_fields, default)]
90pub struct CorsConfig {
91    /// Allowed origins: `["*"]` means any
92    pub allowed_origins: Vec<String>,
93    /// Allowed HTTP methods, e.g. `["GET","POST","OPTIONS","PUT","DELETE","PATCH"]`
94    pub allowed_methods: Vec<String>,
95    /// Allowed request headers; `["*"]` means any
96    pub allowed_headers: Vec<String>,
97    /// Whether to allow credentials
98    pub allow_credentials: bool,
99    /// Max age for preflight caching in seconds
100    pub max_age_seconds: u64,
101}
102
103impl Default for CorsConfig {
104    fn default() -> Self {
105        Self {
106            allowed_origins: vec!["*".to_owned()],
107            allowed_methods: vec![
108                "GET".to_owned(),
109                "POST".to_owned(),
110                "PUT".to_owned(),
111                "PATCH".to_owned(),
112                "DELETE".to_owned(),
113                "OPTIONS".to_owned(),
114            ],
115            allowed_headers: vec!["*".to_owned()],
116            allow_credentials: false,
117            max_age_seconds: 600,
118        }
119    }
120}
121
122/// `OpenAPI` document metadata configuration
123#[derive(Debug, Clone, Deserialize, Serialize)]
124#[serde(deny_unknown_fields, default)]
125pub struct OpenApiConfig {
126    /// API title shown in `OpenAPI` documentation
127    pub title: String,
128    /// API version
129    pub version: String,
130    /// API description (optional)
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub description: Option<String>,
133}
134
135impl Default for OpenApiConfig {
136    fn default() -> Self {
137        Self {
138            title: "API Documentation".to_owned(),
139            version: "0.1.0".to_owned(),
140            description: None,
141        }
142    }
143}