conductor/env/nginx_bind_le/src/nginx.rs

133 lines
4.2 KiB
Rust

/*
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::cell::RefCell;
use std::error::Error;
use std::path::{Path, PathBuf};
use tera::*;
use tokio::fs;
use tokio::io::AsyncWriteExt;
use tokio::process::Command;
use crate::templates::*;
pub struct Nginx;
impl Nginx {
pub async fn reload() -> MyResult<()> {
Command::new("sudo")
.arg("nginx")
.arg("-s")
.arg("reload")
.spawn()?
.wait()
.await?;
Ok(())
}
pub async fn status() -> bool {
async fn run_async_cmd(cmd: &mut Command) -> bool {
if let Ok(mut child) = cmd.spawn() {
if let Ok(res) = child.wait().await {
return res.success();
}
}
false
}
run_async_cmd(Command::new("sudo").arg("nginx").arg("-t")).await
}
pub async fn new_site(
hostname: &str,
path: &str,
config: Option<libconfig::Config>,
) -> MyResult<()> {
let config = CreateSite::new(hostname, path, config);
let contents = config.render();
let staging = Self::get_staging(hostname);
let prod = Self::get_prod(hostname);
let mut file = fs::File::create(&staging).await?;
file.write_all(contents.as_bytes()).await?;
file.sync_all().await?;
fs::symlink(&staging, &prod).await?;
Self::reload().await
}
fn get_staging(hostname: &str) -> PathBuf {
Path::new(NGINX_STAGING_CONFIG_PATH).join(hostname)
}
fn get_prod(hostname: &str) -> PathBuf {
Path::new(NGINX_PRODUCTION_CONFIG_PATH).join(hostname)
}
pub fn site_exists(hostname: &str) -> bool {
Self::get_prod(hostname).exists()
}
pub async fn rm_site(hostname: &str) -> MyResult<()> {
let staging = Self::get_staging(hostname);
let prod = Self::get_prod(hostname);
fs::remove_file(&prod).await?;
fs::remove_file(&staging).await?;
Self::reload().await
}
pub fn env_exists() -> bool {
let prod = Path::new(NGINX_PRODUCTION_CONFIG_PATH);
let staging = Path::new(NGINX_STAGING_CONFIG_PATH);
prod.exists() && prod.is_dir() && staging.exists() && staging.is_dir()
}
}
pub struct CreateSite {
ctx: RefCell<Context>,
}
pub const CREATE_SITE: TemplateFile = TemplateFile::new("create_site", "nginx/create-site.j2");
pub const CREATE_SITE_FRAGMENT: TemplateFile =
TemplateFile::new("new_site_frag", "nginx/_new_site.fragement.j2");
pub const HOSTNAME_KEY: &str = "hostname";
pub const DOMAINS_KEY: &str = "domains";
pub const PATH_KEY: &str = "path";
pub const REDIRECTS_KEY: &str = "redirects";
pub const NGINX_STAGING_CONFIG_PATH: &str = "/etc/librepages/nginx/sites-available/";
pub const NGINX_PRODUCTION_CONFIG_PATH: &str = "/etc/librepages/nginx/sites-enabled/";
type MyResult<T> = std::result::Result<T, Box<dyn Error>>;
impl CreateSite {
fn new(hostname: &str, path: &str, config: Option<libconfig::Config>) -> Self {
let ctx = RefCell::new(context());
ctx.borrow_mut().insert(HOSTNAME_KEY, hostname);
ctx.borrow_mut().insert(PATH_KEY, path);
if let Some(config) = config {
ctx.borrow_mut().insert(REDIRECTS_KEY, &config.redirects);
ctx.borrow_mut().insert(DOMAINS_KEY, &config.domains);
}
Self { ctx }
}
fn render(&self) -> String {
TEMPLATES
.render(CREATE_SITE.name, &self.ctx.borrow())
.unwrap()
}
}