first commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"web": [
|
||||
"lainoa.eus",
|
||||
"apunteak.lainoa.eus",
|
||||
"git.lainoa.eus",
|
||||
"nextcloud.lainoa.eus"
|
||||
],
|
||||
"xmpp": "lainoa.eus"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Error as IoError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
pub web: Vec<String>,
|
||||
pub xmpp: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new() -> Result<Self, IoError> {
|
||||
|
||||
//let mut file = File::open("./src/config.json").unwrap().try_into().ok()?;
|
||||
|
||||
let mut file = match File::open(CONFIG_FILE_PATH) {
|
||||
Ok(file) => file,
|
||||
Err(error) => {
|
||||
match error.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
println!("Config file not found");
|
||||
return Ok(Self{ //default configuration
|
||||
web: ["nextcloud.com".to_string()].to_vec(),
|
||||
xmpp: "conversations.im".to_string()
|
||||
});
|
||||
}
|
||||
_ => return Err(error),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut buff = String::new();
|
||||
file.read_to_string(&mut buff).unwrap();
|
||||
//println!("{}", &buff);
|
||||
let foo: Config = serde_json::from_str(&buff).unwrap();
|
||||
|
||||
Ok(Self {
|
||||
web: foo.web,
|
||||
xmpp: foo.xmpp
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const CONFIG_FILE_PATH: &str = "./src/config.json";
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
use awc::http::StatusCode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Service {
|
||||
pub name: String,
|
||||
pub status: String,
|
||||
pub class: String,
|
||||
}
|
||||
|
||||
impl Service {
|
||||
pub fn new(name: String, code: StatusCode, class: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
status: code.to_string(),
|
||||
class,
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
pub web: Vec<String>,
|
||||
pub xmpp: String,
|
||||
pub email: String
|
||||
}
|
||||
*/
|
||||
Reference in New Issue
Block a user