~vpzom/lotide

754c40dc9087ea887c0c4152564064eb1860a998 — Colin Reeder 8 months ago 2f611bb
Disallow empty comments (#106)
4 files changed, 46 insertions(+), 36 deletions(-)

M res/lang/en.ftl
M src/routes/api/comments.rs
M src/routes/api/mod.rs
M src/routes/api/posts.rs
M res/lang/en.ftl => res/lang/en.ftl +1 -0
@@ 1,4 1,5 @@
comment_content_conflict = Exactly one of content_markdown and content_text must be specified
comment_empty = Comment may not be empty
comment_not_yours = That's not your comment
community_edit_denied = You are not authorized to modify this community
community_name_disallowed_chars = Community name contains disallowed characters

M src/routes/api/comments.rs => src/routes/api/comments.rs +2 -18
@@ 520,24 520,8 @@ async fn route_unstable_comments_replies_create(
    let body = hyper::body::to_bytes(req.into_body()).await?;
    let body: CommentRepliesCreateBody<'_> = serde_json::from_slice(&body)?;

    if !(body.content_markdown.is_some() ^ body.content_text.is_some()) {
        return Err(crate::Error::UserError(crate::simple_response(
            hyper::StatusCode::BAD_REQUEST,
            lang.tr("comment_content_conflict", None).into_owned(),
        )));
    }

    let (content_text, content_markdown, content_html) = match body.content_markdown {
        Some(md) => {
            let (html, md) =
                tokio::task::spawn_blocking(move || (crate::render_markdown(&md), md)).await?;
            (None, Some(md), Some(html))
        }
        None => match body.content_text {
            Some(text) => (Some(text), None, None),
            None => (None, None, None),
        },
    };
    let (content_text, content_markdown, content_html) =
        super::process_comment_content(&lang, body.content_text, body.content_markdown).await?;

    let post: PostLocalID = match db
        .query_opt("SELECT post FROM reply WHERE id=$1", &[&parent_id])

M src/routes/api/mod.rs => src/routes/api/mod.rs +41 -0
@@ 778,3 778,44 @@ async fn handle_common_posts_list(

    Ok(posts)
}

pub async fn process_comment_content<'a>(
    lang: &crate::Translator,
    content_text: Option<Cow<'a, str>>,
    content_markdown: Option<String>,
) -> Result<(Option<Cow<'a, str>>, Option<String>, Option<String>), crate::Error> {
    if !(content_markdown.is_some() ^ content_text.is_some()) {
        return Err(crate::Error::UserError(crate::simple_response(
            hyper::StatusCode::BAD_REQUEST,
            lang.tr("comment_content_conflict", None).into_owned(),
        )));
    }

    Ok(match content_markdown {
        Some(md) => {
            if md.trim().is_empty() {
                return Err(crate::Error::UserError(crate::simple_response(
                    hyper::StatusCode::BAD_REQUEST,
                    lang.tr("comment_empty", None).into_owned(),
                )));
            }

            let (html, md) =
                tokio::task::spawn_blocking(move || (crate::render_markdown(&md), md)).await?;
            (None, Some(md), Some(html))
        }
        None => match content_text {
            Some(text) => {
                if text.trim().is_empty() {
                    return Err(crate::Error::UserError(crate::simple_response(
                        hyper::StatusCode::BAD_REQUEST,
                        lang.tr("comment_empty", None).into_owned(),
                    )));
                }

                (Some(text), None, None)
            }
            None => (None, None, None),
        },
    })
}

M src/routes/api/posts.rs => src/routes/api/posts.rs +2 -18
@@ 820,24 820,8 @@ async fn route_unstable_posts_replies_create(

    let body: RepliesCreateBody<'_> = serde_json::from_slice(&body)?;

    if !(body.content_markdown.is_some() ^ body.content_text.is_some()) {
        return Err(crate::Error::UserError(crate::simple_response(
            hyper::StatusCode::BAD_REQUEST,
            lang.tr("comment_content_conflict", None).into_owned(),
        )));
    }

    let (content_text, content_markdown, content_html) = match body.content_markdown {
        Some(md) => {
            let (html, md) =
                tokio::task::spawn_blocking(move || (crate::render_markdown(&md), md)).await?;
            (None, Some(md), Some(html))
        }
        None => match body.content_text {
            Some(text) => (Some(text), None, None),
            None => (None, None, None),
        },
    };
    let (content_text, content_markdown, content_html) =
        super::process_comment_content(&lang, body.content_text, body.content_markdown).await?;

    let row = db.query_one(
        "INSERT INTO reply (post, author, created, local, content_text, content_markdown, content_html) VALUES ($1, $2, current_timestamp, TRUE, $3, $4, $5) RETURNING id, created",