247 lines
7.3 KiB
Rust
247 lines
7.3 KiB
Rust
mod services;
|
|
use crate::services::Service;
|
|
|
|
mod config;
|
|
use crate::config::Config;
|
|
|
|
use actix_files::Files;
|
|
use actix_governor::{Governor, GovernorConfigBuilder, KeyExtractor, SimpleKeyExtractionError};
|
|
use actix_web::{get, web,
|
|
http::{
|
|
header::ContentType,
|
|
StatusCode,
|
|
// Method, StatusCode,
|
|
},
|
|
//cookie::{ Key, SameSite, Cookie },
|
|
App, /*HttpRequest,*/ HttpServer, HttpResponse, Responder, HttpResponseBuilder
|
|
};
|
|
use actix_web::dev::ServiceRequest;
|
|
use governor::{NotUntil, clock::{Clock, QuantaInstant, DefaultClock}};
|
|
use awc::Client;
|
|
use serde_json::{Map, Value};
|
|
use handlebars::Handlebars;
|
|
use handlebars::handlebars_helper;
|
|
use tokio_xmpp::SimpleClient as XmppClient;
|
|
#[macro_use]
|
|
extern crate lazy_static;
|
|
|
|
lazy_static! {
|
|
static ref CONFIG: config::Config =
|
|
Config::new().expect("to load config file");
|
|
}
|
|
|
|
#[get("/")]
|
|
async fn index(/*req: HttpRequest, */ hb: web::Data<Handlebars<'_>>) -> impl Responder {
|
|
|
|
/*
|
|
if let Some(val) = req.peer_addr() {
|
|
println!("Address {:?}", val.ip());
|
|
};
|
|
println!("ip: {:?}", req.connection_info().realip_remote_addr());
|
|
*/
|
|
let mut list:Vec<String> = Vec::new();
|
|
|
|
// add web services
|
|
let web_services = &CONFIG.web;
|
|
for web_service in web_services.into_iter() {
|
|
let mut service = "web:".to_string();
|
|
service.push_str(web_service);
|
|
list.push(service);
|
|
}
|
|
//add xmpp service
|
|
let xmpp_service = &CONFIG.xmpp;
|
|
let mut service = "xmpp:".to_string();
|
|
service.push_str(xmpp_service);
|
|
list.push(service);
|
|
|
|
// check services
|
|
let res = check(list.to_vec()).await;
|
|
|
|
let body = hb.render("index", &res).unwrap();
|
|
HttpResponse::Ok()
|
|
.content_type("text/html")
|
|
.body(body)
|
|
|
|
}
|
|
|
|
async fn check(list:Vec<String>) -> Map<String, Value>{
|
|
let mut obj = Map::new();
|
|
let mut vmap = Vec::new();
|
|
let mut map = Map::new();
|
|
|
|
#[derive(Debug)]
|
|
//#[derive(Debug, strum_macros::Display)]
|
|
enum Class {
|
|
Web,
|
|
Mail,
|
|
Xmpp,
|
|
Unknown
|
|
}
|
|
impl Class {
|
|
fn as_str(&self) -> &'static str {
|
|
match self {
|
|
Class::Web => "Web",
|
|
Class::Mail => "Mail",
|
|
Class::Xmpp => "Xmpp",
|
|
Class::Unknown => "Unknown",
|
|
}
|
|
}
|
|
}
|
|
//https://stackoverflow.com/questions/65040158/can-i-create-string-enum
|
|
for service in list.into_iter(){
|
|
let class = match &service {
|
|
s if s.starts_with("web") => Class::Web,
|
|
s if s.starts_with("xmpp") => Class::Xmpp,
|
|
s if s.starts_with("mail") => Class::Mail,
|
|
_ => Class::Unknown,
|
|
};
|
|
|
|
match class {
|
|
Class::Web => {
|
|
let n: Vec<&str> = service.split(":").collect();
|
|
let name = n[1];
|
|
|
|
let mut url = "https://".to_string();
|
|
url.push_str(&name);
|
|
|
|
//println!("service type: {:?}", class.as_str().to_string());
|
|
let web_service = Service::new(name.to_string(), ws(url.to_string()).await, class.as_str().to_string());
|
|
|
|
obj.insert("name".to_string(), Value::String(web_service.name));
|
|
obj.insert("status".to_string(), Value::String(web_service.status));
|
|
obj.insert("class".to_string(), Value::String(web_service.class));
|
|
vmap.push(obj.clone());
|
|
},
|
|
Class::Xmpp => {
|
|
let n: Vec<&str> = service.split(":").collect();
|
|
let name = n[1];
|
|
|
|
let xmpp_service = Service::new(name.to_string(), xs(name.to_string()).await, class.as_str().to_string());
|
|
|
|
obj.insert("name".to_string(), Value::String(xmpp_service.name));
|
|
obj.insert("status".to_string(), Value::String(xmpp_service.status));
|
|
obj.insert("class".to_string(), Value::String(xmpp_service.class));
|
|
vmap.push(obj.clone());
|
|
},
|
|
Class::Mail => println!("mail service"),
|
|
Class::Unknown => println!("Unknown service")
|
|
}
|
|
|
|
}
|
|
|
|
map.insert("data".to_string(), serde_json::json!(vmap).into());
|
|
map
|
|
}
|
|
|
|
//xmpp server check
|
|
async fn xs(server: String) -> StatusCode {
|
|
|
|
dotenv::dotenv().ok();
|
|
let xmpp_user = std::env::var("XMPP_USER").unwrap_or("user".to_string());
|
|
|
|
let jid = format!("{}@{}",xmpp_user,server);
|
|
let password = std::env::var("XMPP_PASS").unwrap_or("secret".to_string());
|
|
|
|
// Xmpp Client instance
|
|
let client = XmppClient::new(&jid, password.to_owned()).await;
|
|
|
|
match client {
|
|
Ok(_) => {
|
|
//println!("Client connected!");
|
|
client.expect("REASON").end().await.unwrap();
|
|
StatusCode::OK
|
|
},
|
|
Err(..) => {
|
|
StatusCode::INTERNAL_SERVER_ERROR
|
|
}
|
|
}
|
|
}
|
|
|
|
// web service check
|
|
async fn ws(url: String) -> StatusCode {
|
|
let client = Client::default();
|
|
let res = client
|
|
.get(url)
|
|
.send()
|
|
.await;
|
|
//res.unwrap().status()
|
|
match res {
|
|
Ok(status) => status.status(),
|
|
Err(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
|
}
|
|
|
|
}
|
|
|
|
#[actix_web::main]
|
|
async fn main() -> std::io::Result<()> {
|
|
|
|
dotenv::dotenv().ok();
|
|
let port = std::env::var("PORT").unwrap_or("8080".to_string());
|
|
let address = format!("127.0.0.1:{}", port);
|
|
|
|
//println!("web: {:?}", CONFIG.web);
|
|
//println!("web: {:?}", CONFIG.web[0]);
|
|
|
|
|
|
// Allow bursts with up to five requests per IP address
|
|
// and replenishes one element every two seconds
|
|
let governor_conf = GovernorConfigBuilder::default()
|
|
.per_second(4)
|
|
.burst_size(2)
|
|
.finish()
|
|
.unwrap();
|
|
|
|
handlebars_helper!(compare: |a: String, b: String | a == b);
|
|
let mut handlebars = Handlebars::new();
|
|
handlebars.register_helper("compare", Box::new(compare));
|
|
handlebars
|
|
.register_template_file("index", "./static/index.html")
|
|
.unwrap();
|
|
|
|
let handlebars_ref = web::Data::new(handlebars);
|
|
|
|
HttpServer::new(move || {
|
|
App::new()
|
|
// Enable Governor middleware
|
|
.wrap(Governor::new(&governor_conf))
|
|
// Route hello world service
|
|
//.route("/", web::get().to(index))
|
|
.app_data(handlebars_ref.clone())
|
|
.service(Files::new("/static", "static").show_files_listing())
|
|
.service(index)
|
|
})
|
|
.bind(&address)?
|
|
.run()
|
|
.await
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
//#[warn(dead_code)]
|
|
#[allow(dead_code)]
|
|
//#[allow(unused_variables)]
|
|
struct Foo;
|
|
|
|
// will return 500 error and 'Extract error' as content
|
|
impl KeyExtractor for Foo {
|
|
type Key = ();
|
|
type KeyExtractionError = SimpleKeyExtractionError<&'static str>;
|
|
|
|
fn extract(&self, _req: &ServiceRequest) -> Result<Self::Key, Self::KeyExtractionError> {
|
|
Err(SimpleKeyExtractionError::new("Extract error"))
|
|
}
|
|
|
|
fn exceed_rate_limit_response(
|
|
&self,
|
|
negative: &NotUntil<QuantaInstant>,
|
|
mut response: HttpResponseBuilder,
|
|
) -> HttpResponse {
|
|
let wait_time = negative
|
|
.wait_time_from(DefaultClock::default().now())
|
|
.as_secs();
|
|
response
|
|
.content_type(ContentType::plaintext())
|
|
.body(format!("Too many requests, retry in {}s", wait_time))
|
|
}
|
|
}
|
|
|