Compare commits

...

2 commits

Author SHA1 Message Date
cb9e094582
refactor into using tera 2019-04-30 22:49:22 -04:00
38cad2e788
clean up of main 2019-04-30 15:27:15 -04:00
6 changed files with 743 additions and 233 deletions

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
/target
target
**/*.rs.bk
*.ron
.vscode/

796
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,7 @@ edition = "2018"
reqwest = "0.9.15"
serde = { version = "1.0.90", features = ["derive"] }
ron = "0.5"
actix-web = "0.7"
actix-web = "1.0.0-beta.2"
tokio = "0.1"
actix = "0.7"
tera = "0.11"
env_logger = "0.6.1"

View file

@ -1,13 +1,19 @@
extern crate actix;
extern crate actix_web;
extern crate env_logger;
extern crate reqwest;
extern crate ron;
extern crate serde;
#[macro_use]
extern crate tera;
use actix::System;
mod utils;
use self::utils::EpochTimestamp;
use actix_web::{
http::{Method, StatusCode},
server, App, HttpResponse, Json, Result as WebResult, State,
error::ErrorInternalServerError,
middleware::Logger,
web::{resource, Data},
App, Error as WebError, HttpResponse, HttpServer, Result as WebResult,
};
use reqwest::{Client, Url, UrlError};
use ron::de::from_str;
@ -15,11 +21,11 @@ use serde::{Deserialize, Serialize};
use std::{
error::Error,
fs::read_to_string,
sync::{Arc, Mutex},
time::{Duration, Instant},
sync::{Arc, Mutex, MutexGuard},
};
use tera::{Context, Tera};
#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
struct EndpointConf {
label: Option<String>,
endpoint: Option<String>,
@ -28,21 +34,22 @@ struct EndpointConf {
body: Option<String>,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
struct WebsiteConf {
label: String,
base: String,
endpoints: Vec<EndpointConf>,
}
#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
struct Config {
refresh_time: u64,
bind_address: String,
websites: Vec<WebsiteConf>,
}
#[derive(Debug, Clone, Serialize)]
struct Status {
#[derive(Clone, Serialize)]
pub struct Status {
status: u8,
location: String,
domain: String,
@ -50,8 +57,9 @@ struct Status {
error: Option<String>,
}
struct FetchResults {
last_update: Instant,
#[derive(Serialize)]
pub struct FetchResults {
last_update: EpochTimestamp,
refresh_time: u64,
config: Config,
statuses: Vec<Status>,
@ -59,54 +67,55 @@ struct FetchResults {
type StatusState = Arc<Mutex<FetchResults>>;
fn index(state: State<StatusState>) -> HttpResponse {
let mut state = state.lock().unwrap();
let mut result = String::new();
if Instant::now().duration_since(state.last_update) > Duration::from_secs(state.refresh_time) {
result = format!(
"it has been {:} seconds since last update, updating",
Instant::now().duration_since(state.last_update).as_secs()
);
state.last_update = Instant::now();
state.statuses = update_status(&state.config);
}
let result = format!("{}\n{:?}", result, state.statuses);
HttpResponse::with_body(StatusCode::OK, result)
fn index(tmpl: Data<Tera>, state: Data<StatusState>) -> WebResult<HttpResponse, WebError> {
let state = update_state(state.lock().unwrap());
let mut ctx = Context::new();
ctx.insert("results", &*state);
let s = tmpl
.render("index.html", &tera::Context::new())
.map_err(|_| ErrorInternalServerError("Template error"))?;
Ok(HttpResponse::Ok().content_type("text/html").body(s))
}
fn json_endpoint(state: State<StatusState>) -> WebResult<Json<Vec<Status>>> {
let mut state = state.lock().unwrap();
if Instant::now().duration_since(state.last_update) > Duration::from_secs(state.refresh_time) {
state.last_update = Instant::now();
fn json_endpoint(state: Data<StatusState>) -> HttpResponse {
let state = update_state(state.lock().unwrap());
HttpResponse::Ok().json(&state.statuses)
}
fn update_state(mut state: MutexGuard<FetchResults>) -> MutexGuard<FetchResults> {
if EpochTimestamp::now() - state.last_update >= state.refresh_time {
state.last_update = EpochTimestamp::now();
state.statuses = update_status(&state.config);
}
Ok(Json(state.statuses.clone()))
state
}
fn main() -> Result<(), Box<Error>> {
let config = from_str::<Config>(&read_to_string("./endstat_conf.ron")?)?;
let bind_addr = config.bind_address.clone();
std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
println!("running server");
let sys = System::new("status");
let a: Arc<Mutex<FetchResults>> = Arc::from(Mutex::from(FetchResults {
last_update: Instant::now(),
refresh_time: config.refresh_time,
HttpServer::new(move || {
let state = Arc::from(Mutex::from(FetchResults {
last_update: EpochTimestamp::now(),
refresh_time: config.refresh_time.clone(),
config: config.clone(),
statuses: update_status(&config),
}));
let tera = compile_templates!(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/**/*"));
server::new(move || {
App::with_state(a.clone())
.resource("/", |r| r.get().with(index))
.resource("/api", |r| r.get().with(json_endpoint))
App::new()
.data(state)
.data(tera)
.wrap(Logger::default())
.service(resource("/").to(index))
.service(resource("/api").to(json_endpoint))
})
.bind("0.0.0.0:8080")?
.start();
.bind(&bind_addr)?
.run()?;
sys.run();
Ok(())
}

49
src/utils.rs Normal file
View file

@ -0,0 +1,49 @@
use serde::{Serialize, Serializer};
use std::{
fmt::{Display, Formatter, Result as FmtResult},
ops::Sub,
time::{SystemTime, UNIX_EPOCH},
};
#[derive(PartialEq, PartialOrd, Copy, Clone)]
pub struct EpochTimestamp(SystemTime);
impl EpochTimestamp {
pub fn now() -> Self {
EpochTimestamp(SystemTime::now())
}
}
impl Sub for EpochTimestamp {
type Output = u64;
fn sub(self, other: EpochTimestamp) -> u64 {
self.0.duration_since(other.0).unwrap_or_default().as_secs()
}
}
impl Display for EpochTimestamp {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(
f,
"{}",
self.0
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
)
}
}
impl Serialize for EpochTimestamp {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_u64(
self.0
.duration_since(SystemTime::from(UNIX_EPOCH))
.unwrap_or_default()
.as_secs(),
)
}
}

18
templates/index.html Normal file
View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Actix web</title>
</head>
<body>
<h1>Welcome!</h1>
<p>
<h3>What is your name?</h3>
<form>
<input type="text" name="name" /><br/>
<p><input type="submit"></p>
</form>
</p>
</body>
</html>