diff --git a/src/db.rs b/src/db.rs index b6b67af..90144ea 100644 --- a/src/db.rs +++ b/src/db.rs @@ -82,7 +82,6 @@ impl CommentId { } pub fn from_base64(s: &str) -> Result { - // TODO prevent panic when s is too long let mut buf = [0; 16]; base64::decode_config_slice(s, base64::URL_SAFE_NO_PAD, &mut buf).map(|_| Self(buf)) } diff --git a/src/helpers.rs b/src/helpers.rs index 81e9c8f..4a537e3 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,4 +1,4 @@ -use crate::{config::Config, db::*, queries::*}; +use crate::{config::Config, db::*}; use log::error; use std::{net::IpAddr, str::FromStr}; @@ -30,18 +30,13 @@ pub fn approve_comment(comment_id: CommentId, dbs: &Dbs) -> Result<(), sled::Err Ok(()) } -pub fn remove_comment(comment_id: CommentId, dbs: &Dbs) -> Result, sled::Error> { +pub fn remove_pending_comment( + comment_id: CommentId, + dbs: &Dbs, +) -> Result, sled::Error> { if let Some(comment) = dbs.comment.remove(&comment_id)? { - dbs.comment_pending.remove(&( - comment.topic_hash.clone(), - comment.post_time, - comment_id.clone(), - ))?; - dbs.comment_approved.remove(&( - comment.topic_hash.clone(), - comment.post_time, - comment_id, - ))?; + dbs.comment_pending + .remove(&(comment.topic_hash.clone(), comment.post_time, comment_id))?; return Ok(Some(comment)); } Ok(None) @@ -168,30 +163,6 @@ pub fn get_client_addr( )) } -pub fn check_comment(config: &Config, comment: &CommentForm, errors: &mut Vec) { - if comment.author.len() > config.comment_author_max_len { - errors.push(format!( - "Author name length is {} but maximum is {}.", - comment.author.len(), - config.comment_author_max_len - )); - } - if comment.email.len() > config.comment_email_max_len { - errors.push(format!( - "E-mail length is {} but maximum is {}.", - comment.email.len(), - config.comment_email_max_len - )); - } - if comment.text.len() > config.comment_text_max_len { - errors.push(format!( - "Comment length is {} but maximum is {}.", - comment.text.len(), - config.comment_text_max_len - )); - } -} - #[cfg(test)] mod test { use super::*; diff --git a/src/queries.rs b/src/queries.rs index f59a70e..21d0d2a 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -1,4 +1,4 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize)] pub struct AdminLoginQuery { @@ -8,35 +8,37 @@ pub struct AdminLoginQuery { #[derive(Clone, Debug, Deserialize)] pub struct AdminEditCommentQuery { pub author: String, - pub id: String, + pub comment_id: String, pub email: String, pub text: String, } #[derive(Clone, Debug, Deserialize)] pub struct AdminRmCommentQuery { - pub id: String, + pub comment_id: String, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct NewCommentQuery { - #[serde(flatten)] - pub comment: CommentForm, + pub author: String, + pub email: String, + pub text: String, } #[derive(Clone, Debug, Deserialize)] pub struct EditCommentQuery { - #[serde(flatten)] - pub comment: CommentForm, - pub id: String, - //pub token: String, + pub author: String, + pub comment_id: String, + pub email: String, + pub text: String, + pub token: String, } -/*#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize)] pub struct RmCommentQuery { - pub id: String, - //pub token: String, -}*/ + pub comment_id: String, + pub token: String, +} #[derive(Clone, Debug, Deserialize)] #[serde(tag = "a")] @@ -45,8 +47,8 @@ pub enum CommentQuery { NewComment(NewCommentQuery), #[serde(rename = "edit_comment")] EditComment(EditCommentQuery), - /*#[serde(rename = "rm_comment")] - RmComment(RmCommentQuery),*/ + #[serde(rename = "rm_comment")] + RmComment(RmCommentQuery), } #[derive(Clone, Debug, Deserialize)] @@ -69,15 +71,3 @@ pub struct ApproveQuery { pub struct RemoveQuery { pub remove: String, } - -#[derive(Clone, Debug, Deserialize)] -pub struct EditQuery { - pub edit: String, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct CommentForm { - pub author: String, - pub email: String, - pub text: String, -} diff --git a/src/server.rs b/src/server.rs index 43b9232..6177aa3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -18,14 +18,7 @@ pub async fn run_server(config: Arc, dbs: Dbs, templates: Arc let templates = templates.clone(); let dbs = dbs.clone(); move |req: tide::Request<()>| { - serve_comments( - req, - config.clone(), - templates.clone(), - dbs.clone(), - Context::new(), - 200, - ) + serve_comments(req, config.clone(), templates.clone(), dbs.clone(), &[]) } }); app.at(&format!("{}t/:topic", config.root_url)).post({ @@ -63,9 +56,10 @@ async fn serve_comments<'a>( config: Arc, templates: Arc, dbs: Dbs, - mut context: Context, - status_code: u16, + errors: &[String], ) -> tide::Result { + dbg!(req.peer_addr()); + let Ok(topic) = req.param("topic") else { return Err(tide::Error::from_str(404, "No topic")) }; @@ -76,8 +70,10 @@ async fn serve_comments<'a>( let topic_hash = TopicHash::from_topic(topic); + let mut context = Context::new(); context.insert("config", &config); context.insert("admin", &admin); + context.insert("new_comment_errors", errors); if admin { if let Ok(query) = req.query::() { @@ -89,28 +85,17 @@ async fn serve_comments<'a>( } if let Ok(query) = req.query::() { if let Ok(comment_id) = CommentId::from_base64(&query.remove) { - helpers::remove_comment(comment_id, &dbs) + helpers::remove_pending_comment(comment_id, &dbs) .map_err(|e| error!("Removing comment: {:?}", e)) .ok(); } } - if let Ok(query) = req.query::() { - if let Ok(comment_id) = CommentId::from_base64(&query.edit) { - if let Some(comment) = dbs.comment.get(&comment_id).unwrap() { - context.insert("edit_comment", &comment_id.to_base64()); - context.insert("edit_comment_author", &comment.author); - context.insert("edit_comment_email", &comment.email); - context.insert("edit_comment_text", &comment.text); - } - } - } context.insert( "comments_pending", &helpers::iter_pending_comments_by_topic(topic_hash.clone(), &dbs) .map(|(comment_id, comment)| CommentWithId { author: comment.author, - editable: admin, id: comment_id.to_base64(), needs_approval: true, post_time: comment.post_time, @@ -125,7 +110,6 @@ async fn serve_comments<'a>( &helpers::iter_approved_comments_by_topic(topic_hash, &dbs) .map(|(comment_id, comment)| CommentWithId { author: comment.author, - editable: admin, id: comment_id.to_base64(), needs_approval: false, post_time: comment.post_time, @@ -134,10 +118,12 @@ async fn serve_comments<'a>( .collect::>(), ); - Ok(tide::Response::builder(status_code) - .content_type(tide::http::mime::HTML) - .body(templates.tera.render("comments.html", &context)?) - .build()) + Ok( + tide::Response::builder(if errors.is_empty() { 200 } else { 400 }) + .content_type(tide::http::mime::HTML) + .body(templates.tera.render("comments.html", &context)?) + .build(), + ) } async fn serve_admin<'a>( @@ -169,7 +155,6 @@ async fn serve_admin<'a>( })?; Some(CommentWithId { author: comment.author, - editable: true, id: comment_id.to_base64(), needs_approval: true, post_time: comment.post_time, @@ -206,11 +191,7 @@ async fn handle_post_comments( dbs: Dbs, notify_send: Sender<()>, ) -> tide::Result { - let admin = req.cookie("admin").map_or(false, |psw| { - check_admin_password_hash(&config, &String::from(psw.value())) - }); - - let client_addr = if !admin && config.antispam_enable { + let client_addr = if config.antispam_enable { match helpers::get_client_addr(&config, &req) { Some(Ok(addr)) => { if config.antispam_whitelist.contains(&addr) { @@ -233,7 +214,6 @@ async fn handle_post_comments( }; let mut errors = Vec::new(); - let mut context = Context::new(); match req.body_form::().await? { CommentQuery::NewComment(query) => { @@ -241,8 +221,27 @@ async fn handle_post_comments( return Err(tide::Error::from_str(404, "No topic")) }; - helpers::check_comment(&config, &query.comment, &mut errors); - + if query.author.len() > config.comment_author_max_len { + errors.push(format!( + "Author name length is {} but maximum is {}.", + query.author.len(), + config.comment_author_max_len + )); + } + if query.email.len() > config.comment_email_max_len { + errors.push(format!( + "E-mail length is {} but maximum is {}.", + query.email.len(), + config.comment_email_max_len + )); + } + if query.text.len() > config.comment_text_max_len { + errors.push(format!( + "Comment length is {} but maximum is {}.", + query.text.len(), + config.comment_text_max_len + )); + } if let Some(client_addr) = &client_addr { if let Some(antispam_timeout) = helpers::antispam_check_client_mutation(client_addr, &dbs, &config).unwrap() @@ -268,95 +267,25 @@ async fn handle_post_comments( let comment = Comment { topic_hash, - author: query.comment.author, - email: if query.comment.email.is_empty() { + author: query.author, + email: if query.email.is_empty() { None } else { - Some(query.comment.email) + Some(query.email) }, last_edit_time: None, post_time: time, - text: query.comment.text, + text: query.text, }; helpers::new_pending_comment(&comment, &dbs) .map_err(|e| error!("Adding pending comment: {:?}", e)) .ok(); notify_send.send(()).ok(); - } else { - context.insert("new_comment_author", &query.comment.author); - context.insert("new_comment_email", &query.comment.email); - context.insert("new_comment_text", &query.comment.text); } - context.insert("new_comment_errors", &errors); - } - CommentQuery::EditComment(query) => { - if !admin { - return Err(tide::Error::from_str(403, "Forbidden")); - } - - helpers::check_comment(&config, &query.comment, &mut errors); - - let comment_id = if let Ok(comment_id) = CommentId::from_base64(&query.id) { - comment_id - } else { - return Err(tide::Error::from_str(400, "Invalid comment id")); - }; - - let mut comment = if let Some(comment) = dbs.comment.get(&comment_id).unwrap() { - comment - } else { - return Err(tide::Error::from_str(404, "Not found")); - }; - - if let Some(client_addr) = &client_addr { - if let Some(antispam_timeout) = - helpers::antispam_check_client_mutation(client_addr, &dbs, &config).unwrap() - { - errors.push(format!( - "The edition quota from your IP is reached. You will be unblocked in {}s.", - antispam_timeout - )); - } - } - - if errors.is_empty() { - if let Some(client_addr) = &client_addr { - helpers::antispam_update_client_mutation(client_addr, &dbs).unwrap(); - } - - let time = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(); - - comment.author = query.comment.author; - comment.email = if query.comment.email.is_empty() { - None - } else { - Some(query.comment.email) - }; - comment.text = query.comment.text; - comment.last_edit_time = Some(time); - - dbs.comment.insert(&comment_id, &comment).unwrap(); - } else { - context.insert("edit_comment", &comment_id.to_base64()); - context.insert("edit_comment_author", &query.comment.author); - context.insert("edit_comment_email", &query.comment.email); - context.insert("edit_comment_text", &query.comment.text); - } - context.insert("edit_comment_errors", &errors); } + _ => {} } - serve_comments( - req, - config, - templates, - dbs, - context, - if errors.is_empty() { 200 } else { 400 }, - ) - .await + serve_comments(req, config, templates, dbs, &errors).await } async fn handle_post_admin( @@ -367,7 +296,6 @@ async fn handle_post_admin( ) -> tide::Result { if let Some(psw) = req.cookie("admin") { if check_admin_password(&config, &String::from(psw.value())).is_some() { - #[allow(clippy::match_single_binding)] match req.body_form::().await? { _ => serve_admin(req, config, templates, dbs).await, } diff --git a/src/templates.rs b/src/templates.rs index bb9e827..d78a64f 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -38,7 +38,6 @@ impl Templates { #[derive(Clone, Debug, Serialize)] pub struct CommentWithId { pub author: String, - pub editable: bool, pub id: String, pub needs_approval: bool, pub post_time: Time, diff --git a/templates/comments.html b/templates/comments.html index 70a720b..4b669bf 100644 --- a/templates/comments.html +++ b/templates/comments.html @@ -8,17 +8,14 @@ {% if comments_pending %}
{% for comment in comments_pending %} -
+
{{ comment.author }} {{ comment.post_time | date(format="%F %R", locale=config.lang) }} - {% if comment.editable %} - Edit - {% endif %} {% if admin and comment.needs_approval %} - Approve + Approve {% endif %} {% if admin %} - Remove + Remove {% endif %}

{{ comment.text }}

@@ -27,17 +24,14 @@ {% endif %}
{% for comment in comments %} -
+
{{ comment.author }} {{ comment.post_time | date(format="%F %R", locale=config.lang) }} - {% if comment.editable %} - Edit - {% endif %} {% if admin and comment.needs_approval %} - Approve + Approve {% endif %} {% if admin %} - Remove + Remove {% endif %}

{{ comment.text }}

@@ -53,32 +47,12 @@ {% endif %} -
+
-
+

-
+
- {% if edit_comment %} -
- {% if edit_comment_errors %} -

Whoops, the following error occurred:

-
    - {% for error in edit_comment_errors %} -
  • {{ error | safe }}
  • - {% endfor %} -
- {% endif %} - - -
- -
-
-
- -
- {% endif %}