use mysql_async::Pool; use hyper::{Response, Body, StatusCode}; use hyper::body::to_bytes; use serde_json::Value; use serde_json::json; use std::collections::HashMap; use crate::http::{self, set_json_body}; use crate::perms; use db::messages::Message; pub async fn get_by_time(pool: &Pool, response: &mut Response, params: Value) { /* * Has a ton of required parameters just be warned * @channel: channel id we're looking at * @start-time: how long ago do we start searching * @end-time: how long ago do we stop searching * { * "channel": 1, * "start-time": unix_now - 24 hours * "end-time": unix_now - 23 hours * } * */ let channel = match params.get("channel") { Some(chan_v) => chan_v.as_u64(), None => None }; let start_time = match params.get("start-time") { Some(val) => val.as_i64(), None => None }; let end_time = match params.get("end-time") { Some(val) => val.as_i64(), None => None }; let limit = match params.get("limit") { Some(val) => val.as_u64(), None => None }; // TODO: flatten me mommy if let (Some(channel), Some(start), Some(end)) = (channel, start_time, end_time) { match Message::get_time_range(pool, channel, start, end, limit).await { Ok(db_response) => { match db_response { // this absolute lack of data streaming is prolly gonna suck like // a whore in hell week for performance but lets pretend servers don't get massive db::Response::Set(messages) => { set_json_body(response, json!({"messages": messages})); }, db::Response::RestrictedInput(_/*error message to log*/) => { *response.status_mut() = StatusCode::BAD_REQUEST; } _ => { *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; } }; }, Err(e) => { eprintln!("{}", e); *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; } }; } else { *response.status_mut() = StatusCode::BAD_REQUEST; } } pub async fn send_message(pool: &Pool, response: &mut Response, body: Body, params: HashMap<&str, &str>) { /* * Message content is sent in the message body * @channel_id: channel id that we're going to send a message to * TODO: more features here because send_message is a large handler */ use db::Response::*; use db::member::Member; use crate::db::common::FromDB; // NOTE: auth module guarantees this will be there in the correct form let uid = http::extract_uid(¶ms); let permissions = match Member::get(pool, uid).await { Row(user) => user.permissions, _ => 0 }; if perms::has_perm(permissions, perms::SEND_MESSAGES) == false { *response.status_mut() = StatusCode::BAD_REQUEST; return; } let channel_id = match params.get("channel") { Some(cval) => { if let Ok(num) = (*cval).to_string().parse::() { Some(num) } else { None } } None => None }; // Black magic let body_bytes: &[u8] = &to_bytes(body).await.unwrap(); // yolo let content = String::from_utf8_lossy(body_bytes); // 400 on empty bodies or missing channel id's if content.len() == 0 || channel_id.is_none() { *response.status_mut() = StatusCode::BAD_REQUEST; } else { match db::messages::Message::send(pool, &content, channel_id.unwrap(), uid).await { Ok(Empty) => {}, // nothing to do hyper defaults to 200 Ok(RestrictedInput(msg)) => *response.status_mut() = StatusCode::BAD_REQUEST, Ok(Other(msg)) => { eprintln!("{}", msg); *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; }, _ => *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR } } } pub async fn from_id(pool: &Pool, response: &mut Response, params: Value) { /* * @start-id: u64 * @limit: optional * @channel: u64 * { * "channel": 1, * "start": 123, * "limit": 100 * } */ let channel = match params.get("channel") { Some(chan_v) => chan_v.as_u64(), None => None }; let start_id = match params.get("start") { Some(val) => val.as_u64(), None => None }; let limit = match params.get("limit") { Some(val) => val.as_u64(), None => None }; if let (Some(channel), Some(start_id)) = (channel, start_id) { match Message::get_from_id(pool, channel, start_id, limit).await { Ok(db_response) => { match db_response { db::Response::Set(messages) => { // NOTE this check is here because the db's check doesn't // correctly with async and caching and magic idfk its here // it works its correct and the cost is the same as putting // it in the db layer so whatever if messages.len() == 0 { *response.status_mut() = StatusCode::NOT_FOUND; } else { set_json_body(response, json!({"messages": messages})); } }, _ => *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR }; }, Err(err) => { *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; eprintln!("{}", err); } }; } else { *response.status_mut() = StatusCode::BAD_REQUEST; } } #[cfg(test)] mod messaging_tests { use crate::testing::{get_pool, hyper_resp}; use serde_json::Value; use hyper::StatusCode; #[tokio::test] async fn send_message_test_missing_channel() { /* * Attempt to send a message i na channel that does not exist */ let p = get_pool(); let mut resp = hyper_resp(); let params: Value = serde_json::from_str(r#" { "channel": "this does not exist", "content": "bs message", "id": 420 } "#).unwrap(); super::send_message(&p, &mut resp, params).await; assert_ne!(StatusCode::OK, resp.status()); } }