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}