taxy_api/
proxy.rs

1use crate::error::Error;
2use crate::vhost::VirtualHost;
3use crate::{id::ShortId, port::UpstreamServer};
4use serde_default::DefaultFromSerde;
5use serde_derive::{Deserialize, Serialize};
6use std::fmt;
7use std::str::FromStr;
8use url::Url;
9use utoipa::ToSchema;
10
11#[derive(Debug, DefaultFromSerde, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
12pub struct Proxy {
13    #[serde(default = "default_active", skip_serializing_if = "is_true")]
14    pub active: bool,
15    #[serde(default, skip_serializing_if = "String::is_empty")]
16    pub name: String,
17    #[serde(default)]
18    #[schema(example = json!(["c56yqmqcvpmp49n14s2lexxl"]))]
19    pub ports: Vec<ShortId>,
20    #[serde(flatten, default = "default_kind")]
21    #[schema(inline)]
22    pub kind: ProxyKind,
23}
24
25fn default_active() -> bool {
26    true
27}
28
29fn default_kind() -> ProxyKind {
30    ProxyKind::Http(HttpProxy::default())
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
34#[serde(tag = "protocol", rename_all = "snake_case")]
35pub enum ProxyKind {
36    Tcp(TcpProxy),
37    Http(HttpProxy),
38    Udp(UdpProxy),
39}
40
41#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
42pub struct TcpProxy {
43    #[serde(default, skip_serializing_if = "Vec::is_empty")]
44    pub upstream_servers: Vec<UpstreamServer>,
45}
46
47#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
48pub struct UdpProxy {
49    #[serde(default, skip_serializing_if = "Vec::is_empty")]
50    pub upstream_servers: Vec<UpstreamServer>,
51}
52
53#[derive(Debug, DefaultFromSerde, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
54pub struct HttpProxy {
55    #[serde(default, skip_serializing_if = "Vec::is_empty")]
56    #[schema(value_type = [String], example = json!(["example.com"]))]
57    pub vhosts: Vec<VirtualHost>,
58    pub routes: Vec<Route>,
59    #[serde(default = "upgrade_insecure_default", skip_serializing_if = "is_true")]
60    pub upgrade_insecure: bool,
61}
62
63fn upgrade_insecure_default() -> bool {
64    true
65}
66
67fn is_true(b: &bool) -> bool {
68    *b
69}
70
71#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
72#[serde(rename_all = "snake_case")]
73pub enum ProxyState {
74    Active,
75    Inactive,
76    #[default]
77    Unknown,
78}
79
80#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
81pub struct ProxyStatus {
82    pub state: ProxyState,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
86pub struct ProxyEntry {
87    pub id: ShortId,
88    #[schema(inline)]
89    #[serde(flatten)]
90    pub proxy: Proxy,
91}
92
93impl From<(ShortId, Proxy)> for ProxyEntry {
94    fn from((id, proxy): (ShortId, Proxy)) -> Self {
95        Self { id, proxy }
96    }
97}
98
99impl From<ProxyEntry> for (ShortId, Proxy) {
100    fn from(entry: ProxyEntry) -> Self {
101        (entry.id, entry.proxy)
102    }
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
106pub struct Route {
107    #[schema(example = "/")]
108    #[serde(default = "default_route_path")]
109    pub path: String,
110    pub servers: Vec<Server>,
111}
112
113fn default_route_path() -> String {
114    "/".to_owned()
115}
116
117#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
118pub struct Server {
119    #[schema(value_type = String, example = "https://example.com/api")]
120    pub url: ServerUrl,
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema)]
124#[serde(transparent)]
125#[schema(value_type = String)]
126pub struct ServerUrl(pub Url);
127
128impl ServerUrl {
129    pub fn hostname(&self) -> Option<&str> {
130        self.0.host_str()
131    }
132
133    pub fn authority(&self) -> Option<String> {
134        Some(format!(
135            "{}:{}",
136            self.hostname()?,
137            self.0.port_or_known_default().unwrap_or_default()
138        ))
139    }
140}
141
142impl<'de> serde::Deserialize<'de> for ServerUrl {
143    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144    where
145        D: serde::Deserializer<'de>,
146    {
147        let url = String::deserialize(deserializer)?;
148        ServerUrl::from_str(&url).map_err(serde::de::Error::custom)
149    }
150}
151
152impl From<ServerUrl> for Url {
153    fn from(url: ServerUrl) -> Self {
154        url.0
155    }
156}
157
158impl FromStr for ServerUrl {
159    type Err = Error;
160
161    fn from_str(s: &str) -> Result<Self, Self::Err> {
162        Url::from_str(s)
163            .ok()
164            .map(ServerUrl)
165            .filter(|url| url.authority().is_some())
166            .ok_or_else(|| Error::InvalidServerUrl { url: s.into() })
167    }
168}
169
170impl TryFrom<Url> for ServerUrl {
171    type Error = url::ParseError;
172
173    fn try_from(url: Url) -> Result<Self, Self::Error> {
174        Ok(ServerUrl(url))
175    }
176}
177
178impl fmt::Display for ServerUrl {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        self.0.fmt(f)
181    }
182}