Skip to main content

jokoway_core/config/
models.rs

1pub use pingora::server::configuration::ServerConf;
2use serde::{Deserialize, Deserializer, Serialize};
3use std::collections::HashMap;
4use std::sync::Arc;
5
6#[cfg(feature = "api")]
7use utoipa::ToSchema;
8
9#[derive(Debug, Serialize, Deserialize)]
10pub struct RootConfig {
11    pub jokoway: JokowayConfig,
12    pub pingora: Option<ServerConf>,
13}
14
15#[derive(Debug, Serialize, Deserialize, Clone, Default)]
16#[serde(deny_unknown_fields)]
17pub struct HttpServerOptionsConfig {
18    pub keepalive_request_limit: Option<u32>,
19    #[serde(default = "default_false")]
20    pub h2c: bool,
21    #[serde(default = "default_true")]
22    pub allow_connect_method_proxying: bool,
23}
24
25fn default_false() -> bool {
26    false
27}
28
29#[derive(Debug, Serialize, Deserialize, Clone, Default)]
30pub struct JokowayConfig {
31    pub http_listen: String,
32    pub https_listen: Option<String>,
33    pub api: Option<ApiSettings>,
34    pub tls: Option<TlsSettings>,
35    pub http_server_options: Option<HttpServerOptionsConfig>,
36
37    #[serde(default)]
38    pub upstreams: Vec<Upstream>,
39    #[serde(default)]
40    pub services: Vec<Arc<Service>>,
41    #[serde(default)]
42    pub dns: Option<DnsSettings>,
43
44    // Allow for extra configuration that might not be strictly defined
45    #[serde(flatten)]
46    pub extra: HashMap<String, serde_yaml::Value>,
47}
48
49#[derive(Debug, Serialize, Deserialize, Clone, Default)]
50#[serde(deny_unknown_fields)]
51pub struct ApiSettings {
52    pub listen: String,
53    #[serde(default, deserialize_with = "deserialize_basic_auth_credentials")]
54    pub basic_auth: Option<Vec<BasicAuth>>,
55    #[serde(default, deserialize_with = "deserialize_api_keys")]
56    pub api_keys: Option<Vec<String>>,
57    pub rate_limit: Option<RateLimit>,
58    pub openapi: Option<OpenApiSettings>,
59}
60
61#[derive(Deserialize)]
62#[serde(untagged)]
63enum BasicAuthCredentials {
64    Single(BasicAuth),
65    Multiple(Vec<BasicAuth>),
66}
67
68#[derive(Deserialize)]
69#[serde(untagged)]
70enum ApiKeyCredentials {
71    Single(String),
72    Multiple(Vec<String>),
73}
74
75fn deserialize_basic_auth_credentials<'de, D>(
76    deserializer: D,
77) -> Result<Option<Vec<BasicAuth>>, D::Error>
78where
79    D: Deserializer<'de>,
80{
81    let value = Option::<BasicAuthCredentials>::deserialize(deserializer)?;
82    Ok(value.map(|v| match v {
83        BasicAuthCredentials::Single(auth) => vec![auth],
84        BasicAuthCredentials::Multiple(auths) => auths,
85    }))
86}
87
88fn deserialize_api_keys<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
89where
90    D: Deserializer<'de>,
91{
92    let value = Option::<ApiKeyCredentials>::deserialize(deserializer)?;
93    Ok(value.map(|v| match v {
94        ApiKeyCredentials::Single(key) => vec![key],
95        ApiKeyCredentials::Multiple(keys) => keys,
96    }))
97}
98
99#[derive(Debug, Serialize, Deserialize, Clone, Default)]
100#[serde(deny_unknown_fields)]
101pub struct BasicAuth {
102    pub username: String,
103    pub password: String,
104}
105
106#[derive(Debug, Serialize, Deserialize, Clone, Default)]
107#[serde(deny_unknown_fields)]
108pub struct RateLimit {
109    pub requests_per_second: u32,
110    pub burst: u32,
111}
112
113#[derive(Debug, Serialize, Deserialize, Clone, Default)]
114#[serde(deny_unknown_fields)]
115pub struct DnsSettings {
116    pub nameservers: Option<Vec<String>>,
117    pub timeout: Option<u64>,
118    pub attempts: Option<usize>,
119    pub strategy: Option<String>,
120    pub cache_size: Option<usize>,
121    #[serde(default = "default_true")]
122    pub use_hosts_file: bool,
123    #[serde(default = "default_true")]
124    pub system_conf: bool,
125}
126
127fn default_true() -> bool {
128    true
129}
130
131#[derive(Debug, Serialize, Deserialize, Clone, Default)]
132#[cfg_attr(feature = "api", derive(ToSchema))]
133#[serde(deny_unknown_fields)]
134pub struct TcpKeepaliveConfig {
135    pub idle: Option<u64>,
136    pub interval: Option<u64>,
137    pub count: Option<u32>,
138    #[cfg(target_os = "linux")]
139    pub user_timeout: Option<u64>,
140}
141
142#[derive(Debug, Serialize, Deserialize, Clone, Default)]
143#[cfg_attr(feature = "api", derive(ToSchema))]
144#[serde(deny_unknown_fields)]
145pub struct PeerOptions {
146    pub connection_timeout: Option<u64>,
147    pub read_timeout: Option<u64>,
148    pub idle_timeout: Option<u64>,
149    pub write_timeout: Option<u64>,
150    pub verify_cert: Option<bool>,
151    pub verify_hostname: Option<bool>,
152    pub alternative_cn: Option<String>,
153    /// ALPN protocol negotiation: "h1", "h2", or "h1h2"
154    pub alpn: Option<String>,
155    pub tcp_keepalive: Option<TcpKeepaliveConfig>,
156    pub tcp_recv_buf: Option<usize>,
157    pub dscp: Option<u8>,
158    pub h2_ping_interval: Option<u64>,
159    pub max_h2_streams: Option<usize>,
160    pub allow_h1_response_invalid_content_length: Option<bool>,
161    pub extra_proxy_headers: Option<std::collections::HashMap<String, String>>,
162    pub curves: Option<String>,
163    pub second_keyshare: Option<bool>,
164    pub tcp_fast_open: Option<bool>,
165    pub cacert: Option<String>,
166    pub client_cert: Option<String>,
167    pub client_key: Option<String>,
168    pub sni: Option<String>,
169}
170
171#[derive(Debug, Serialize, Deserialize, Clone, Default)]
172#[serde(deny_unknown_fields)]
173pub struct TlsSettings {
174    pub cacert: Option<String>,
175    pub server_cert: Option<String>,
176    pub server_key: Option<String>,
177    pub sans: Option<Vec<String>>,
178    pub cipher_suites: Option<Vec<String>>,
179}
180
181fn default_openapi_title() -> String {
182    "Jokoway API".to_string()
183}
184
185fn default_openapi_description() -> String {
186    "Jokoway Management API".to_string()
187}
188
189fn default_openapi_root_path() -> String {
190    "/docs".to_string()
191}
192
193#[derive(Debug, Serialize, Deserialize, Clone)]
194#[serde(deny_unknown_fields)]
195pub struct OpenApiSettings {
196    #[serde(default = "default_openapi_title")]
197    pub title: String,
198    #[serde(default = "default_openapi_description")]
199    pub description: String,
200    #[serde(default = "default_openapi_root_path")]
201    pub root_path: String,
202}
203
204impl Default for OpenApiSettings {
205    fn default() -> Self {
206        Self {
207            title: default_openapi_title(),
208            description: default_openapi_description(),
209            root_path: default_openapi_root_path(),
210        }
211    }
212}
213
214#[derive(Debug, Serialize, Deserialize, Clone, Default)]
215#[cfg_attr(feature = "api", derive(ToSchema))]
216#[serde(deny_unknown_fields)]
217pub struct Upstream {
218    pub name: String,
219    pub peer_options: Option<PeerOptions>,
220    #[serde(default)]
221    pub servers: Vec<UpstreamServer>,
222    pub health_check: Option<HealthCheckConfig>,
223    pub update_frequency: Option<u64>,
224}
225
226#[derive(Debug, Serialize, Deserialize, Clone, Default)]
227#[cfg_attr(feature = "api", derive(ToSchema))]
228#[serde(deny_unknown_fields)]
229pub struct UpstreamServer {
230    pub host: String,
231    pub weight: Option<u32>,
232    pub tls: Option<bool>,
233    pub peer_options: Option<PeerOptions>,
234}
235
236#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
237#[cfg_attr(feature = "api", derive(ToSchema))]
238pub enum ServiceProtocol {
239    #[serde(rename = "http")]
240    Http,
241    #[serde(rename = "https")]
242    Https,
243    #[serde(rename = "ws")]
244    Ws,
245    #[serde(rename = "wss")]
246    Wss,
247    #[serde(rename = "grpc")]
248    Grpc,
249    #[serde(rename = "grpcs")]
250    Grpcs,
251}
252
253#[derive(Debug, Serialize, Deserialize, Clone, Default)]
254#[cfg_attr(feature = "api", derive(ToSchema))]
255#[serde(deny_unknown_fields)]
256pub struct Service {
257    pub name: String,
258    pub host: String,
259    pub protocols: Vec<ServiceProtocol>,
260    pub max_retries: Option<u32>,
261    #[serde(default)]
262    pub routes: Vec<Route>,
263}
264
265#[derive(Debug, Serialize, Deserialize, Clone, Default)]
266#[cfg_attr(feature = "api", derive(ToSchema))]
267#[serde(deny_unknown_fields)]
268pub struct Route {
269    pub name: String,
270    pub rule: String,
271    pub priority: Option<i32>,
272    pub max_retries: Option<u32>,
273    pub request_transformer: Option<String>,
274    pub response_transformer: Option<String>,
275}
276
277// Health Check Configuration
278
279fn default_health_check_interval() -> u64 {
280    10
281}
282
283fn default_health_check_timeout() -> u64 {
284    3
285}
286
287fn default_unhealthy_threshold() -> u32 {
288    3
289}
290
291fn default_healthy_threshold() -> u32 {
292    2
293}
294
295#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
296#[cfg_attr(feature = "api", derive(ToSchema))]
297#[serde(rename_all = "lowercase")]
298pub enum HealthCheckType {
299    Http,
300    Https,
301    Tcp,
302}
303
304#[derive(Debug, Serialize, Deserialize, Clone)]
305#[cfg_attr(feature = "api", derive(ToSchema))]
306#[serde(deny_unknown_fields)]
307pub struct HealthCheckConfig {
308    #[serde(rename = "type")]
309    pub check_type: HealthCheckType,
310
311    #[serde(default = "default_health_check_interval")]
312    pub interval: u64, // seconds
313
314    #[serde(default = "default_health_check_timeout")]
315    pub timeout: u64, // seconds
316
317    #[serde(default = "default_unhealthy_threshold")]
318    pub unhealthy_threshold: u32,
319
320    #[serde(default = "default_healthy_threshold")]
321    pub healthy_threshold: u32,
322
323    // HTTP/HTTPS specific
324    pub path: Option<String>,
325    pub method: Option<String>, // GET, HEAD, POST
326    pub expected_status: Option<Vec<u16>>,
327    pub headers: Option<HashMap<String, String>>,
328}