diff options
author | Colin Reeder <colin@vpzom.click> | 2020-09-16 16:50:19 -0600 |
---|---|---|
committer | Colin Reeder <colin@vpzom.click> | 2020-09-16 16:50:19 -0600 |
commit | 3fb5dbdcf22d3ea269e93b0b17eca8cfea18f379 (patch) | |
tree | d713661b2db2cf0de66f3f017a7a79911900115f | |
parent | f443d25dbd6227e30b40bc8a75a926166b9f1c50 (diff) |
Add API for listing and adding community moderators
-rw-r--r-- | openapi/openapi.json | 55 | ||||
-rw-r--r-- | res/lang/en.ftl | 2 | ||||
-rw-r--r-- | src/routes/api/communities.rs | 129 |
3 files changed, 185 insertions, 1 deletions
diff --git a/openapi/openapi.json b/openapi/openapi.json index a5b4eae..3b04b05 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.1", "info": { "title": "lotide API", - "version": "0.5.0-pre" + "version": "0.6.0-pre" }, "components": { "schemas": { @@ -586,6 +586,59 @@ "security": [{"bearer": []}] } }, + "/api/unstable/communities/{communityID}/moderators": { + "get": { + "summary": "List moderators of a community", + "parameters": [ + { + "name": "communityID", + "in": "path", + "required": true, + "schema": {"type": "integer"} + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MinimalUserInfo" + } + } + } + } + } + } + } + }, + "/api/unstable/communities/{communityID}/moderators/{userID}": { + "put": { + "summary": "Add a moderator to a community", + "parameters": [ + { + "name": "communityID", + "in": "path", + "required": true, + "schema": {"type": "integer"} + }, + { + "name": "userID", + "in": "path", + "required": true, + "schema": {"type": "integer"} + } + ], + "responses": { + "204": { + "description": "Successfully added." + } + }, + "security": [{"bearer": []}] + } + }, "/api/unstable/communities/{communityID}/posts": { "get": { "summary": "List posts published to a community", diff --git a/res/lang/en.ftl b/res/lang/en.ftl index 16fadd4..8b9aab1 100644 --- a/res/lang/en.ftl +++ b/res/lang/en.ftl @@ -2,6 +2,8 @@ comment_content_conflict = Exactly one of content_markdown and content_text must 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 +moderators_only_local = Only local users can be community moderators +must_be_moderator = You must be a community moderator to perform this action name_in_use = That name is already in use no_password = No password set for this user no_such_comment = No such comment diff --git a/src/routes/api/communities.rs b/src/routes/api/communities.rs index 8f9c71f..81c52f1 100644 --- a/src/routes/api/communities.rs +++ b/src/routes/api/communities.rs @@ -340,6 +340,124 @@ async fn route_unstable_communities_follow( .body(serde_json::to_vec(&output)?.into())?) } +async fn route_unstable_communities_moderators_list( + params: (CommunityLocalID,), + ctx: Arc<crate::RouteContext>, + req: hyper::Request<hyper::Body>, +) -> Result<hyper::Response<hyper::Body>, crate::Error> { + let (community_id,) = params; + + let lang = crate::get_lang_for_req(&req); + + let db = ctx.db_pool.get().await?; + + ({ + let row = db + .query_opt("SELECT 1 FROM community WHERE id=$1", &[&community_id]) + .await?; + + match row { + None => Err(crate::Error::UserError(crate::simple_response( + hyper::StatusCode::NOT_FOUND, + lang.tr("no_such_community", None).into_owned(), + ))), + Some(_) => Ok(()), + } + })?; + + let rows = db.query( + "SELECT id, username, local, ap_id, avatar FROM person WHERE id IN (SELECT person FROM community_moderator WHERE community=$1)", + &[&community_id], + ).await?; + + let output: Vec<_> = rows + .iter() + .map(|row| { + let local = row.get(2); + let ap_id = row.get(3); + + RespMinimalAuthorInfo { + id: UserLocalID(row.get(0)), + username: Cow::Borrowed(row.get(1)), + local, + host: crate::get_actor_host_or_unknown(local, ap_id, &ctx.local_hostname), + remote_url: ap_id.map(|x| x.into()), + avatar: row + .get::<_, Option<&str>>(4) + .map(|url| RespAvatarInfo { url: url.into() }), + } + }) + .collect(); + + let output = serde_json::to_vec(&output)?; + + Ok(hyper::Response::builder() + .header(hyper::header::CONTENT_TYPE, "application/json") + .body(output.into())?) +} + +async fn route_unstable_communities_moderators_add( + params: (CommunityLocalID, UserLocalID), + ctx: Arc<crate::RouteContext>, + req: hyper::Request<hyper::Body>, +) -> Result<hyper::Response<hyper::Body>, crate::Error> { + let (community_id, user_id) = params; + + let db = ctx.db_pool.get().await?; + + let lang = crate::get_lang_for_req(&req); + let login_user = crate::require_login(&req, &db).await?; + + ({ + let row = db + .query_opt( + "SELECT 1 FROM community_moderator WHERE community=$1 AND person=$2", + &[&community_id, &login_user], + ) + .await?; + match row { + None => Err(crate::Error::UserError(crate::simple_response( + hyper::StatusCode::FORBIDDEN, + lang.tr("must_be_moderator", None).into_owned(), + ))), + Some(_) => Ok(()), + } + })?; + + ({ + let row = db + .query_opt("SELECT local FROM person WHERE id=$1", &[&user_id]) + .await?; + + match row { + None => Err(crate::Error::UserError(crate::simple_response( + hyper::StatusCode::FORBIDDEN, + lang.tr("no_such_user", None).into_owned(), + ))), + Some(row) => { + let local: bool = row.get(0); + + if local { + Ok(()) + } else { + Err(crate::Error::UserError(crate::simple_response( + hyper::StatusCode::FORBIDDEN, + lang.tr("moderators_only_local", None).into_owned(), + ))) + } + } + } + })?; + + db.execute( + "INSERT INTO community_moderator (community, person) VALUES ($1, $2)", + &[&community_id, &user_id], + ) + .await?; + + Ok(crate::empty_response()) +} + async fn route_unstable_communities_unfollow( params: (CommunityLocalID,), ctx: Arc<crate::RouteContext>, @@ -613,6 +731,17 @@ pub fn route_communities() -> crate::RouteNode<()> { .with_handler_async("POST", route_unstable_communities_follow), ) .with_child( + "moderators", + crate::RouteNode::new() + .with_handler_async("GET", route_unstable_communities_moderators_list) + .with_child_parse::<UserLocalID, _>( + crate::RouteNode::new().with_handler_async( + "PUT", + route_unstable_communities_moderators_add, + ), + ), + ) + .with_child( "unfollow", crate::RouteNode::new() .with_handler_async("POST", route_unstable_communities_unfollow), |