232 lines
5.7 KiB
Rust
232 lines
5.7 KiB
Rust
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<Config> = Lazy::new(|| Config::read().unwrap());
|
|
|
|
static SERVER: Lazy<ServerState> = Lazy::new(|| ServerState::init(&CONFIG).unwrap());
|
|
|
|
static INTERFACES: Lazy<Interfaces> = Lazy::new(|| {
|
|
let interfaces = Interfaces::init_blocking(&CONFIG).unwrap();
|
|
if !SERVER.debug {
|
|
interfaces.reset_blocking().unwrap();
|
|
}
|
|
interfaces
|
|
});
|
|
|
|
static DB: Lazy<Db> = Lazy::new(|| sled::open("router-db-0-34-3").unwrap());
|
|
|
|
static QS: Lazy<serde_qs::Config> = 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::<tide::http::Mime>()
|
|
.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::<tide::http::Mime>()
|
|
.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::<tide::http::Mime>()
|
|
.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<String> = INTERFACES
|
|
.internal
|
|
.iter()
|
|
.map(|info| format!("{}:80", info.ip))
|
|
.collect();
|
|
|
|
app.listen(listeners).await?;
|
|
}
|
|
|
|
Ok(()) as Result<(), anyhow::Error>
|
|
})?;
|
|
|
|
Ok(())
|
|
}
|