use std::collections::HashMap;
use std::convert::Infallible;
use std::fs::File;
use std::io::Read;
use log::*;
use warp::http::Uri;
use reqwest::Url;
use handlebars::to_json;
use serde_json::value::Map;
use super::models::comment::Comment;
use super::models::reply::Reply;
use super::models::user::User;
use super::models::status::Status;
pub async fn index() -> Result<impl warp::Reply, Infallible> {
let comments_total = get_comments_total().await;
let mut data = Map::new();
data.insert("comments_total".to_string(), to_json( &comments_total ) );
let page = super::render("index_page", &data);
Ok( warp::reply::html(page) )
}
pub async fn show_few_comments() -> Result<impl warp::Reply, Infallible> {
debug!("show_few_comments()............................................");
let count = super::config("comments_display_count_initial").parse::<usize>().unwrap();
show_comments_for(count).await
}
pub async fn show_comments_for_session_id(sid: String) -> Result<impl warp::Reply, Infallible> {
info!("show_comments_for_session_id({})............................................", &sid);
super::set_session_id(sid); let count = super::config("comments_display_count_initial").parse::<usize>().unwrap();
let user: User = get_session_user().await;
let comments: Vec<Comment> = get_comments_for(&user, count).await;
let page = process_comments_for(&user, comments).await;
Ok( warp::reply::html(page) )
}
pub async fn show_comments_for(count: usize) -> Result<impl warp::Reply, Infallible> {
debug!("show_comments_for({:?}.....................", &count);
let user: User = get_session_user().await;
let comments: Vec<Comment> = get_comments_for(&user, count).await;
let page = process_comments_for(&user, comments).await;
Ok( warp::reply::html(page) )
}
async fn process_comments_for(user: &User, comments: Vec<Comment>) -> String {
debug!("process_comments_for({:?}, {:?})-------------------------------------------->", &user.name, &comments.len());
let max_comments = super::config("max_comments").parse::<usize>().unwrap();
let can_add = comments.len() < max_comments;
let additional_count = super::config("comments_display_count_additional").parse::<usize>().unwrap();
let more_count = comments.len() + additional_count;
let comments_total = get_comments_total().await;
let has_more_comments = comments_total > comments.len();
let mut data = Map::new();
data.insert("comments".to_string(), to_json( &comments ) );
data.insert("comments_total".to_string(), to_json( &comments_total ) );
data.insert("can_add".to_string(), to_json( &can_add ) );
data.insert("more_count".to_string(), to_json( &more_count ) );
data.insert("has_more_comments".to_string(), to_json( &has_more_comments ) );
data.insert("user_id".to_string(), to_json( &user.unique_id) );
data.insert("user_avatar_filename".to_string(), to_json( &user.avatar) );
data.insert("avatar_url_path".to_string(), to_json( &super::avatar_url_path() ) );
data.insert("avatar_anonymous_filename".to_string(), to_json( &super::config("avatar_anonymous_filename") ) );
data.insert("replies_display_count_initial".to_string(), to_json( &super::config("replies_display_count_initial") ) );
data.insert("replies_display_count_additional".to_string(), to_json( &super::config("replies_display_count_additional") ) );
super::render("comments_page", &data)
}
async fn get_comments_total() -> usize {
let url = super::comments_total_url();
let result_str = make_reqwest_call_on(url).await;
let total: usize = serde_json::from_str(&result_str)
.unwrap_or_else(|err| {
error!("{:?} occurred in handlers::get_comments_total()!!", err);
0usize
});
total
}
async fn get_comments_for(session_user: &User, count: usize) -> Vec<Comment> {
debug!("get_comments_for({:?}, {:?}).......................................", &session_user.name, &count);
let url = super::more_comments_url(count);
let result_str = make_reqwest_call_on(url).await;
let comments: Vec<Comment> = serde_json::from_str(&result_str)
.unwrap_or_else(|err| {
error!("{:?} occurred in handlers::get_comments_for()!!", err);
Vec::new()
});
let comments_n_replies = get_n_attach_replies(comments, session_user).await;
comments_n_replies
}
async fn get_session_user() -> User {
let sid = super::get_session_id();
let url = super::session_url_with(&sid);
let result_str = make_reqwest_call_on(url).await;
let user: User = serde_json::from_str(&result_str)
.unwrap_or_else(|err| {
error!("{:?} occurred in handlers::get_session_user()!!", err);
User::default()
});
info!("session user now is: {:?}..........................................", &user);
user
}
async fn get_n_attach_replies(comments: Vec<Comment>, session_user: &User) -> Vec<Comment> {
debug!("get_n_attach_replies(...).........................");
let mut answer: Vec<Comment> = Vec::new();
let max_replies = super::config("max_replies").parse::<usize>().unwrap();
let init_msg_len = super::config("initial_message_length").parse::<usize>().unwrap();
for comment in comments {
let url = super::replies_url_with( &comment.unique_id );
let result_str = make_reqwest_call_on(url).await; let replies: Vec<Reply> = serde_json::from_str(&result_str)
.unwrap_or_else(|err| {
error!("{:?} occurred in handlers::attach_replies_to(...)", err);
Vec::new()
});
let mut cwr = comment.clone(); cwr.user = get_commenter(&comment.user_id).await; cwr.session_user = session_user.clone();
cwr.upvotes_count = get_upvotes_count(&comment.unique_id).await;
cwr.downvotes_count = get_downvotes_count(&comment.unique_id).await;
if cwr.message.len() > init_msg_len {
cwr.more_message = cwr.message.split_off(init_msg_len);
cwr.has_more_message = true;
}
let mut authzn_data = Map::new();
authzn_data.insert( "session_user_id".to_string(), to_json(&session_user.unique_id) );
authzn_data.insert( "resource_owner_id".to_string(), to_json(&cwr.user.unique_id) );
let view_permitted = super::allows_view(&session_user.unique_id, "cb_enable", &authzn_data);
debug!("view_permitted for cb_enable: {:?}", &view_permitted);
cwr.visible = view_permitted;
let edit_permitted = super::allows_edit(&session_user.unique_id, "comment_edit", &authzn_data);
cwr.editable = edit_permitted;
let delete_permitted = super::allows_delete(&session_user.unique_id, "trash_button", &authzn_data);
cwr.trashable = delete_permitted;
cwr.show_dropdown = cwr.visible && cwr.editable && cwr.trashable;
let mut replies_with_commenter: Vec<Reply> = Vec::new();
let max_reply_depth = super::config("max_reply_depth").parse::<u32>().unwrap();
for mut reply in replies {
reply.user = get_commenter(&reply.user_id).await;
reply.session_user = session_user.clone();
if reply.message.len() > init_msg_len {
reply.more_message = reply.message.split_off(init_msg_len);
reply.has_more_message = true;
}
reply.enabled = cwr.enabled && reply.level < max_reply_depth;
let mut authzn_data = Map::new();
authzn_data.insert( "session_user_id".to_string(), to_json(&session_user.unique_id) );
authzn_data.insert( "resource_owner_id".to_string(), to_json(&reply.user.unique_id) );
reply.editable = super::allows_edit(&session_user.unique_id, "comment_edit", &authzn_data);
reply.trashable = super::allows_delete(&session_user.unique_id, "trash_button", &authzn_data);
reply.show_dropdown = reply.editable && reply.trashable;
reply.upvotes_count = get_upvotes_count(&reply.unique_id).await;
reply.downvotes_count = get_downvotes_count(&reply.unique_id).await;
reply.time_ago = super::time_ago(&reply.created_on);
replies_with_commenter.push(reply);
}
cwr.replies = Reply::tree_from(replies_with_commenter, max_replies);
debug!("Replies tree >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
debug!("{:?}", cwr.replies);
debug!("REplies tree ends <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
cwr.replies_count = cwr.replies.len();
cwr.has_replies = cwr.replies.len() > 0;
let repliable = cwr.replies.len() < max_replies;
cwr.enabled = cwr.status != Status::Disabled && repliable;
cwr.time_ago = super::time_ago(&cwr.created_on);
answer.push(cwr);
}
answer
}
async fn get_upvotes_count(comment_id: &str) -> usize {
let url = super::upvotes_count_url(comment_id);
let result_str = make_reqwest_call_on(url).await;
let result: usize = serde_json::from_str(&result_str)
.unwrap_or_else(|err| {
error!("{:?} occurred in handlers::get_upvotes_count()!!", err);
0usize });
debug!("get_upvotes_count({:?}) -> {}", &comment_id, &result);
result
}
async fn get_downvotes_count(comment_id: &str) -> usize {
let url = super::downvotes_count_url(comment_id);
let result_str = make_reqwest_call_on(url).await;
let result: usize = serde_json::from_str(&result_str)
.unwrap_or_else(|err| {
error!("{:?} occurred in handlers::get_downvotes_count()!!", err);
0usize });
debug!("get_downvotes_count({:?}) -> {}", &comment_id, &result);
result
}
async fn get_commenter(uid: &str) -> User {
let url = super::commenter_url_with(uid);
let result_str = make_reqwest_call_on(url).await;
let user: User = serde_json::from_str(&result_str)
.unwrap_or_else(|err| {
error!("{:?} occurred in handlers::get_commenter()!!", err);
User::default()
});
debug!("get_commeter({:?}): {:?}..........", uid, user);
user
}
async fn make_reqwest_call_on(url: Url) -> String {
let url_copy = url.clone();
let result = match reqwest::get(url).await {
Ok(response) => match response.text().await {
Ok(text) => text,
Err(error) => {
error!("{:?} occurred in response.text() at handlers.make_request_call_on(url): {:?} call", error, &url_copy);
String::new()
}
},
Err(error) => {
error!("{:?} occurred in handlers.make_request_call_on({:?})", error, &url_copy);
String::new()
}
};
result
}
pub async fn add_comment(map: HashMap<String, String>) -> Result<impl warp::Reply, Infallible> {
debug!("add_comment({:?}).......................<<", map);
let message = if map.contains_key("message") {
map.get("message").unwrap()
} else { "" };
let is_anonymous: bool = if map.contains_key("is-anonymous") {
map.get("is-anonymous").unwrap() == &"on"
} else { false };
let user_id = if map.contains_key("user_id") {
map.get("user_id").unwrap()
} else { "" };
let comment: Comment = Comment::from(message, is_anonymous, user_id);
save(&comment).await;
Ok( warp::redirect(Uri::from_static("/comments")) )
}
fn process_client_result(result: Result<reqwest::Response, reqwest::Error>) {
match result {
Ok(response) => debug!("Response received as: {:?}", response),
Err(error) => error!("Error occurred in process_client_result(..): {:?}", error)
}
}
pub async fn update_comment_status(cid: String, map: HashMap<String, String>) -> Result<impl warp::Reply, Infallible> {
debug!("update_comment_status({:?}, {:?})..............", &cid, &map);
let client = reqwest::Client::new();
let enabled = if map.contains_key("enable") {
map.get("enable").unwrap() == &"on"
} else { false };
let status: &str = if enabled { "enabled" } else { "disabled" };
let url = super::status_update_url(status, &cid);
let result = client.put(url).send().await;
process_client_result(result);
Ok( warp::redirect(Uri::from_static("/comments")) )
}
async fn save(comment: &Comment) {
debug!("save({:?})................................................<<", comment);
let client = reqwest::Client::new();
let url = super::comments_url();
let result = client.post(url).json(&comment).send().await;
process_client_result( result );
}
pub async fn add_reply(map: HashMap<String, String>) -> Result<impl warp::Reply, Infallible> {
debug!("add_reply({:?}).......................<<", map);
let message = if map.contains_key("message") {
map.get("message").unwrap()
} else { "" };
let is_anonymous: bool = if map.contains_key("is-anonymous") {
map.get("is-anonymous").unwrap() == &"on"
} else { false };
let parent_id = if map.contains_key("parent_id") {
map.get("parent_id").unwrap()
} else { "" };
let comment_id = if map.contains_key("comment_id") {
map.get("comment_id").unwrap()
} else { "" };
let level: u32 = if map.contains_key("level") {
map.get("level").unwrap().parse::<u32>().unwrap()
} else { 0 };
let user_id = if map.contains_key("user_id") {
map.get("user_id").unwrap()
} else { "" };
let reply: Reply = Reply::from(message, is_anonymous, &parent_id, &comment_id, level, &user_id);
save_reply(&reply).await;
Ok( warp::redirect(Uri::from_static("/comments")) )
}
async fn save_reply(reply: &Reply) {
debug!("save_reply(reply)................<<");
let client = reqwest::Client::new();
let url = super::replies_url();
let result = client.post(url).json(&reply).send().await;
process_client_result( result );
}
pub async fn get_static_file(file_name: String) -> Result<impl warp::Reply, Infallible> {
debug!("get_static_file({})............", &file_name);
let parts: Vec<&str> = file_name.split('.').collect();
let file_type: &str = match parts[1] {
"js" => "text/javascript",
"css" => "text/css",
"jpg" => "image/jpeg",
_ => "text/text",
};
if file_name == "comment.js" {
let content = include_str!("resource/comment.js").to_string();
let response = warp::http::Response::builder()
.header("content-type", file_type)
.body(content);
return Ok(response);
};
if file_name == "index.js" {
let content = include_str!("resource/index.js").to_string();
let response = warp::http::Response::builder()
.header("content-type", file_type)
.body(content);
return Ok(response);
};
if file_name == "comment.css" {
let content = include_str!("resource/comment.css").to_string();
let response = warp::http::Response::builder()
.header("content-type", file_type)
.body(content);
return Ok(response);
};
let response = warp::http::Response::builder()
.header("content-type", file_type)
.body("ERROR OCCURRED".to_string());
Ok( response )
}
pub async fn get_image(name: String) -> Result<impl warp::Reply, Infallible> {
debug!("get_image({})............", &name);
let mut content = Vec::new();
let mut file = File::open("./images/".to_owned() + &name).unwrap();
let name_parts: Vec<&str> = name.split(".").collect();
let ext = name_parts.last().unwrap();
let content_type = match *ext {
"svg" => "image/svg+xml",
"jpg" => "image/jpeg",
"jpeg" => "image/jpeg",
"png" => "image/png",
_ => "image/jpeg",
};
file.read_to_end(&mut content).unwrap();
let response = warp::http::Response::builder()
.header("content-type", content_type)
.body(content);
Ok(response)
}
pub async fn get_image_type(img_type: String, name: String) -> Result<impl warp::Reply, Infallible> {
debug!("get_image({})............", &name);
let mut content = Vec::new();
let filename = match img_type.as_str() {
"icons" => "./images/icons/".to_owned() + &name,
_ => "./images/".to_owned() + &name
};
let mut file = File::open(filename).unwrap();
let name_parts: Vec<&str> = name.split(".").collect();
let ext = name_parts.last().unwrap();
let content_type = match *ext {
"svg" => "image/svg+xml",
"jpg" => "image/jpeg",
"jpeg" => "image/jpeg",
"png" => "image/png",
_ => "image/jpeg",
};
file.read_to_end(&mut content).unwrap();
let response = warp::http::Response::builder()
.header("content-type", content_type)
.body(content);
Ok(response)
}
pub async fn update_comment_msg(map: HashMap<String, String>) -> Result<impl warp::Reply, Infallible> {
debug!("update_comment_msg({:?})..............", &map);
let client = reqwest::Client::new();
let uid = if map.contains_key("unique_id") {
map.get("unique_id").unwrap()
} else { "" };
let message = if map.contains_key("message") {
map.get("message").unwrap().to_string()
} else { String::new() };
let url = super::comment_msg_update_url(uid.clone());
debug!("url: {} for update comment message: {} ++++++++++++++++++++++++", &url, &message);
let result = client.put(url).json(&map).send().await;
process_client_result( result );
Ok( warp::redirect(Uri::from_static("/comments")) )
}
pub async fn trash_comment(uid: String, user_id: String) -> Result<impl warp::Reply, Infallible> {
debug!("trash_comment({:?}, {:?})..............", &uid, &user_id);
let client = reqwest::Client::new();
let url = super::comments_trash_url(&uid);
debug!("url: {} to trash comment: -------------------------", &url);
let result = client.delete(url).header("authorization", "Bearer admin").send().await;
process_client_result( result );
Ok( warp::redirect(Uri::from_static("/comments")) )
}
pub async fn update_reply_msg(map: HashMap<String, String>) -> Result<impl warp::Reply, Infallible> {
debug!("update_reply_msg({:?})..............", &map);
let client = reqwest::Client::new();
let uid = if map.contains_key("unique_id") {
map.get("unique_id").unwrap()
} else { "" };
let message = if map.contains_key("message") {
map.get("message").unwrap().to_string()
} else { String::new() };
let url = super::reply_msg_update_url(uid.clone());
debug!("url: {} for update reply message: {} ++++++++++++++++++++++++", &url, &message);
let result = client.put(url).json(&map).send().await;
process_client_result( result );
Ok( warp::redirect(Uri::from_static("/comments")) )
}
pub async fn trash_reply(uid: String) -> Result<impl warp::Reply, Infallible> {
debug!("trash_reply({:?})..............", &uid);
let client = reqwest::Client::new();
let url = super::replies_trash_url(&uid);
debug!("url: {} to trash reply: -------------------------", &url);
let result = client.delete(url).header("authorization", "Bearer admin").send().await;
process_client_result( result );
Ok( warp::redirect(Uri::from_static("/comments")) )
}
pub async fn upvote(map: HashMap<String, String>) -> Result<impl warp::Reply, Infallible> {
debug!("upvote({:?})..............", &map);
let client = reqwest::Client::new();
let url = super::upvote_url();
let result = client.post(url).json(&map).send().await;
process_client_result( result );
Ok( warp::redirect(Uri::from_static("/comments")) )
}
pub async fn downvote(map: HashMap<String, String>) -> Result<impl warp::Reply, Infallible> {
debug!("downvote({:?})..............", &map);
let client = reqwest::Client::new();
let url = super::downvote_url();
let result = client.post(url).json(&map).send().await;
process_client_result( result );
Ok( warp::redirect(Uri::from_static("/comments")) )
}