use std::error::Error; use std::fmt; use std::io; use std::ops::Deref; use command_runner::CommandRunner; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; use symbols::file::File as FileSymbol; use resources::Resource; #[derive(Debug)] pub enum NginxServerError { ExecError(E), GenericError } impl From for NginxServerError { fn from(err: io::Error) -> NginxServerError { NginxServerError::ExecError(err) } } impl Error for NginxServerError { fn description(&self) -> &str { match self { &NginxServerError::ExecError(ref e) => e.description(), &NginxServerError::GenericError => "Generic error" } } fn cause(&self) -> Option<&Error> { match self { &NginxServerError::ExecError(ref e) => Some(e), _ => None } } } impl fmt::Display for NginxServerError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{}", self.description()) } } pub struct NginxServer<'a, C> where C: Deref { command_runner: &'a CommandRunner, file: FileSymbol>, } use std::borrow::Cow; impl<'a> NginxServer<'a, String> { pub fn server_config(domain: &str, content: &str) -> String { format!("server {{ listen 80; server_name {0}; include \"snippets/acme-challenge.conf\"; location / {{ # Redirect all HTTP links to the matching HTTPS page return 301 https://$host$request_uri; }} }} server {{ listen 443 ssl http2; server_name {0}; include \"snippets/acme-challenge.conf\"; ssl_certificate /etc/ssl/local_certs/{0}.chained.crt; ssl_certificate_key /etc/ssl/private/{0}.key; add_header Strict-Transport-Security \"max-age=31536000\"; {1} }} ", domain, content) } pub fn new_redir(domain: &'a str, target: &'a str, command_runner: &'a CommandRunner) -> Self { let content = NginxServer::server_config(domain, &format!("location / {{ return 301 $scheme://{}$request_uri; }}", target)); NginxServer::new(domain, content, command_runner) } pub fn new_proxy(domain: &'a str, socket_path: &'a str, static_path: &'a str, command_runner: &'a CommandRunner) -> Self { let proxy_content = format!("location / {{ try_files $uri @proxy; }} location @proxy {{ include fastcgi_params; proxy_pass http://unix:{}:; proxy_redirect off; }}", socket_path); let content = NginxServer::server_config(domain, &format!(" root {}; {} ", static_path, proxy_content)); NginxServer::new(domain, content, command_runner) } pub fn new_php(domain: &'a str, socket_path: &'a str, static_path: &'a str, command_runner: &'a CommandRunner) -> Self { let content = NginxServer::server_config(domain, &format!(" root {}; index index.html index.php; location ~ [^/]\\.php(/|$) {{ fastcgi_pass unix:{}; include \"snippets/fastcgi-php.conf\"; }} ", static_path, socket_path)); NginxServer::new(domain, content, command_runner) } pub fn new_static(domain: &'a str, static_path: &'a str, command_runner: &'a CommandRunner) -> Self { let content = NginxServer::server_config(domain, &format!(" root {}; try_files $uri $uri/ $uri.html =404; ", static_path)); NginxServer::new(domain, content, command_runner) } pub fn new(domain: &'a str, content: String, command_runner: &'a CommandRunner) -> Self { let file_path: Cow = Cow::from(String::from("/etc/nginx/sites-enabled/") + domain); NginxServer { command_runner: command_runner, file: FileSymbol::new(file_path, content) } } } impl<'a, C> Symbol for NginxServer<'a, C> where C: Deref { fn target_reached(&self) -> Result> { if !try!(self.file.target_reached()) { return Ok(false); } // TODO: Could try to find out if the server is in the live config Ok(true) } fn execute(&self) -> Result<(), Box> { try!(self.file.execute()); self.command_runner.run_successfully("systemctl", &["reload-or-restart", "nginx"]) } fn get_prerequisites(&self) -> Vec { self.file.get_prerequisites() } fn as_action<'b>(&'b self, runner: &'b SymbolRunner) -> Box { Box::new(SymbolAction::new(runner, self)) } fn into_action<'b>(self: Box, runner: &'b SymbolRunner) -> Box where Self: 'b { Box::new(OwnedSymbolAction::new(runner, *self)) } } impl<'a, C> fmt::Display for NginxServer<'a, C> where C: Deref { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(),fmt::Error>{ write!(f, "Nginx server config") } }