// Basic handler for getting meta data about the server use std::collections::HashMap; use crate::http::set_json_body; use db::Neighbor; use mysql_async::Pool; use hyper::{Response, Body, StatusCode}; use hyper::body::to_bytes; use hyper::body::Bytes; use serde_json::{json, to_string, Result as JsonResult}; use serde::{Serialize, Deserialize}; use lazy_static::lazy_static; #[derive(Debug, Deserialize, Serialize)] pub struct Config { pub name: String, pub description: String, pub url: String, pub wsurl: String, pub tags: Vec } lazy_static! { // NOTE: this object must be access by proxy through get_config() #[derive(Deserialize, Serialize)] static ref BASIC_CONFIG: Config = { use std::fs::File; use std::io::BufReader; match File::open("config.json") { Ok(file) => { let reader = BufReader::new(file); let rr: JsonResult = serde_json::from_reader(reader); match rr { Ok(meta) => meta, Err(e) => panic!("{}", e) } }, Err(e) => panic!("{}", e) } }; } pub fn get_config() -> Config { // We have to do this (for now) because lazy_static silently hides the actual fields // we care about Config { name: BASIC_CONFIG.name.clone(), description: BASIC_CONFIG.description.clone(), url: BASIC_CONFIG.url.clone(), wsurl: BASIC_CONFIG.wsurl.clone(), tags: BASIC_CONFIG.tags.clone() } } pub async fn server_meta(response: &mut Response) { // NOTE: This route _is_ open but we should do something to rate limit the // number of requests we service as it could be ripe for abuse *response.body_mut() = Body::from(to_string(&get_config()).unwrap()); } pub async fn server_neighbors(p: &Pool, response: &mut Response) { // This method refers to what servers have been added as **related** by the admins // It returns a list of servers meta objects which converted to JSON match db::neighbors::get_all(p).await { Ok(neighbors) => set_json_body(response, json!({"neighbors": neighbors})), Err(e) => { *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; eprintln!("500 /neighbors/list {}", e); } } } pub async fn add_neighbor(p: &Pool, response: &mut Response, body: Body) { let body: Bytes = to_bytes(body).await.unwrap_or(Bytes::new()); let json: JsonResult = serde_json::from_slice(&body); if let Ok(neighbor) = json { // Before we try adding the new neighbor, make sure there isn't already // an entry with the same url if let Ok(row) = db::neighbors::get(p, &neighbor.url).await { match row.is_some() { true => *response.status_mut() = StatusCode::CONFLICT, false => if let Err(e) = db::neighbors::add_neighbor(p, neighbor).await { eprintln!("{}", e); } }; } else { eprintln!(); *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; } } else { *response.status_mut() = StatusCode::BAD_REQUEST; } } pub async fn update_neighbor(p: &Pool, response: &mut Response, params: HashMap, body: Body) { // First collect the target url from the map and try to parse the body let target = params.get("url"); let body: Bytes = to_bytes(body).await.unwrap_or(Bytes::new()); let s: String = String::from_utf8_lossy(&body).to_string(); let json: JsonResult = serde_json::from_str(&s); println!("\tjson result: {:?}", json); println!("\tbody {}", s); // Verify query string parameter **and** body before attempting to reach database match (target, json) { (Some(target_url), Ok(neighbor)) => { // Only return a 500 if something happened on db-lib's end if let Err(e) = db::neighbors::update(p, target_url, neighbor).await { *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; eprintln!("/neighbor/update [DB-LIB] {}", e); } // Nothing to do on success 200 is already set by hyper }, _ => *response.status_mut() = StatusCode::BAD_REQUEST } }