Compare commits
No commits in common. "63f4975d5a3cbc841439f698a7cea85660fcb78e" and "651f3f4bb7e25d07b86d9cba795a6fd96c23386f" have entirely different histories.
63f4975d5a
...
651f3f4bb7
@ -5,14 +5,7 @@ domain:
|
|||||||
builtins:
|
builtins:
|
||||||
foo:
|
foo:
|
||||||
kind: proxy
|
kind: proxy
|
||||||
url: http://127.0.0.1:9000
|
url: http://ok
|
||||||
request_http_headers:
|
|
||||||
- name: x-foo
|
|
||||||
value: BAR
|
|
||||||
- name: host
|
|
||||||
value: hi
|
|
||||||
- name: user-agent
|
|
||||||
value: ""
|
|
||||||
bar:
|
bar:
|
||||||
kind: git
|
kind: git
|
||||||
url: a
|
url: a
|
||||||
|
25
README.md
25
README.md
@ -64,39 +64,16 @@ domain:
|
|||||||
# url: "https://somewhere.com/some/repo.git"
|
# url: "https://somewhere.com/some/repo.git"
|
||||||
# branch_name: main
|
# branch_name: main
|
||||||
# public: false
|
# public: false
|
||||||
#
|
|
||||||
# # Which protocols to serve the domain on. The given list overwrites the
|
|
||||||
# # default, which is to serve on all available protocols.
|
|
||||||
# #serve_protocols:
|
|
||||||
# #- protocol: http
|
|
||||||
# #- protocol: https
|
|
||||||
|
|
||||||
# An example built-in domain backed by a reverse-proxy to some other
|
# An example built-in domain backed by a reverse-proxy to some other
|
||||||
# web-service. Requests to the backing service will automatically have
|
# web-service. Proxies are currently limited in the following ways:
|
||||||
# X-Forwarded-For and (if HTTPS) X-Forwarded-Proto headers added to them.
|
|
||||||
#
|
|
||||||
# Proxies are currently limited in the following ways:
|
|
||||||
# * url must be to an http endpoint (not https)
|
# * url must be to an http endpoint (not https)
|
||||||
# * dns.resolver_addr is ignored and the system-wide dns is used
|
# * dns.resolver_addr is ignored and the system-wide dns is used
|
||||||
#
|
#
|
||||||
#proxy.example.com:
|
#proxy.example.com:
|
||||||
# kind: proxy
|
# kind: proxy
|
||||||
# url: "http://some.other.service.com"
|
# url: "http://some.other.service.com"
|
||||||
#
|
|
||||||
# # Extra headers to add to requests being proxied
|
|
||||||
# request_http_headers:
|
|
||||||
# - name: Host
|
|
||||||
# value: "yet.another.service.com"
|
|
||||||
# - name: X-HEADER-TO-DELETE
|
|
||||||
# value: ""
|
|
||||||
#
|
|
||||||
# public: false
|
# public: false
|
||||||
#
|
|
||||||
# # Which protocols to serve the domain on. The given list overwrites the
|
|
||||||
# # default, which is to serve on all available protocols.
|
|
||||||
# #serve_protocols:
|
|
||||||
# #- protocol: http
|
|
||||||
# #- protocol: https
|
|
||||||
|
|
||||||
service:
|
service:
|
||||||
|
|
||||||
|
@ -3,9 +3,30 @@ pub mod checker;
|
|||||||
mod config;
|
mod config;
|
||||||
pub mod manager;
|
pub mod manager;
|
||||||
mod name;
|
mod name;
|
||||||
mod settings;
|
|
||||||
pub mod store;
|
pub mod store;
|
||||||
|
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
pub use name::*;
|
pub use name::*;
|
||||||
pub use settings::*;
|
|
||||||
|
use crate::error::unexpected::{self, Mappable};
|
||||||
|
use crate::origin;
|
||||||
|
|
||||||
|
use hex::ToHex;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||||
|
/// Defines how a domain will behave when it is accessed. These are configured by the owner of the
|
||||||
|
/// domain during setup.
|
||||||
|
pub struct Settings {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub origin_descr: origin::Descr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn hash(&self) -> Result<String, unexpected::Error> {
|
||||||
|
let mut h = Sha256::new();
|
||||||
|
serde_json::to_writer(&mut h, self).or_unexpected()?;
|
||||||
|
Ok(h.finalize().encode_hex::<String>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,8 +6,6 @@ use crate::util;
|
|||||||
use std::sync;
|
use std::sync;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
pub type GetSettingsResult = domain::store::GetResult;
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum GetSettingsError {
|
pub enum GetSettingsError {
|
||||||
#[error("not found")]
|
#[error("not found")]
|
||||||
@ -142,7 +140,7 @@ pub type GetAcmeHttp01ChallengeKeyError = acme::manager::GetHttp01ChallengeKeyEr
|
|||||||
|
|
||||||
//#[mockall::automock]
|
//#[mockall::automock]
|
||||||
pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
|
pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
|
||||||
fn get_settings(&self, domain: &domain::Name) -> Result<GetSettingsResult, GetSettingsError>;
|
fn get_settings(&self, domain: &domain::Name) -> Result<domain::Settings, GetSettingsError>;
|
||||||
|
|
||||||
fn get_file<'store>(
|
fn get_file<'store>(
|
||||||
&'store self,
|
&'store self,
|
||||||
@ -237,7 +235,7 @@ impl ManagerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Manager for ManagerImpl {
|
impl Manager for ManagerImpl {
|
||||||
fn get_settings(&self, domain: &domain::Name) -> Result<GetSettingsResult, GetSettingsError> {
|
fn get_settings(&self, domain: &domain::Name) -> Result<domain::Settings, GetSettingsError> {
|
||||||
Ok(self.domain_store.get(domain)?)
|
Ok(self.domain_store.get(domain)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,13 +244,13 @@ impl Manager for ManagerImpl {
|
|||||||
domain: &domain::Name,
|
domain: &domain::Name,
|
||||||
path: &str,
|
path: &str,
|
||||||
) -> Result<util::BoxByteStream, GetFileError> {
|
) -> Result<util::BoxByteStream, GetFileError> {
|
||||||
let settings = self.domain_store.get(domain)?.settings;
|
let config = self.domain_store.get(domain)?;
|
||||||
|
|
||||||
if let origin::Descr::Proxy { .. } = settings.origin_descr {
|
if let origin::Descr::Proxy { .. } = config.origin_descr {
|
||||||
return Err(unexpected::Error::from("origin is proxy, can't serve file").into());
|
return Err(unexpected::Error::from("origin is proxy, can't serve file").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let f = self.origin_store.get_file(&settings.origin_descr, path)?;
|
let f = self.origin_store.get_file(&config.origin_descr, path)?;
|
||||||
Ok(f)
|
Ok(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
use crate::error::unexpected::{self, Mappable};
|
|
||||||
use crate::origin;
|
|
||||||
|
|
||||||
use hex::ToHex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
|
||||||
#[serde(tag = "protocol")]
|
|
||||||
pub enum SettingsServeProtocol {
|
|
||||||
#[serde(rename = "http")]
|
|
||||||
Http,
|
|
||||||
#[serde(rename = "https")]
|
|
||||||
Https,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_serve_protocols() -> Vec<SettingsServeProtocol> {
|
|
||||||
vec![SettingsServeProtocol::Http, SettingsServeProtocol::Https]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
|
||||||
/// Defines how a domain will behave when it is accessed. These are configured by the owner of the
|
|
||||||
/// domain during setup.
|
|
||||||
pub struct Settings {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub origin_descr: origin::Descr,
|
|
||||||
|
|
||||||
#[serde(default = "default_serve_protocols")]
|
|
||||||
pub serve_protocols: Vec<SettingsServeProtocol>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Settings {
|
|
||||||
pub fn hash(&self) -> Result<String, unexpected::Error> {
|
|
||||||
let mut h = Sha256::new();
|
|
||||||
serde_json::to_writer(&mut h, self).or_unexpected()?;
|
|
||||||
Ok(h.finalize().encode_hex::<String>())
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,13 +3,6 @@ use std::{collections, fs, io, path, str::FromStr};
|
|||||||
use crate::domain;
|
use crate::domain;
|
||||||
use crate::error::unexpected::{self, Intoable, Mappable};
|
use crate::error::unexpected::{self, Intoable, Mappable};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct GetResult {
|
|
||||||
pub settings: domain::Settings,
|
|
||||||
pub builtin: bool,
|
|
||||||
pub public: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum GetError {
|
pub enum GetError {
|
||||||
#[error("not found")]
|
#[error("not found")]
|
||||||
@ -30,7 +23,7 @@ pub enum SetError {
|
|||||||
|
|
||||||
#[mockall::automock]
|
#[mockall::automock]
|
||||||
pub trait Store {
|
pub trait Store {
|
||||||
fn get(&self, domain: &domain::Name) -> Result<GetResult, GetError>;
|
fn get(&self, domain: &domain::Name) -> Result<domain::Settings, GetError>;
|
||||||
fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> Result<(), SetError>;
|
fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> Result<(), SetError>;
|
||||||
fn all_domains(&self) -> Result<Vec<domain::Name>, unexpected::Error>;
|
fn all_domains(&self) -> Result<Vec<domain::Name>, unexpected::Error>;
|
||||||
}
|
}
|
||||||
@ -57,7 +50,7 @@ impl FSStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Store for FSStore {
|
impl Store for FSStore {
|
||||||
fn get(&self, domain: &domain::Name) -> Result<GetResult, GetError> {
|
fn get(&self, domain: &domain::Name) -> Result<domain::Settings, GetError> {
|
||||||
let path = self.settings_file_path(domain);
|
let path = self.settings_file_path(domain);
|
||||||
let settings_file = fs::File::open(path.as_path()).map_err(|e| match e.kind() {
|
let settings_file = fs::File::open(path.as_path()).map_err(|e| match e.kind() {
|
||||||
io::ErrorKind::NotFound => GetError::NotFound,
|
io::ErrorKind::NotFound => GetError::NotFound,
|
||||||
@ -68,12 +61,7 @@ impl Store for FSStore {
|
|||||||
|
|
||||||
let settings = serde_json::from_reader(settings_file)
|
let settings = serde_json::from_reader(settings_file)
|
||||||
.map_unexpected_while(|| format!("json parsing {}", path.display()))?;
|
.map_unexpected_while(|| format!("json parsing {}", path.display()))?;
|
||||||
|
Ok(settings)
|
||||||
Ok(GetResult {
|
|
||||||
settings,
|
|
||||||
public: true,
|
|
||||||
builtin: false,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> Result<(), SetError> {
|
fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> Result<(), SetError> {
|
||||||
@ -127,13 +115,9 @@ impl<S: Store> StoreWithBuiltin<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Store> Store for StoreWithBuiltin<S> {
|
impl<S: Store> Store for StoreWithBuiltin<S> {
|
||||||
fn get(&self, domain: &domain::Name) -> Result<GetResult, GetError> {
|
fn get(&self, domain: &domain::Name) -> Result<domain::Settings, GetError> {
|
||||||
if let Some(domain) = self.domains.get(domain) {
|
if let Some(domain) = self.domains.get(domain) {
|
||||||
return Ok(GetResult {
|
return Ok(domain.settings.clone());
|
||||||
settings: domain.settings.clone(),
|
|
||||||
public: domain.public,
|
|
||||||
builtin: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
self.inner.get(domain)
|
self.inner.get(domain)
|
||||||
}
|
}
|
||||||
@ -181,39 +165,26 @@ mod tests {
|
|||||||
url: "bar".to_string(),
|
url: "bar".to_string(),
|
||||||
branch_name: "baz".to_string(),
|
branch_name: "baz".to_string(),
|
||||||
},
|
},
|
||||||
serve_protocols: vec![domain::SettingsServeProtocol::Http],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
store.get(&domain),
|
store.get(&domain),
|
||||||
Err::<GetResult, GetError>(GetError::NotFound)
|
Err::<domain::Settings, GetError>(GetError::NotFound)
|
||||||
));
|
));
|
||||||
|
|
||||||
store.set(&domain, &settings).expect("set");
|
store.set(&domain, &settings).expect("set");
|
||||||
assert_eq!(
|
assert_eq!(settings, store.get(&domain).expect("settings retrieved"));
|
||||||
GetResult {
|
|
||||||
settings,
|
|
||||||
public: true,
|
|
||||||
builtin: false,
|
|
||||||
},
|
|
||||||
store.get(&domain).expect("settings retrieved")
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_settings = domain::Settings {
|
let new_settings = domain::Settings {
|
||||||
origin_descr: Descr::Git {
|
origin_descr: Descr::Git {
|
||||||
url: "BAR".to_string(),
|
url: "BAR".to_string(),
|
||||||
branch_name: "BAZ".to_string(),
|
branch_name: "BAZ".to_string(),
|
||||||
},
|
},
|
||||||
serve_protocols: vec![],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
store.set(&domain, &new_settings).expect("set");
|
store.set(&domain, &new_settings).expect("set");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
GetResult {
|
new_settings,
|
||||||
settings: new_settings,
|
|
||||||
public: true,
|
|
||||||
builtin: false,
|
|
||||||
},
|
|
||||||
store.get(&domain).expect("settings retrieved")
|
store.get(&domain).expect("settings retrieved")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -79,9 +79,7 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (domain, builtin_domain) in &config.domain.builtins {
|
for (domain, builtin_domain) in &config.domain.builtins {
|
||||||
if let domani::origin::Descr::Proxy { ref url, .. } =
|
if let domani::origin::Descr::Proxy { ref url } = builtin_domain.settings.origin_descr {
|
||||||
builtin_domain.settings.origin_descr
|
|
||||||
{
|
|
||||||
if let Err(e) = domani::origin::proxy::validate_proxy_url(url) {
|
if let Err(e) = domani::origin::proxy::validate_proxy_url(url) {
|
||||||
panic!("invalid config for builtin domain {domain}: {e}");
|
panic!("invalid config for builtin domain {domain}: {e}");
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,6 @@ use hex::ToHex;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
pub struct DescrProxyHttpHeader {
|
|
||||||
pub name: String,
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[serde(tag = "kind")]
|
#[serde(tag = "kind")]
|
||||||
/// A unique description of an origin, from where a domain might be served.
|
/// A unique description of an origin, from where a domain might be served.
|
||||||
@ -16,10 +10,7 @@ pub enum Descr {
|
|||||||
Git { url: String, branch_name: String },
|
Git { url: String, branch_name: String },
|
||||||
|
|
||||||
#[serde(rename = "proxy")]
|
#[serde(rename = "proxy")]
|
||||||
Proxy {
|
Proxy { url: String },
|
||||||
url: String,
|
|
||||||
request_http_headers: Vec<DescrProxyHttpHeader>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Descr {
|
impl Descr {
|
||||||
@ -38,17 +29,9 @@ impl Descr {
|
|||||||
h_update(url);
|
h_update(url);
|
||||||
h_update(branch_name);
|
h_update(branch_name);
|
||||||
}
|
}
|
||||||
Descr::Proxy {
|
Descr::Proxy { url } => {
|
||||||
url,
|
|
||||||
request_http_headers,
|
|
||||||
} => {
|
|
||||||
h_update("proxy");
|
h_update("proxy");
|
||||||
h_update(url);
|
h_update(url);
|
||||||
for h in request_http_headers {
|
|
||||||
h_update("header");
|
|
||||||
h_update(&h.name);
|
|
||||||
h_update(&h.value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::error::unexpected::{self, Mappable};
|
use crate::error::unexpected::{self, Mappable};
|
||||||
use crate::{domain, origin};
|
use crate::{domain, origin};
|
||||||
use http::header::{HeaderName, HeaderValue};
|
use http::header::HeaderValue;
|
||||||
use std::{net, str::FromStr};
|
use std::{net, str::FromStr};
|
||||||
|
|
||||||
// proxy is a special case because it is so tied to the underlying protocol that a request is
|
// proxy is a special case because it is so tied to the underlying protocol that a request is
|
||||||
@ -30,37 +30,33 @@ pub async fn serve_http_request(
|
|||||||
mut req: hyper::Request<hyper::Body>,
|
mut req: hyper::Request<hyper::Body>,
|
||||||
req_is_https: bool,
|
req_is_https: bool,
|
||||||
) -> unexpected::Result<hyper::Response<hyper::Body>> {
|
) -> unexpected::Result<hyper::Response<hyper::Body>> {
|
||||||
let (proxy_url, request_http_headers) = if let origin::Descr::Proxy {
|
let proxy_url = if let origin::Descr::Proxy { ref url } = settings.origin_descr {
|
||||||
ref url,
|
url
|
||||||
ref request_http_headers,
|
|
||||||
} = settings.origin_descr
|
|
||||||
{
|
|
||||||
(url, request_http_headers)
|
|
||||||
} else {
|
} else {
|
||||||
panic!("non-proxy domain settings passed in: {settings:?}")
|
panic!("non-proxy domain settings passed in: {settings:?}")
|
||||||
};
|
};
|
||||||
|
|
||||||
for header in request_http_headers {
|
let parsed_proxy_url =
|
||||||
let name: HeaderName = header
|
http::Uri::from_str(proxy_url).or_unexpected_while("parsing proxy url {proxy_url}")?;
|
||||||
.name
|
|
||||||
.as_str()
|
|
||||||
.try_into()
|
|
||||||
.map_unexpected_while(|| format!("parsing header name {}", &header.name))?;
|
|
||||||
|
|
||||||
if header.value == "" {
|
// figure out what the host header should be, based on the host[:port] of the proxy_url
|
||||||
req.headers_mut().remove(name);
|
let host = {
|
||||||
continue;
|
let authority = parsed_proxy_url.authority().or_unexpected_while(format!(
|
||||||
}
|
"getting host from proxy url {proxy_url}, there is no host"
|
||||||
|
))?;
|
||||||
|
|
||||||
let value = HeaderValue::from_str(&header.value).map_unexpected_while(|| {
|
let host_and_port;
|
||||||
format!(
|
let mut host = authority.host();
|
||||||
"parsing {} as value for header {}",
|
|
||||||
&header.value, &header.name
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
req.headers_mut().insert(name, value);
|
if let Some(port) = authority.port() {
|
||||||
}
|
host_and_port = format!("{host}:{port}");
|
||||||
|
host = host_and_port.as_str();
|
||||||
|
};
|
||||||
|
|
||||||
|
HeaderValue::from_str(host).or_unexpected()?
|
||||||
|
};
|
||||||
|
|
||||||
|
req.headers_mut().insert("host", host);
|
||||||
|
|
||||||
if req_is_https {
|
if req_is_https {
|
||||||
req.headers_mut()
|
req.headers_mut()
|
||||||
|
@ -165,8 +165,23 @@ impl<'svc> Service {
|
|||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
req_is_https: bool,
|
req_is_https: bool,
|
||||||
) -> Response<Body> {
|
) -> Response<Body> {
|
||||||
let settings = match self.domain_manager.get_settings(&domain) {
|
// first check if the domain is backed by a proxy, and deal with that first
|
||||||
Ok(domain::manager::GetSettingsResult { settings, .. }) => settings,
|
match self.domain_manager.get_settings(&domain) {
|
||||||
|
Ok(settings) => {
|
||||||
|
if let origin::Descr::Proxy { .. } = settings.origin_descr {
|
||||||
|
return origin::proxy::serve_http_request(
|
||||||
|
&settings,
|
||||||
|
client_ip,
|
||||||
|
req,
|
||||||
|
req_is_https,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
self.internal_error(format!("proxying {domain}: {e}").as_str())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// fall out of match
|
||||||
|
}
|
||||||
Err(domain::manager::GetSettingsError::NotFound) => {
|
Err(domain::manager::GetSettingsError::NotFound) => {
|
||||||
return self.render_error_page(404, "Domain not found");
|
return self.render_error_page(404, "Domain not found");
|
||||||
}
|
}
|
||||||
@ -175,29 +190,8 @@ impl<'svc> Service {
|
|||||||
format!("failed to fetch settings for domain {domain}: {e}").as_str(),
|
format!("failed to fetch settings for domain {domain}: {e}").as_str(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let allowed = settings.serve_protocols.iter().any(|p| match p {
|
|
||||||
domain::SettingsServeProtocol::Http => !req_is_https,
|
|
||||||
domain::SettingsServeProtocol::Https => req_is_https,
|
|
||||||
});
|
|
||||||
|
|
||||||
if !allowed {
|
|
||||||
return self.render_error_page(
|
|
||||||
421,
|
|
||||||
"The requested protocol is not supported by this domain",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the domain is backed by a proxy then that is handled specially.
|
|
||||||
if let origin::Descr::Proxy { .. } = settings.origin_descr {
|
|
||||||
return origin::proxy::serve_http_request(&settings, client_ip, req, req_is_https)
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
self.internal_error(format!("proxying {domain}: {e}").as_str())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut path_owned;
|
let mut path_owned;
|
||||||
let path = req.uri().path();
|
let path = req.uri().path();
|
||||||
|
|
||||||
@ -266,24 +260,7 @@ impl<'svc> Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let settings = match self.domain_manager.get_settings(&args.domain) {
|
let settings = match self.domain_manager.get_settings(&args.domain) {
|
||||||
Ok(domain::manager::GetSettingsResult {
|
Ok(settings) => Some(settings),
|
||||||
settings,
|
|
||||||
public,
|
|
||||||
builtin,
|
|
||||||
}) => match (public, builtin) {
|
|
||||||
(false, _) => None,
|
|
||||||
(true, false) => Some(settings),
|
|
||||||
(true, true) => {
|
|
||||||
return self.render_error_page(
|
|
||||||
403,
|
|
||||||
format!(
|
|
||||||
"Settings for domain {} cannot be viewed or modified",
|
|
||||||
args.domain
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(domain::manager::GetSettingsError::NotFound) => None,
|
Err(domain::manager::GetSettingsError::NotFound) => None,
|
||||||
Err(domain::manager::GetSettingsError::Unexpected(e)) => {
|
Err(domain::manager::GetSettingsError::Unexpected(e)) => {
|
||||||
return self.internal_error(
|
return self.internal_error(
|
||||||
|
@ -22,17 +22,17 @@ automatically updated too!</p>
|
|||||||
|
|
||||||
<form method="{{ form_method }}" action="/domain_init.html">
|
<form method="{{ form_method }}" action="/domain_init.html">
|
||||||
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
||||||
<input name="domain_setting_origin_descr_kind" type="hidden" value="git" />
|
<input name="config_origin_descr_kind" type="hidden" value="git" />
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Git Repository</legend>
|
<legend>Git Repository</legend>
|
||||||
<p>
|
<p>
|
||||||
<label>
|
<label>
|
||||||
URL (HTTPS only):
|
URL (HTTPS only):
|
||||||
<input name="domain_setting_origin_descr_git_url"
|
<input name="config_origin_descr_git_url"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="https://example.com/some_repo.git"
|
placeholder="https://example.com/some_repo.git"
|
||||||
value="{{ data.settings.url }}"
|
value="{{ data.settings.origin_descr.Git.url }}"
|
||||||
required />
|
required />
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
@ -40,10 +40,10 @@ automatically updated too!</p>
|
|||||||
<p>
|
<p>
|
||||||
<label>
|
<label>
|
||||||
Branch name:
|
Branch name:
|
||||||
<input name="domain_setting_origin_descr_git_branch_name"
|
<input name="config_origin_descr_git_branch_name"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="main / master / etc..."
|
placeholder="main / master / etc..."
|
||||||
value="{{ data.settings.branch_name }}"
|
value="{{ data.settings.origin_descr.Git.branch_name }}"
|
||||||
required />
|
required />
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
|
@ -1,45 +1,28 @@
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use serde::{de, Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{domain, error::unexpected, origin};
|
use crate::{domain, error::unexpected, origin};
|
||||||
|
|
||||||
fn deserialize_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
|
|
||||||
where
|
|
||||||
D: de::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let s: &str = de::Deserialize::deserialize(deserializer)?;
|
|
||||||
|
|
||||||
match s {
|
|
||||||
"true" => Ok(true),
|
|
||||||
"false" => Ok(false),
|
|
||||||
_ => Err(de::Error::unknown_variant(s, &["true", "false"])),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
pub struct FlatDomainSettings {
|
pub struct FlatDomainSettings {
|
||||||
domain_setting_origin_descr_kind: String,
|
domain_setting_origin_descr_kind: Option<String>,
|
||||||
|
|
||||||
domain_setting_origin_descr_git_url: Option<String>,
|
domain_setting_origin_descr_git_url: Option<String>,
|
||||||
domain_setting_origin_descr_git_branch_name: Option<String>,
|
domain_setting_origin_descr_git_branch_name: Option<String>,
|
||||||
|
|
||||||
domain_setting_origin_descr_proxy_url: Option<String>,
|
domain_setting_origin_descr_proxy_url: Option<String>,
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
#[serde(deserialize_with = "deserialize_bool")]
|
|
||||||
domain_setting_serve_protocol_http: bool,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
#[serde(deserialize_with = "deserialize_bool")]
|
|
||||||
domain_setting_serve_protocol_https: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<FlatDomainSettings> for domain::Settings {
|
impl TryFrom<FlatDomainSettings> for domain::Settings {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
fn try_from(v: FlatDomainSettings) -> Result<Self, Self::Error> {
|
fn try_from(v: FlatDomainSettings) -> Result<Self, Self::Error> {
|
||||||
let origin_descr = match v.domain_setting_origin_descr_kind.as_str() {
|
let origin_descr = match v
|
||||||
|
.domain_setting_origin_descr_kind
|
||||||
|
.unwrap_or("".to_string())
|
||||||
|
.as_str()
|
||||||
|
{
|
||||||
"git" => Ok(origin::Descr::Git {
|
"git" => Ok(origin::Descr::Git {
|
||||||
url: v
|
url: v
|
||||||
.domain_setting_origin_descr_git_url
|
.domain_setting_origin_descr_git_url
|
||||||
@ -52,20 +35,7 @@ impl TryFrom<FlatDomainSettings> for domain::Settings {
|
|||||||
_ => Err("invalid domain_setting_origin_descr_kind".to_string()),
|
_ => Err("invalid domain_setting_origin_descr_kind".to_string()),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let mut serve_protocols = Vec::<domain::SettingsServeProtocol>::default();
|
Ok(Self { origin_descr })
|
||||||
|
|
||||||
if v.domain_setting_serve_protocol_http {
|
|
||||||
serve_protocols.push(domain::SettingsServeProtocol::Http);
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.domain_setting_serve_protocol_https {
|
|
||||||
serve_protocols.push(domain::SettingsServeProtocol::Https);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
origin_descr,
|
|
||||||
serve_protocols,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,32 +43,16 @@ impl TryFrom<domain::Settings> for FlatDomainSettings {
|
|||||||
type Error = unexpected::Error;
|
type Error = unexpected::Error;
|
||||||
|
|
||||||
fn try_from(v: domain::Settings) -> Result<Self, Self::Error> {
|
fn try_from(v: domain::Settings) -> Result<Self, Self::Error> {
|
||||||
let mut res = FlatDomainSettings::default();
|
|
||||||
|
|
||||||
match v.origin_descr {
|
match v.origin_descr {
|
||||||
origin::Descr::Git { url, branch_name } => {
|
origin::Descr::Git { url, branch_name } => Ok(FlatDomainSettings {
|
||||||
res.domain_setting_origin_descr_kind = "git".to_string();
|
domain_setting_origin_descr_kind: Some("git".to_string()),
|
||||||
res.domain_setting_origin_descr_git_url = Some(url);
|
domain_setting_origin_descr_git_url: Some(url),
|
||||||
res.domain_setting_origin_descr_git_branch_name = Some(branch_name);
|
domain_setting_origin_descr_git_branch_name: Some(branch_name),
|
||||||
}
|
..Default::default()
|
||||||
origin::Descr::Proxy { .. } => {
|
}),
|
||||||
return Err(unexpected::Error::from(
|
origin::Descr::Proxy { .. } => Err(unexpected::Error::from(
|
||||||
"proxy origins not supported for forms",
|
"proxy origins not supported for forms",
|
||||||
));
|
)),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for serve_protocol in v.serve_protocols {
|
|
||||||
match serve_protocol {
|
|
||||||
domain::SettingsServeProtocol::Http => {
|
|
||||||
res.domain_setting_serve_protocol_http = true
|
|
||||||
}
|
|
||||||
domain::SettingsServeProtocol::Https => {
|
|
||||||
res.domain_setting_serve_protocol_https = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user