~vpzom/lotide

3a46189e536a8a99e39a184c7834de6632a736db — Colin Reeder 4 months ago a47283e
Prepare for moderation support by tracking Announces
A migrations/20200731152704_post-approved/down.sql => migrations/20200731152704_post-approved/down.sql +4 -0
@@ 0,0 1,4 @@
BEGIN;
	ALTER TABLE post DROP COLUMN approved;
	ALTER TABLE post DROP COLUMN approved_ap_id;
COMMIT;

A migrations/20200731152704_post-approved/up.sql => migrations/20200731152704_post-approved/up.sql +6 -0
@@ 0,0 1,6 @@
BEGIN;
	ALTER TABLE post ADD COLUMN approved BOOLEAN NOT NULL DEFAULT (FALSE);
	ALTER TABLE post ADD COLUMN approved_ap_id TEXT;

	UPDATE post SET approved=TRUE;
COMMIT;

M src/apub_util.rs => src/apub_util.rs +12 -3
@@ 1328,7 1328,7 @@ pub async fn handle_recieved_object_for_local_community<'a>(
    };

    if let Some(local_community_id) = local_community_id {
        handle_recieved_object_for_community(local_community_id, true, obj, ctx).await?;
        handle_recieved_object_for_community(local_community_id, true, None, obj, ctx).await?;
    } else {
        // not to a community, but might still match as a reply
        if let Some(in_reply_to) = in_reply_to {


@@ 1366,6 1366,7 @@ pub async fn handle_recieved_object_for_local_community<'a>(
pub async fn handle_received_page_for_community<Kind: Clone + std::fmt::Debug>(
    community_local_id: CommunityLocalID,
    community_is_local: bool,
    is_announce: Option<&url::Url>,
    obj: Verified<activitystreams::object::Object<Kind>>,
    ctx: Arc<crate::RouteContext>,
) -> Result<(), crate::Error> {


@@ 1409,6 1410,7 @@ pub async fn handle_received_page_for_community<Kind: Clone + std::fmt::Debug>(
            author,
            community_local_id,
            community_is_local,
            is_announce,
            ctx,
        )
        .await?;


@@ 1420,6 1422,7 @@ pub async fn handle_received_page_for_community<Kind: Clone + std::fmt::Debug>(
pub async fn handle_recieved_object_for_community<'a>(
    community_local_id: CommunityLocalID,
    community_is_local: bool,
    is_announce: Option<&url::Url>,
    obj: Verified<KnownObject>,
    ctx: Arc<crate::RouteContext>,
) -> Result<(), crate::Error> {


@@ 1430,6 1433,7 @@ pub async fn handle_recieved_object_for_community<'a>(
            handle_received_page_for_community(
                community_local_id,
                community_is_local,
                is_announce,
                Verified(obj),
                ctx,
            )


@@ 1439,6 1443,7 @@ pub async fn handle_recieved_object_for_community<'a>(
            handle_received_page_for_community(
                community_local_id,
                community_is_local,
                is_announce,
                Verified(obj),
                ctx,
            )


@@ 1506,6 1511,7 @@ pub async fn handle_recieved_object_for_community<'a>(
                            author,
                            community_local_id,
                            community_is_local,
                            is_announce,
                            ctx,
                        )
                        .await?;


@@ 1529,6 1535,7 @@ async fn handle_recieved_post(
    author: Option<&url::Url>,
    community_local_id: CommunityLocalID,
    community_is_local: bool,
    is_announce: Option<&url::Url>,
    ctx: Arc<crate::RouteContext>,
) -> Result<(), crate::Error> {
    let db = ctx.db_pool.get().await?;


@@ 1546,9 1553,11 @@ async fn handle_recieved_post(
        (Some(content), None)
    };

    let approved = is_announce.is_some() || community_is_local;

    let row = db.query_opt(
        "INSERT INTO post (author, href, content_text, content_html, title, created, community, local, ap_id) VALUES ($1, $2, $3, $4, $5, COALESCE($6, current_timestamp), $7, FALSE, $8) ON CONFLICT (ap_id) DO NOTHING RETURNING id",
        &[&author, &href, &content_text, &content_html, &title, &created, &community_local_id, &object_id.as_str()],
        "INSERT INTO post (author, href, content_text, content_html, title, created, community, local, ap_id, approved, approved_ap_id) VALUES ($1, $2, $3, $4, $5, COALESCE($6, current_timestamp), $7, FALSE, $8, $9, $10) ON CONFLICT (ap_id) DO UPDATE SET approved=$9, approved_ap_id=$10 RETURNING id",
        &[&author, &href, &content_text, &content_html, &title, &created, &community_local_id, &object_id.as_str(), &approved, &is_announce.map(|x| x.as_str())],
    ).await?;

    if community_is_local {

M src/routes/api/communities.rs => src/routes/api/communities.rs +1 -1
@@ 444,7 444,7 @@ async fn route_unstable_communities_posts_list(

    let values: &[&(dyn tokio_postgres::types::ToSql + Sync)] = &[&community_id, &limit];
    let sql: &str = &format!(
        "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, person.username, person.local, person.ap_id FROM post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = $1 AND post.deleted=FALSE ORDER BY {} LIMIT $2",
        "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, person.username, person.local, person.ap_id FROM post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = $1 AND post.approved=TRUE AND post.deleted=FALSE ORDER BY {} LIMIT $2",
        query.sort.post_sort_sql(),
    );


M src/routes/api/mod.rs => src/routes/api/mod.rs +1 -1
@@ 758,7 758,7 @@ async fn route_unstable_users_me_following_posts_list(
    let values: &[&(dyn tokio_postgres::types::ToSql + Sync)] = &[&user, &limit];

    let stream = db.query_raw(
        "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, community.id, community.name, community.local, community.ap_id, person.username, person.local, person.ap_id FROM community, post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = community.id AND deleted=FALSE AND community.id IN (SELECT community FROM community_follow WHERE follower=$1 AND accepted) ORDER BY hot_rank((SELECT COUNT(*) FROM post_like WHERE post = post.id AND person != post.author), post.created) DESC LIMIT $2",
        "SELECT post.id, post.author, post.href, post.content_text, post.title, post.created, post.content_html, community.id, community.name, community.local, community.ap_id, person.username, person.local, person.ap_id FROM community, post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = community.id AND post.approved AND post.deleted=FALSE AND community.id IN (SELECT community FROM community_follow WHERE follower=$1 AND accepted) ORDER BY hot_rank((SELECT COUNT(*) FROM post_like WHERE post = post.id AND person != post.author), post.created) DESC LIMIT $2",
        values.iter().map(|s| *s as _)
    ).await?;


M src/routes/api/posts.rs => src/routes/api/posts.rs +30 -16
@@ 175,9 175,25 @@ async fn route_unstable_posts_create(
        },
    };

    let community_row = db
        .query_opt(
            "SELECT local FROM community WHERE id=$1",
            &[&body.community],
        )
        .await?
        .ok_or_else(|| {
            crate::Error::UserError(crate::simple_response(
                hyper::StatusCode::BAD_REQUEST,
                lang.tr("no_such_community", None).into_owned(),
            ))
        })?;

    let community_local: bool = community_row.get(0);
    let already_approved = community_local;

    let res_row = db.query_one(
        "INSERT INTO post (author, href, title, created, community, local, content_text, content_markdown, content_html) VALUES ($1, $2, $3, current_timestamp, $4, TRUE, $5, $6, $7) RETURNING id, created, (SELECT local FROM community WHERE id=post.community)",
        &[&user, &body.href, &body.title, &body.community, &content_text, &content_markdown, &content_html],
        "INSERT INTO post (author, href, title, created, community, local, content_text, content_markdown, content_html, approved) VALUES ($1, $2, $3, current_timestamp, $4, TRUE, $5, $6, $7, $8) RETURNING id, created",
        &[&user, &body.href, &body.title, &body.community, &content_text, &content_markdown, &content_html, &already_approved],
    ).await?;

    let id = PostLocalID(res_row.get(0));


@@ 196,19 212,15 @@ async fn route_unstable_posts_create(
    };

    crate::spawn_task(async move {
        let community_local: Option<bool> = res_row.get(2);

        if let Some(community_local) = community_local {
            if community_local {
                crate::on_community_add_post(
                    post.community,
                    post.id,
                    crate::apub_util::get_local_post_apub_id(post.id, &ctx.host_url_apub).into(),
                    ctx,
                );
            } else {
                crate::apub_util::spawn_enqueue_send_local_post_to_community(post, ctx);
            }
        if community_local {
            crate::on_community_add_post(
                post.community,
                post.id,
                crate::apub_util::get_local_post_apub_id(post.id, &ctx.host_url_apub).into(),
                ctx,
            );
        } else {
            crate::apub_util::spawn_enqueue_send_local_post_to_community(post, ctx);
        }

        Ok(())


@@ 244,6 256,7 @@ async fn route_unstable_posts_get(
    struct RespPostInfo<'a> {
        #[serde(flatten)]
        post: &'a RespPostListPost<'a>,
        approved: bool,
        score: i64,
        comments: Vec<RespPostCommentInfo<'a>>,
        #[serde(skip_serializing_if = "Option::is_none")]


@@ 254,7 267,7 @@ async fn route_unstable_posts_get(

    let (row, comments, your_vote) = futures::future::try_join3(
        db.query_opt(
            "SELECT post.author, post.href, post.content_text, post.title, post.created, post.content_html, community.id, community.name, community.local, community.ap_id, person.username, person.local, person.ap_id, (SELECT COUNT(*) FROM post_like WHERE post_like.post = $1) FROM community, post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = community.id AND post.id = $1",
            "SELECT post.author, post.href, post.content_text, post.title, post.created, post.content_html, community.id, community.name, community.local, community.ap_id, person.username, person.local, person.ap_id, (SELECT COUNT(*) FROM post_like WHERE post_like.post = $1), post.approved FROM community, post LEFT OUTER JOIN person ON (person.id = post.author) WHERE post.community = community.id AND post.id = $1",
            &[&post_id],
        )
        .map_err(crate::Error::from),


@@ 334,6 347,7 @@ async fn route_unstable_posts_get(
            let output = RespPostInfo {
                post: &post,
                comments,
                approved: row.get(14),
                score: row.get(13),
                your_vote,
            };

M src/routes/apub/mod.rs => src/routes/apub/mod.rs +13 -1
@@ 298,13 298,25 @@ async fn inbox_common(
                let object_id = activity.object().as_single_id();

                if let Some(object_id) = object_id {
                    if !object_id.as_str().starts_with(&ctx.host_url_apub.as_str()) {
                    if object_id.as_str().starts_with(&ctx.host_url_apub.as_str()) {
                        let remaining = &object_id.as_str()[ctx.host_url_apub.as_str().len()..];
                        if remaining.starts_with("/posts/") {
                            let remaining = &remaining[7..];
                            if let Ok(local_post_id) = remaining.parse::<PostLocalID>() {
                                db.execute(
                                    "UPDATE post SET approved=TRUE, approved_ap_id=$1 WHERE id=$2 AND community=$3",
                                    &[&activity_id.as_str(), &local_post_id, &community_local_id],
                                ).await?;
                            }
                        }
                    } else {
                        // don't need announces for local objects
                        let obj =
                            crate::apub_util::fetch_ap_object(object_id, &ctx.http_client).await?;
                        crate::apub_util::handle_recieved_object_for_community(
                            community_local_id,
                            community_is_local,
                            Some(&activity_id),
                            obj,
                            ctx,
                        )