use std::{ fs, path::Path, time::{Duration, Instant}, }; use anyhow::{anyhow, Context, Result}; use chrono::Utc; use log::info; use rouille::Request; use crate::Rewriter; pub struct Server { rewriter: Rewriter, // Where we write temporary files workspace_dir: String, // Directory to read configs from template_dir: String, // The symlink that is updated config_dir: String, } impl Server { pub fn new( rewriter: Rewriter, workspace_dir: String, template_dir: String, config_dir: String, ) -> Self { Self { rewriter, workspace_dir, template_dir, config_dir, } } pub fn cleanup(&mut self) -> Result { let cleaned_up = self.rewriter.cleanup(); if cleaned_up { self.generate_config()?; } Ok(cleaned_up) } pub fn register(&mut self, _request: &Request, ip: &str) -> Result<()> { info!("Registering {} as a handler", ip); let cleanup_time = Instant::now() .checked_add(Duration::from_secs(60 * 5)) .ok_or(anyhow!("failed to convert time"))?; self.rewriter.add_replacement(ip.to_string(), cleanup_time); self.generate_config()?; Ok(()) } pub fn unregister(&mut self, _request: &Request, ip: &str) -> Result<()> { info!("Deregistering {} as a handler", ip); self.rewriter.remove_replacement(ip); self.generate_config()?; Ok(()) } pub fn generate_config(&self) -> Result<()> { // Create a new directory in our workspace let now = Utc::now(); // Writes into 2020/01/01/ // This will fail if we have multiple requests per second let path = Path::new(&self.workspace_dir).join(&now.format("%Y/%m/%d/%s").to_string()); let path = path.as_os_str().to_str().unwrap(); fs::create_dir_all(path).with_context(|| "creating directory")?; self.rewriter .rewrite_folder(&self.template_dir, path) .with_context(|| "generating configs")?; // Finally, symlink it to the output folder; only support Linux for now let symlink = Path::new(&self.workspace_dir).join("symlink.tmp"); std::os::unix::fs::symlink(path, &symlink).with_context(|| "creating symlink")?; fs::rename(symlink, &self.config_dir).with_context(|| "renaming symlink")?; Ok(()) } }