use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct ProxyConfig {
#[serde(default)]
pub server: ServerConfig,
#[serde(default)]
pub tls: TlsConfig,
#[serde(default)]
pub rate_limit: RateLimitDefaults,
#[serde(default)]
pub routes: Vec<Route>,
#[serde(default)]
pub upstreams: Vec<Upstream>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ServerConfig {
#[serde(default = "default_http_host")]
pub http_host: String,
#[serde(default = "default_http_port")]
pub http_port: u16,
#[serde(default = "default_https_host")]
pub https_host: String,
#[serde(default = "default_https_port")]
pub https_port: u16,
#[serde(default)]
pub https_enabled: bool,
#[serde(default = "default_true")]
pub database_config: bool,
pub config_file: Option<String>,
#[serde(default)]
pub watch_config_file: bool,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
http_host: default_http_host(),
http_port: default_http_port(),
https_host: default_https_host(),
https_port: default_https_port(),
https_enabled: false,
database_config: true,
config_file: None,
watch_config_file: false,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TlsConfig {
#[serde(default)]
pub acme_enabled: bool,
#[serde(default = "default_acme_directory")]
pub acme_directory: String,
pub acme_email: Option<String>,
#[serde(default = "default_cert_dir")]
pub cert_dir: String,
#[serde(default)]
pub acme_staging: bool,
}
impl Default for TlsConfig {
fn default() -> Self {
Self {
acme_enabled: false,
acme_directory: default_acme_directory(),
acme_email: None,
cert_dir: default_cert_dir(),
acme_staging: false,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RateLimitDefaults {
#[serde(default = "default_rate_limit_requests")]
pub requests: u32,
#[serde(default = "default_rate_limit_window")]
pub window_secs: u32,
#[serde(default = "default_burst")]
pub burst: u32,
}
impl Default for RateLimitDefaults {
fn default() -> Self {
Self {
requests: default_rate_limit_requests(),
window_secs: default_rate_limit_window(),
burst: default_burst(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Route {
pub id: Option<Uuid>,
pub name: String,
pub description: Option<String>,
#[serde(default, rename = "match")]
pub match_: RouteMatch,
#[serde(default = "default_priority")]
pub priority: i32,
pub upstream: String,
#[serde(default)]
pub strip_path: bool,
#[serde(default)]
pub add_headers: HashMap<String, String>,
#[serde(default)]
pub remove_headers: Vec<String>,
pub rate_limit: Option<RouteRateLimit>,
#[serde(default = "default_timeout")]
pub timeout_secs: u32,
#[serde(default)]
pub retry_count: u32,
#[serde(default = "default_true")]
pub enabled: bool,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct RouteMatch {
pub host: Option<String>,
pub path: Option<String>,
#[serde(default)]
pub path_type: PathMatchType,
#[serde(default)]
pub headers: HashMap<String, String>,
pub methods: Option<Vec<String>>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum PathMatchType {
#[default]
Prefix,
Exact,
Regex,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RouteRateLimit {
pub requests: u32,
pub window_secs: u32,
#[serde(default)]
pub key: RateLimitKey,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RateLimitKey {
#[default]
ClientIp,
Header(String),
Route,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Upstream {
pub id: Option<Uuid>,
pub name: String,
pub description: Option<String>,
#[serde(default)]
pub lb_strategy: LoadBalanceStrategy,
#[serde(default)]
pub backends: Vec<Backend>,
#[serde(default)]
pub health_check: HealthCheckConfig,
#[serde(default = "default_true")]
pub enabled: bool,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum LoadBalanceStrategy {
#[default]
RoundRobin,
LeastConnections,
Weighted,
Random,
Sticky,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Backend {
pub id: Option<Uuid>,
pub address: String,
#[serde(default = "default_scheme")]
pub scheme: String,
#[serde(default = "default_weight")]
pub weight: u32,
#[serde(default = "default_true")]
pub enabled: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HealthCheckConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_health_path")]
pub path: String,
#[serde(default = "default_health_interval")]
pub interval_secs: u32,
#[serde(default = "default_health_timeout")]
pub timeout_secs: u32,
#[serde(default = "default_healthy_threshold")]
pub healthy_threshold: u32,
#[serde(default = "default_unhealthy_threshold")]
pub unhealthy_threshold: u32,
}
impl Default for HealthCheckConfig {
fn default() -> Self {
Self {
enabled: true,
path: default_health_path(),
interval_secs: default_health_interval(),
timeout_secs: default_health_timeout(),
healthy_threshold: default_healthy_threshold(),
unhealthy_threshold: default_unhealthy_threshold(),
}
}
}
fn default_http_host() -> String {
"0.0.0.0".into()
}
fn default_http_port() -> u16 {
8080
}
fn default_https_host() -> String {
"0.0.0.0".into()
}
fn default_https_port() -> u16 {
8443
}
fn default_true() -> bool {
true
}
fn default_acme_directory() -> String {
"https://acme-v02.api.letsencrypt.org/directory".into()
}
fn default_cert_dir() -> String {
"./certs".into()
}
fn default_rate_limit_requests() -> u32 {
1000
}
fn default_rate_limit_window() -> u32 {
60
}
fn default_burst() -> u32 {
50
}
fn default_priority() -> i32 {
100
}
fn default_timeout() -> u32 {
30
}
fn default_scheme() -> String {
"http".into()
}
fn default_weight() -> u32 {
100
}
fn default_health_path() -> String {
"/health".into()
}
fn default_health_interval() -> u32 {
10
}
fn default_health_timeout() -> u32 {
5
}
fn default_healthy_threshold() -> u32 {
2
}
fn default_unhealthy_threshold() -> u32 {
3
}
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct AcmeConfig {
#[serde(default)]
pub enabled: bool,
pub email: Option<String>,
#[serde(default)]
pub staging: bool,
#[serde(default)]
pub domains: Vec<String>,
}