use blocking::unblock; use futures_lite::*; use once_cell::sync::Lazy; use sled::Db; use std::time::Duration; use tide::{ log::{self, LogMiddleware}, sessions::{MemoryStore, Session, SessionMiddleware}, }; include!(concat!(env!("OUT_DIR"), "/templates.rs")); mod iptables; mod middleware; mod rules; mod session; mod startup; use self::{ middleware::AuthMiddleware, rules::Rule, startup::{Config, Interfaces, ServerState}, templates::statics::StaticFile, }; static CONFIG: Lazy = Lazy::new(|| Config::read().unwrap()); static SERVER: Lazy = Lazy::new(|| ServerState::init(&CONFIG).unwrap()); static INTERFACES: Lazy = Lazy::new(|| { let interfaces = Interfaces::init_blocking(&CONFIG).unwrap(); if !SERVER.debug { interfaces.reset_blocking().unwrap(); } interfaces }); static DB: Lazy = Lazy::new(|| sled::open("router-db-0-34-3").unwrap()); static QS: Lazy = Lazy::new(|| serde_qs::Config::new(3, false)); async fn rules_page(_: tide::Request<()>) -> tide::Result { let mut html = Vec::new(); let rules = unblock(move || rules::read(&DB)).await?; templates::rules(&mut html, &rules)?; Ok(tide::Response::builder(200) .body(html) .content_type( "text/html;charset=utf-8" .parse::() .unwrap(), ) .build()) } async fn save_rule(mut req: tide::Request<()>) -> tide::Result { let body_string = req.body_string().await?; let rule: Rule = QS.deserialize_str(&body_string)?; rules::save(&DB, &rule).await?; if !SERVER.debug { rules::apply(&INTERFACES, rule).await?; } Ok(to_rules_page()) } async fn delete_rule(req: tide::Request<()>) -> tide::Result { let id = req.param("id")?; let rule = rules::delete(&DB, id).await?; if !SERVER.debug { rules::unset(&INTERFACES, rule).await?; } Ok(to_rules_page()) } #[derive(serde::Deserialize)] struct LoginForm { username: String, password: String, } async fn login(mut req: tide::Request<()>) -> tide::Result { tide::log::debug!("LOGIN"); let body_string = req.body_string().await?; let login_form: LoginForm = QS.deserialize_str(&body_string)?; session::login(&DB, login_form.username, login_form.password)?; let session = req.session_mut(); session.insert("logged_in", true)?; if let Some(path) = session.get("return_to") { let path: String = path; // tell the compiler what type i want it to be session.remove("return_to"); Ok(to_custom(&path)) } else { Ok(to_home()) } } async fn login_page(req: tide::Request<()>) -> tide::Result { tide::log::debug!("LOGIN PAGE"); let session = req.session(); let logged_in: bool = session.get("logged_in").unwrap_or(false); if logged_in { tide::log::debug!("TO HOME"); return Ok(to_home()); } let mut html = Vec::new(); templates::login(&mut html)?; Ok(tide::Response::builder(200) .body(html) .content_type( "text/html;charset=utf-8" .parse::() .unwrap(), ) .build()) } async fn home_page(_: tide::Request<()>) -> tide::Result { let mut html = Vec::new(); templates::home_html(&mut html, INTERFACES.external.ip.to_string())?; Ok(tide::Response::builder(200) .body(html) .content_type( "text/html;charset=utf-8" .parse::() .unwrap(), ) .build()) } fn to_custom(location: &str) -> tide::Response { tide::Response::builder(302) .header("Location", location) .header("Cache-Control", "no-store") .build() } fn to_home() -> tide::Response { to_custom("/") } fn to_login(session: &mut Session, from: &str) -> tide::Result { session.insert("return_to", from)?; Ok(to_custom("/login")) } fn to_rules_page() -> tide::Response { to_custom("/rules") } async fn statics(req: tide::Request<()>) -> tide::Result { let file: String = req.param("file")?; if let Some(data) = StaticFile::get(&file) { Ok(tide::Response::builder(200) .header("Content-Type", data.mime.to_string()) .body(data.content) .build()) } else { Ok(tide::Response::builder(404).build()) } } fn main() -> Result<(), anyhow::Error> { future::block_on(async { println!("Hello, world!"); log::with_level(log::LevelFilter::Debug); session::create_admin(&DB).await?; if !SERVER.debug { rules::apply_all(&DB, &INTERFACES).await?; } let mut app = tide::new(); app.with( SessionMiddleware::new(MemoryStore::new(), &SERVER.secret) .with_cookie_name("router.cookie") .with_session_ttl(Some(Duration::from_secs(60 * 15))), ); app.with(LogMiddleware::new()); app.at("/static/:file").get(statics); app.at("/login").get(login_page).post(login); app.at("/rules") .with(AuthMiddleware) .get(rules_page) .post(save_rule) .nest({ let mut app = tide::new(); app.at("/:id").get(delete_rule); app }); app.at("/").get(home_page); if SERVER.debug { app.listen("127.0.0.1:8080").await?; } else { let listeners: Vec = INTERFACES .internal .iter() .map(|info| format!("{}:80", info.ip)) .collect(); app.listen(listeners).await?; } Ok(()) as Result<(), anyhow::Error> })?; Ok(()) }