diff --git a/src/handlers.rs b/src/handlers.rs new file mode 100644 index 0000000..bd61483 --- /dev/null +++ b/src/handlers.rs @@ -0,0 +1,127 @@ +use crate::config::*; +use crate::utils::EpochTimestamp; +use actix_web::{ + error::ErrorInternalServerError, web::Data, Error as WebError, HttpResponse, + Result as WebResult, +}; +use reqwest::{Client, Url, UrlError}; + +use serde::Serialize; +use std::sync::{Arc, Mutex, MutexGuard}; +use tera::{Context, Tera}; + +#[derive(Clone, Serialize, Default)] +pub struct Status { + status: u8, + location: String, + domain: String, + endpoint: String, + error: Option, +} + +#[derive(Serialize)] +pub struct FetchResults { + pub last_update: EpochTimestamp, + pub refresh_time: u64, + pub config: Config, + pub statuses: Vec, +} + +type StatusState = Arc>; + +pub fn index(tmpl: Data, state: Data) -> WebResult { + let state = update_state(state.lock().unwrap()); + let mut ctx = Context::new(); + ctx.insert("results", &*state); + let s = tmpl.render("index.html", &ctx).map_err(|e| { + println!("{:?}", e); + ErrorInternalServerError("Template error") + })?; + Ok(HttpResponse::Ok().content_type("text/html").body(s)) +} + +pub fn json_endpoint(state: Data) -> HttpResponse { + let state = update_state(state.lock().unwrap()); + HttpResponse::Ok().json(&state.statuses) +} + +pub fn update_state(mut state: MutexGuard) -> MutexGuard { + if EpochTimestamp::now() - state.last_update >= state.refresh_time { + state.last_update = EpochTimestamp::now(); + state.statuses = update_status(&state.config); + } + + state +} + +fn update_status(config: &Config) -> Vec { + let client = Client::new(); + let mut results: Vec = vec![]; + + for website_conf in &config.websites { + for endpoint in &website_conf.endpoints { + results.push(get_result(website_conf, &client, endpoint)); + } + } + + results +} + +fn get_result(website_conf: &WebsiteConfig, client: &Client, endpoint: &EndpointConfig) -> Status { + let (label, path, port, code, body) = get_endpoint_info(endpoint.clone()); + let url = get_url(&website_conf.base, &path, port).expect("reading config"); + let ping_result = client.get(&url).send(); + + match ping_result { + Ok(mut res) => { + let res_body = res.text().expect("could not get body of request"); + let does_code_match = res.status() == code; + let does_body_match = body.is_empty() || res_body == body; + let mut error = None; + + if !does_code_match { + error = Some(format!( + "Status code mismatch: {} != {}.", + res.status().as_u16(), + code + )); + } + + if !does_body_match { + error = Some(if let Some(msg) = error { + format!( + "{} Body mismatch: {} != {}.", + msg, + res_body.len(), + body.len() + ) + } else { + format!("Body mismatch: {} != {}.", res_body.len(), body.len()) + }); + } + + Status { + status: if error.is_some() { 1 } else { 0 }, + location: url, + domain: website_conf.label.clone(), + endpoint: label, + error, + } + } + Err(e) => Status { + status: 2, + location: url, + domain: website_conf.label.clone(), + endpoint: label, + error: Some(format!("{}", e)), + }, + } +} + +fn get_url(base: &String, path: &String, port: Option) -> Result { + let mut url = Url::parse(base)?.join(path)?; + if let Err(e) = url.set_port(port) { + println!("{:?}", e); + } + Ok(url.into_string()) +} diff --git a/src/main.rs b/src/main.rs index f529d53..7aab7c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,69 +7,19 @@ extern crate serde; extern crate tera; mod config; +mod handlers; mod utils; use self::config::*; +use self::handlers::*; use self::utils::EpochTimestamp; -use actix_web::{ - error::ErrorInternalServerError, - middleware::Logger, - web::{resource, Data}, - App, Error as WebError, HttpResponse, HttpServer, Result as WebResult, -}; -use reqwest::{Client, Url, UrlError}; +use actix_web::{middleware::Logger, web::resource, App, HttpServer}; use ron::de::from_str; -use serde::Serialize; use std::{ error::Error, fs::read_to_string, - sync::{Arc, Mutex, MutexGuard}, + sync::{Arc, Mutex}, }; -use tera::{Context, Tera}; - -#[derive(Clone, Serialize)] -pub struct Status { - status: u8, - location: String, - domain: String, - endpoint: String, - error: Option, -} - -#[derive(Serialize)] -pub struct FetchResults { - last_update: EpochTimestamp, - refresh_time: u64, - config: Config, - statuses: Vec, -} - -type StatusState = Arc>; - -fn index(tmpl: Data, state: Data) -> WebResult { - let state = update_state(state.lock().unwrap()); - let mut ctx = Context::new(); - ctx.insert("results", &*state); - let s = tmpl.render("index.html", &ctx).map_err(|e| { - println!("{:?}", e); - ErrorInternalServerError("Template error") - })?; - Ok(HttpResponse::Ok().content_type("text/html").body(s)) -} - -fn json_endpoint(state: Data) -> HttpResponse { - let state = update_state(state.lock().unwrap()); - HttpResponse::Ok().json(&state.statuses) -} - -fn update_state(mut state: MutexGuard) -> MutexGuard { - if EpochTimestamp::now() - state.last_update >= state.refresh_time { - state.last_update = EpochTimestamp::now(); - state.statuses = update_status(&state.config); - } - - state -} fn main() -> Result<(), Box> { let config = from_str::(&read_to_string("./endstat_conf.ron")?)?; @@ -82,7 +32,7 @@ fn main() -> Result<(), Box> { last_update: EpochTimestamp::now(), refresh_time: config.refresh_time.clone(), config: config.clone(), - statuses: update_status(&config), + statuses: vec![], })); let tera = compile_templates!(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*")); @@ -98,75 +48,3 @@ fn main() -> Result<(), Box> { Ok(()) } - -fn update_status(config: &Config) -> Vec { - let client = Client::new(); - let mut results: Vec = vec![]; - - for website_conf in &config.websites { - for endpoint in &website_conf.endpoints { - results.push(get_result(website_conf, &client, endpoint)); - } - } - - results -} - -fn get_result(website_conf: &WebsiteConfig, client: &Client, endpoint: &EndpointConfig) -> Status { - let (label, path, port, code, body) = get_endpoint_info(endpoint.clone()); - let url = get_url(&website_conf.base, &path, port).expect("reading config"); - let ping_result = client.get(&url).send(); - - match ping_result { - Ok(mut res) => { - let res_body = res.text().expect("could not get body of request"); - let does_code_match = res.status() == code; - let does_body_match = body.is_empty() || res_body == body; - let mut error = None; - - if !does_code_match { - error = Some(format!( - "Status code mismatch: {} != {}!", - res.status().as_u16(), - code - )); - } - - if !does_body_match { - error = Some(if let Some(msg) = error { - format!( - "{} Body mismatch! {} != {}", - msg, - res_body.len(), - body.len() - ) - } else { - format!("Body mismatch! {} != {}", res_body.len(), body.len()) - }); - } - - Status { - status: if error.is_some() { 1 } else { 0 }, - location: url, - domain: website_conf.label.clone(), - endpoint: label, - error, - } - } - Err(e) => Status { - status: 2, - location: url, - domain: website_conf.label.clone(), - endpoint: label, - error: Some(format!("{}", e)), - }, - } -} - -fn get_url(base: &String, path: &String, port: Option) -> Result { - let mut url = Url::parse(base)?.join(path)?; - if let Err(e) = url.set_port(port) { - println!("{:?}", e); - } - Ok(url.into_string()) -}