Compare commits
2 commits
82b944a748
...
f42c70b27e
Author | SHA1 | Date | |
---|---|---|---|
f42c70b27e | |||
17a0730275 |
5 changed files with 128 additions and 132 deletions
|
@ -60,21 +60,23 @@
|
||||||
<p>{{ results.timestamp_str }}</p>
|
<p>{{ results.timestamp_str }}</p>
|
||||||
</header>
|
</header>
|
||||||
{% for group in results.groups -%}
|
{% for group in results.groups -%}
|
||||||
<h2>{{ group.label }}</h2>
|
<h2>{{ group.label }}</h2>
|
||||||
{% for status in group.endpoints -%}
|
{% for status in group.endpoints -%}
|
||||||
<section>
|
<section>
|
||||||
<aside class="indicator {% if status.status == 0 %}ok{% elif status.status == 1 %}warn{% else %}error{% endif %}"></aside>
|
<aside class="indicator {% if status.status == 0 %}ok{% elif status.status == 1 %}warn{% else %}error{% endif %}"></aside>
|
||||||
<main>
|
<main>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<h3>{{ status.endpoint }}</h3>
|
<h3>{{ status.endpoint }}</h3>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
{% if status.status != 2 %}<p>{{ status.rtt }}</p>{% endif %}
|
{% if status.status != 2 %}<p>{{ status.rtt }}</p>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<a href="{{ status.location }}" target="_blank">{{ status.location }}</a>
|
<a href="{{ status.location }}" target="_blank">{{ status.location }}</a>
|
||||||
{% if status.error %}<p class="error-msg">{{ status.error }}</p>{% endif %}
|
{% for msg in status.errors -%}
|
||||||
</main>
|
<p class="error-msg">{{ msg }}</p>
|
||||||
</section>
|
{% endfor -%}
|
||||||
{% endfor -%}
|
</main>
|
||||||
|
</section>
|
||||||
|
{% endfor -%}
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,96 +1,10 @@
|
||||||
use crate::{config::*, updater::update_status, State};
|
use crate::State;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
error::ErrorInternalServerError, web::Data, Error as WebError, HttpResponse,
|
error::ErrorInternalServerError, web::Data, Error as WebError, HttpResponse,
|
||||||
Result as WebResult,
|
Result as WebResult,
|
||||||
};
|
};
|
||||||
use chrono::prelude::*;
|
|
||||||
use serde::Serialize;
|
|
||||||
use tera::{Context, Tera};
|
use tera::{Context, Tera};
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Default, Debug)]
|
|
||||||
pub struct EndpointStatus {
|
|
||||||
pub status: u8,
|
|
||||||
pub location: String,
|
|
||||||
pub endpoint: String,
|
|
||||||
pub rtt: Option<String>,
|
|
||||||
pub error: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EndpointStatus {
|
|
||||||
pub fn ok(location: String, endpoint: String, rtt: String) -> Self {
|
|
||||||
EndpointStatus {
|
|
||||||
status: 0,
|
|
||||||
location,
|
|
||||||
endpoint,
|
|
||||||
rtt: Some(rtt),
|
|
||||||
error: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn warn(location: String, endpoint: String, rtt: String, error: Option<String>) -> Self {
|
|
||||||
EndpointStatus {
|
|
||||||
status: 1,
|
|
||||||
location,
|
|
||||||
endpoint,
|
|
||||||
rtt: Some(rtt),
|
|
||||||
error,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn error(location: String, endpoint: String, error: Option<String>) -> Self {
|
|
||||||
EndpointStatus {
|
|
||||||
status: 2,
|
|
||||||
location,
|
|
||||||
endpoint,
|
|
||||||
rtt: None,
|
|
||||||
error,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
pub struct StatusGroup {
|
|
||||||
pub label: String,
|
|
||||||
pub endpoints: Vec<EndpointStatus>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
pub struct QueryResults {
|
|
||||||
pub timestamp: DateTime<Utc>,
|
|
||||||
pub timestamp_str: String,
|
|
||||||
pub refresh_time: u64,
|
|
||||||
pub config: Config,
|
|
||||||
pub groups: Vec<StatusGroup>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QueryResults {
|
|
||||||
pub fn new(config: Config) -> Self {
|
|
||||||
let time = Utc::now();
|
|
||||||
QueryResults {
|
|
||||||
timestamp: time,
|
|
||||||
timestamp_str: Self::format_timestamp(time),
|
|
||||||
refresh_time: config.refresh_time,
|
|
||||||
config: config.clone(),
|
|
||||||
groups: update_status(&config),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, updated_groups: Vec<StatusGroup>) {
|
|
||||||
self.update_timestamp();
|
|
||||||
self.groups = updated_groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_timestamp(&mut self) {
|
|
||||||
let current_time = Utc::now();
|
|
||||||
self.timestamp = current_time;
|
|
||||||
self.timestamp_str = Self::format_timestamp(current_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_timestamp(timestamp: DateTime<Utc>) -> String {
|
|
||||||
timestamp.format("%Y-%m-%d %H:%M:%S").to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn index(tmpl: Data<Tera>, state: Data<State>) -> WebResult<HttpResponse, WebError> {
|
pub fn index(tmpl: Data<Tera>, state: Data<State>) -> WebResult<HttpResponse, WebError> {
|
||||||
let state = state.read().unwrap();
|
let state = state.read().unwrap();
|
||||||
let mut ctx = Context::new();
|
let mut ctx = Context::new();
|
||||||
|
|
|
@ -15,8 +15,9 @@ extern crate ring;
|
||||||
mod config;
|
mod config;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod updater;
|
mod updater;
|
||||||
|
mod results;
|
||||||
|
|
||||||
use self::{config::*, handlers::*, updater::*};
|
use self::{config::*, handlers::*, updater::*, results::QueryResults};
|
||||||
use actix::System;
|
use actix::System;
|
||||||
use actix_web::{middleware::Logger, web::resource, App, HttpServer};
|
use actix_web::{middleware::Logger, web::resource, App, HttpServer};
|
||||||
use ron::de::from_str;
|
use ron::de::from_str;
|
||||||
|
|
87
src/results.rs
Normal file
87
src/results.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use crate::{config::*, updater::update_status};
|
||||||
|
use chrono::prelude::*;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Default, Debug)]
|
||||||
|
pub struct EndpointStatus {
|
||||||
|
pub status: u8,
|
||||||
|
pub location: String,
|
||||||
|
pub endpoint: String,
|
||||||
|
pub rtt: Option<String>,
|
||||||
|
pub errors: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EndpointStatus {
|
||||||
|
pub fn ok(location: String, endpoint: String, rtt: String) -> Self {
|
||||||
|
EndpointStatus {
|
||||||
|
status: 0,
|
||||||
|
location,
|
||||||
|
endpoint,
|
||||||
|
rtt: Some(rtt),
|
||||||
|
errors: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn warn(location: String, endpoint: String, rtt: String, errors: Vec<String>) -> Self {
|
||||||
|
EndpointStatus {
|
||||||
|
status: 1,
|
||||||
|
location,
|
||||||
|
endpoint,
|
||||||
|
rtt: Some(rtt),
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(location: String, endpoint: String, errors: Vec<String>) -> Self {
|
||||||
|
EndpointStatus {
|
||||||
|
status: 2,
|
||||||
|
location,
|
||||||
|
endpoint,
|
||||||
|
rtt: None,
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
pub struct StatusGroup {
|
||||||
|
pub label: String,
|
||||||
|
pub endpoints: Vec<EndpointStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
pub struct QueryResults {
|
||||||
|
pub timestamp: DateTime<Utc>,
|
||||||
|
pub timestamp_str: String,
|
||||||
|
pub refresh_time: u64,
|
||||||
|
pub config: Config,
|
||||||
|
pub groups: Vec<StatusGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueryResults {
|
||||||
|
pub fn new(config: Config) -> Self {
|
||||||
|
let time = Utc::now();
|
||||||
|
QueryResults {
|
||||||
|
timestamp: time,
|
||||||
|
timestamp_str: Self::format_timestamp(time),
|
||||||
|
refresh_time: config.refresh_time,
|
||||||
|
config: config.clone(),
|
||||||
|
groups: update_status(&config),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, updated_groups: Vec<StatusGroup>) {
|
||||||
|
self.update_timestamp();
|
||||||
|
self.groups = updated_groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_timestamp(&mut self) {
|
||||||
|
let current_time = Utc::now();
|
||||||
|
self.timestamp = current_time;
|
||||||
|
self.timestamp_str = Self::format_timestamp(current_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_timestamp(timestamp: DateTime<Utc>) -> String {
|
||||||
|
timestamp.format("%Y-%m-%d %H:%M:%S").to_string()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
config::*,
|
config::*,
|
||||||
handlers::{EndpointStatus, StatusGroup},
|
results::{EndpointStatus, StatusGroup},
|
||||||
State,
|
State,
|
||||||
};
|
};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
@ -62,43 +62,42 @@ fn get_result(
|
||||||
match ping_result {
|
match ping_result {
|
||||||
Ok(mut res) => {
|
Ok(mut res) => {
|
||||||
let res_body = res.text().expect("could not get body of request");
|
let res_body = res.text().expect("could not get body of request");
|
||||||
let mut error = None;
|
let mut errors = vec![];
|
||||||
|
|
||||||
if res.status() != code {
|
if res.status() != code {
|
||||||
error = append_err_msg(
|
errors.push(format!(
|
||||||
error,
|
"Status code mismatch: {} != {}",
|
||||||
format!(
|
res.status().as_u16(),
|
||||||
"Status code mismatch: {} != {}",
|
code
|
||||||
res.status().as_u16(),
|
));
|
||||||
code
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(expected_hash) = &endpoint.body_hash {
|
if let Some(expected_hash) = &endpoint.body_hash {
|
||||||
let expected = from_hex(expected_hash).unwrap();
|
let expected = from_hex(expected_hash).unwrap();
|
||||||
let actual = digest(&SHA256, String::from(res_body).as_bytes());
|
let actual = digest(&SHA256, String::from(res_body).as_bytes());
|
||||||
if &expected != &actual.as_ref() {
|
if &expected != &actual.as_ref() {
|
||||||
error = append_err_msg(error, String::from("Body hash mismatch."));
|
errors.push(String::from("Body hash mismatch."));
|
||||||
}
|
}
|
||||||
} else if !body.is_empty() && res_body != body {
|
} else if !body.is_empty() && res_body != body {
|
||||||
error = append_err_msg(
|
errors.push(format!(
|
||||||
error,
|
"Body mismatch: {} != {}",
|
||||||
format!("Body mismatch: {} != {}", res_body.len(), body.len()),
|
res_body.len(),
|
||||||
);
|
body.len()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(max_rtt) = endpoint.max_rtt {
|
if let Some(max_rtt) = endpoint.max_rtt {
|
||||||
if rtt.num_milliseconds() > max_rtt {
|
if rtt.num_milliseconds() > max_rtt {
|
||||||
error = append_err_msg(
|
errors.push(format!(
|
||||||
error,
|
"RTT too long: {} > {}s",
|
||||||
format!("RTT too long: {} > {}s", rtt_string, max_rtt as f64 / 1000.),
|
rtt_string,
|
||||||
);
|
max_rtt as f64 / 1000.
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if error.is_some() {
|
if !errors.is_empty() {
|
||||||
EndpointStatus::warn(url, label, rtt_string, error)
|
EndpointStatus::warn(url, label, rtt_string, errors)
|
||||||
} else {
|
} else {
|
||||||
EndpointStatus::ok(url, label, rtt_string)
|
EndpointStatus::ok(url, label, rtt_string)
|
||||||
}
|
}
|
||||||
|
@ -107,7 +106,7 @@ fn get_result(
|
||||||
if let Some(true) = endpoint.should_err {
|
if let Some(true) = endpoint.should_err {
|
||||||
EndpointStatus::ok(url, label, rtt_string)
|
EndpointStatus::ok(url, label, rtt_string)
|
||||||
} else {
|
} else {
|
||||||
EndpointStatus::error(url, label, Some(format!("{}", e)))
|
EndpointStatus::error(url, label, vec![format!("{}", e)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,10 +123,3 @@ fn get_url(base: &Option<String>, path: &String, port: Option<u16>) -> Result<St
|
||||||
}
|
}
|
||||||
Ok(url.into_string())
|
Ok(url.into_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_err_msg(optional: Option<String>, to_append: String) -> Option<String> {
|
|
||||||
Some(match optional {
|
|
||||||
Some(e) => format!("{}\n{}", e, to_append),
|
|
||||||
None => to_append,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue