mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-26 14:21:19 +00:00
* Re-organizing federation tests. #746 #1040 * Add more checks in inbox, plus some refactoring (#76) Merge branch 'main' into more-inbox-permissions Move check_community_ban() into helper function Move slur check into helper functions Move Claims::decode and site ban check into helper function Note: this changes behaviour in that site ban is checked in more places now. we could easily add a boolean parameter check_for_site_ban to get the previous behaviour back Rewrite user_inbox and community_inbox in the same way as shared_inbox Add check against instance allowlist etc in shared_inbox Co-authored-by: dessalines <dessalines@noreply.yerbamate.dev> Co-authored-by: Felix Ableitner <me@nutomic.com> Reviewed-on: https://yerbamate.dev/LemmyNet/lemmy/pulls/76 * Adding verbose to test results. Co-authored-by: nutomic <nutomic@noreply.yerbamate.dev> Co-authored-by: dessalines <dessalines@noreply.yerbamate.dev> Co-authored-by: Felix Ableitner <me@nutomic.com>
This commit is contained in:
parent
97d2584a3b
commit
e336e5bcc0
27 changed files with 1812 additions and 2483 deletions
1
docker/federation-test/servers.sh
vendored
1
docker/federation-test/servers.sh
vendored
|
@ -1,6 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
sudo docker-compose --file ../federation/docker-compose.yml --project-directory . down
|
||||||
sudo rm -rf volumes
|
sudo rm -rf volumes
|
||||||
|
|
||||||
pushd ../../server/
|
pushd ../../server/
|
||||||
|
|
6
docker/federation/docker-compose.yml
vendored
6
docker/federation/docker-compose.yml
vendored
|
@ -39,6 +39,8 @@ services:
|
||||||
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
|
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
|
||||||
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
||||||
- LEMMY_SETUP__SITE_NAME=lemmy-alpha
|
- LEMMY_SETUP__SITE_NAME=lemmy-alpha
|
||||||
|
- LEMMY_RATE_LIMIT__POST=99999
|
||||||
|
- LEMMY_RATE_LIMIT__REGISTER=99999
|
||||||
- RUST_BACKTRACE=1
|
- RUST_BACKTRACE=1
|
||||||
- RUST_LOG=debug
|
- RUST_LOG=debug
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -66,6 +68,8 @@ services:
|
||||||
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
|
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
|
||||||
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
||||||
- LEMMY_SETUP__SITE_NAME=lemmy-beta
|
- LEMMY_SETUP__SITE_NAME=lemmy-beta
|
||||||
|
- LEMMY_RATE_LIMIT__POST=99999
|
||||||
|
- LEMMY_RATE_LIMIT__REGISTER=99999
|
||||||
- RUST_BACKTRACE=1
|
- RUST_BACKTRACE=1
|
||||||
- RUST_LOG=debug
|
- RUST_LOG=debug
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -93,6 +97,8 @@ services:
|
||||||
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
|
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
|
||||||
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
||||||
- LEMMY_SETUP__SITE_NAME=lemmy-gamma
|
- LEMMY_SETUP__SITE_NAME=lemmy-gamma
|
||||||
|
- LEMMY_RATE_LIMIT__POST=99999
|
||||||
|
- LEMMY_RATE_LIMIT__REGISTER=99999
|
||||||
- RUST_BACKTRACE=1
|
- RUST_BACKTRACE=1
|
||||||
- RUST_LOG=debug
|
- RUST_LOG=debug
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|
6
docker/travis/docker-compose.yml
vendored
6
docker/travis/docker-compose.yml
vendored
|
@ -39,6 +39,8 @@ services:
|
||||||
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
|
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
|
||||||
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
||||||
- LEMMY_SETUP__SITE_NAME=lemmy-alpha
|
- LEMMY_SETUP__SITE_NAME=lemmy-alpha
|
||||||
|
- LEMMY_RATE_LIMIT__POST=99999
|
||||||
|
- LEMMY_RATE_LIMIT__REGISTER=99999
|
||||||
- RUST_BACKTRACE=1
|
- RUST_BACKTRACE=1
|
||||||
- RUST_LOG=debug
|
- RUST_LOG=debug
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -66,6 +68,8 @@ services:
|
||||||
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
|
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
|
||||||
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
||||||
- LEMMY_SETUP__SITE_NAME=lemmy-beta
|
- LEMMY_SETUP__SITE_NAME=lemmy-beta
|
||||||
|
- LEMMY_RATE_LIMIT__POST=99999
|
||||||
|
- LEMMY_RATE_LIMIT__REGISTER=99999
|
||||||
- RUST_BACKTRACE=1
|
- RUST_BACKTRACE=1
|
||||||
- RUST_LOG=debug
|
- RUST_LOG=debug
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -93,6 +97,8 @@ services:
|
||||||
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
|
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
|
||||||
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
||||||
- LEMMY_SETUP__SITE_NAME=lemmy-gamma
|
- LEMMY_SETUP__SITE_NAME=lemmy-gamma
|
||||||
|
- LEMMY_RATE_LIMIT__POST=99999
|
||||||
|
- LEMMY_RATE_LIMIT__REGISTER=99999
|
||||||
- RUST_BACKTRACE=1
|
- RUST_BACKTRACE=1
|
||||||
- RUST_LOG=debug
|
- RUST_LOG=debug
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|
|
@ -116,7 +116,10 @@ impl Comment {
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
use crate::schema::comment::dsl::*;
|
use crate::schema::comment::dsl::*;
|
||||||
diesel::update(comment.find(comment_id))
|
diesel::update(comment.find(comment_id))
|
||||||
.set(deleted.eq(new_deleted))
|
.set((
|
||||||
|
deleted.eq(new_deleted),
|
||||||
|
updated.eq(naive_now())
|
||||||
|
))
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +130,10 @@ impl Comment {
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
use crate::schema::comment::dsl::*;
|
use crate::schema::comment::dsl::*;
|
||||||
diesel::update(comment.find(comment_id))
|
diesel::update(comment.find(comment_id))
|
||||||
.set(removed.eq(new_removed))
|
.set((
|
||||||
|
removed.eq(new_removed),
|
||||||
|
updated.eq(naive_now())
|
||||||
|
))
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,10 @@ impl Community {
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
use crate::schema::community::dsl::*;
|
use crate::schema::community::dsl::*;
|
||||||
diesel::update(community.find(community_id))
|
diesel::update(community.find(community_id))
|
||||||
.set(deleted.eq(new_deleted))
|
.set((
|
||||||
|
deleted.eq(new_deleted),
|
||||||
|
updated.eq(naive_now())
|
||||||
|
))
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +121,10 @@ impl Community {
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
use crate::schema::community::dsl::*;
|
use crate::schema::community::dsl::*;
|
||||||
diesel::update(community.find(community_id))
|
diesel::update(community.find(community_id))
|
||||||
.set(removed.eq(new_removed))
|
.set((
|
||||||
|
removed.eq(new_removed),
|
||||||
|
updated.eq(naive_now())
|
||||||
|
))
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,10 @@ impl Post {
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
use crate::schema::post::dsl::*;
|
use crate::schema::post::dsl::*;
|
||||||
diesel::update(post.find(post_id))
|
diesel::update(post.find(post_id))
|
||||||
.set(deleted.eq(new_deleted))
|
.set((
|
||||||
|
deleted.eq(new_deleted),
|
||||||
|
updated.eq(naive_now())
|
||||||
|
))
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +133,10 @@ impl Post {
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
use crate::schema::post::dsl::*;
|
use crate::schema::post::dsl::*;
|
||||||
diesel::update(post.find(post_id))
|
diesel::update(post.find(post_id))
|
||||||
.set(removed.eq(new_removed))
|
.set((
|
||||||
|
removed.eq(new_removed),
|
||||||
|
updated.eq(naive_now())
|
||||||
|
))
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{claims::Claims, is_mod_or_admin, APIError, Oper, Perform},
|
api::{
|
||||||
|
check_community_ban,
|
||||||
|
get_user_from_jwt,
|
||||||
|
get_user_from_jwt_opt,
|
||||||
|
is_mod_or_admin,
|
||||||
|
APIError,
|
||||||
|
Oper,
|
||||||
|
Perform,
|
||||||
|
},
|
||||||
apub::{ApubLikeableType, ApubObjectType},
|
apub::{ApubLikeableType, ApubObjectType},
|
||||||
blocking,
|
blocking,
|
||||||
websocket::{
|
websocket::{
|
||||||
|
@ -13,7 +21,6 @@ use crate::{
|
||||||
use lemmy_db::{
|
use lemmy_db::{
|
||||||
comment::*,
|
comment::*,
|
||||||
comment_view::*,
|
comment_view::*,
|
||||||
community_view::*,
|
|
||||||
moderator::*,
|
moderator::*,
|
||||||
post::*,
|
post::*,
|
||||||
site_view::*,
|
site_view::*,
|
||||||
|
@ -123,13 +130,7 @@ impl Perform for Oper<CreateComment> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &CreateComment = &self.data;
|
let data: &CreateComment = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let content_slurs_removed = remove_slurs(&data.content.to_owned());
|
let content_slurs_removed = remove_slurs(&data.content.to_owned());
|
||||||
|
|
||||||
|
@ -137,7 +138,7 @@ impl Perform for Oper<CreateComment> {
|
||||||
content: content_slurs_removed,
|
content: content_slurs_removed,
|
||||||
parent_id: data.parent_id.to_owned(),
|
parent_id: data.parent_id.to_owned(),
|
||||||
post_id: data.post_id,
|
post_id: data.post_id,
|
||||||
creator_id: user_id,
|
creator_id: user.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
read: None,
|
read: None,
|
||||||
|
@ -151,18 +152,7 @@ impl Perform for Oper<CreateComment> {
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
|
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
|
||||||
|
|
||||||
let community_id = post.community_id;
|
check_community_ban(user.id, post.community_id, pool).await?;
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(&conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if post is locked, no new comments
|
// Check if post is locked, no new comments
|
||||||
if post.locked {
|
if post.locked {
|
||||||
|
@ -203,7 +193,7 @@ impl Perform for Oper<CreateComment> {
|
||||||
let like_form = CommentLikeForm {
|
let like_form = CommentLikeForm {
|
||||||
comment_id: inserted_comment.id,
|
comment_id: inserted_comment.id,
|
||||||
post_id: data.post_id,
|
post_id: data.post_id,
|
||||||
user_id,
|
user_id: user.id,
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -214,6 +204,7 @@ impl Perform for Oper<CreateComment> {
|
||||||
|
|
||||||
updated_comment.send_like(&user, &self.client, pool).await?;
|
updated_comment.send_like(&user, &self.client, pool).await?;
|
||||||
|
|
||||||
|
let user_id = user.id;
|
||||||
let comment_view = blocking(pool, move |conn| {
|
let comment_view = blocking(pool, move |conn| {
|
||||||
CommentView::read(&conn, inserted_comment.id, Some(user_id))
|
CommentView::read(&conn, inserted_comment.id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -251,34 +242,16 @@ impl Perform for Oper<EditComment> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &EditComment = &self.data;
|
let data: &EditComment = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_comment =
|
let orig_comment =
|
||||||
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
|
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
|
||||||
|
|
||||||
// Check for a site ban
|
check_community_ban(user.id, orig_comment.community_id, pool).await?;
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let community_id = orig_comment.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that only the creator can edit
|
// Verify that only the creator can edit
|
||||||
if user_id != orig_comment.creator_id {
|
if user.id != orig_comment.creator_id {
|
||||||
return Err(APIError::err("no_comment_edit_allowed").into());
|
return Err(APIError::err("no_comment_edit_allowed").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,6 +282,7 @@ impl Perform for Oper<EditComment> {
|
||||||
send_local_notifs(mentions, updated_comment, &user, post, pool, false).await?;
|
send_local_notifs(mentions, updated_comment, &user, post, pool, false).await?;
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
let user_id = user.id;
|
||||||
let comment_view = blocking(pool, move |conn| {
|
let comment_view = blocking(pool, move |conn| {
|
||||||
CommentView::read(conn, edit_id, Some(user_id))
|
CommentView::read(conn, edit_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -346,34 +320,16 @@ impl Perform for Oper<DeleteComment> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &DeleteComment = &self.data;
|
let data: &DeleteComment = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_comment =
|
let orig_comment =
|
||||||
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
|
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
|
||||||
|
|
||||||
// Check for a site ban
|
check_community_ban(user.id, orig_comment.community_id, pool).await?;
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let community_id = orig_comment.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that only the creator can delete
|
// Verify that only the creator can delete
|
||||||
if user_id != orig_comment.creator_id {
|
if user.id != orig_comment.creator_id {
|
||||||
return Err(APIError::err("no_comment_edit_allowed").into());
|
return Err(APIError::err("no_comment_edit_allowed").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,6 +357,7 @@ impl Perform for Oper<DeleteComment> {
|
||||||
|
|
||||||
// Refetch it
|
// Refetch it
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
let user_id = user.id;
|
||||||
let comment_view = blocking(pool, move |conn| {
|
let comment_view = blocking(pool, move |conn| {
|
||||||
CommentView::read(conn, edit_id, Some(user_id))
|
CommentView::read(conn, edit_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -445,34 +402,16 @@ impl Perform for Oper<RemoveComment> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &RemoveComment = &self.data;
|
let data: &RemoveComment = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_comment =
|
let orig_comment =
|
||||||
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
|
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
|
||||||
|
|
||||||
// Check for a site ban
|
check_community_ban(user.id, orig_comment.community_id, pool).await?;
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let community_id = orig_comment.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that only a mod or admin can remove
|
// Verify that only a mod or admin can remove
|
||||||
is_mod_or_admin(pool, user_id, community_id).await?;
|
is_mod_or_admin(pool, user.id, orig_comment.community_id).await?;
|
||||||
|
|
||||||
// Do the remove
|
// Do the remove
|
||||||
let removed = data.removed;
|
let removed = data.removed;
|
||||||
|
@ -487,7 +426,7 @@ impl Perform for Oper<RemoveComment> {
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
let form = ModRemoveCommentForm {
|
let form = ModRemoveCommentForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
comment_id: data.edit_id,
|
comment_id: data.edit_id,
|
||||||
removed: Some(removed),
|
removed: Some(removed),
|
||||||
reason: data.reason.to_owned(),
|
reason: data.reason.to_owned(),
|
||||||
|
@ -507,6 +446,7 @@ impl Perform for Oper<RemoveComment> {
|
||||||
|
|
||||||
// Refetch it
|
// Refetch it
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
let user_id = user.id;
|
||||||
let comment_view = blocking(pool, move |conn| {
|
let comment_view = blocking(pool, move |conn| {
|
||||||
CommentView::read(conn, edit_id, Some(user_id))
|
CommentView::read(conn, edit_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -551,31 +491,13 @@ impl Perform for Oper<MarkCommentAsRead> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &MarkCommentAsRead = &self.data;
|
let data: &MarkCommentAsRead = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_comment =
|
let orig_comment =
|
||||||
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
|
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
|
||||||
|
|
||||||
// Check for a site ban
|
check_community_ban(user.id, orig_comment.community_id, pool).await?;
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let community_id = orig_comment.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that only the recipient can mark as read
|
// Verify that only the recipient can mark as read
|
||||||
// Needs to fetch the parent comment / post to get the recipient
|
// Needs to fetch the parent comment / post to get the recipient
|
||||||
|
@ -584,14 +506,14 @@ impl Perform for Oper<MarkCommentAsRead> {
|
||||||
Some(pid) => {
|
Some(pid) => {
|
||||||
let parent_comment =
|
let parent_comment =
|
||||||
blocking(pool, move |conn| CommentView::read(&conn, pid, None)).await??;
|
blocking(pool, move |conn| CommentView::read(&conn, pid, None)).await??;
|
||||||
if user_id != parent_comment.creator_id {
|
if user.id != parent_comment.creator_id {
|
||||||
return Err(APIError::err("no_comment_edit_allowed").into());
|
return Err(APIError::err("no_comment_edit_allowed").into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let parent_post_id = orig_comment.post_id;
|
let parent_post_id = orig_comment.post_id;
|
||||||
let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
|
let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
|
||||||
if user_id != parent_post.creator_id {
|
if user.id != parent_post.creator_id {
|
||||||
return Err(APIError::err("no_comment_edit_allowed").into());
|
return Err(APIError::err("no_comment_edit_allowed").into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -606,6 +528,7 @@ impl Perform for Oper<MarkCommentAsRead> {
|
||||||
|
|
||||||
// Refetch it
|
// Refetch it
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
let user_id = user.id;
|
||||||
let comment_view = blocking(pool, move |conn| {
|
let comment_view = blocking(pool, move |conn| {
|
||||||
CommentView::read(conn, edit_id, Some(user_id))
|
CommentView::read(conn, edit_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -631,17 +554,11 @@ impl Perform for Oper<SaveComment> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &SaveComment = &self.data;
|
let data: &SaveComment = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let comment_saved_form = CommentSavedForm {
|
let comment_saved_form = CommentSavedForm {
|
||||||
comment_id: data.comment_id,
|
comment_id: data.comment_id,
|
||||||
user_id,
|
user_id: user.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
if data.save {
|
if data.save {
|
||||||
|
@ -657,6 +574,7 @@ impl Perform for Oper<SaveComment> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
let comment_id = data.comment_id;
|
||||||
|
let user_id = user.id;
|
||||||
let comment_view = blocking(pool, move |conn| {
|
let comment_view = blocking(pool, move |conn| {
|
||||||
CommentView::read(conn, comment_id, Some(user_id))
|
CommentView::read(conn, comment_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -680,13 +598,7 @@ impl Perform for Oper<CreateCommentLike> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommentResponse, LemmyError> {
|
) -> Result<CommentResponse, LemmyError> {
|
||||||
let data: &CreateCommentLike = &self.data;
|
let data: &CreateCommentLike = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let mut recipient_ids = Vec::new();
|
let mut recipient_ids = Vec::new();
|
||||||
|
|
||||||
|
@ -702,21 +614,9 @@ impl Perform for Oper<CreateCommentLike> {
|
||||||
let orig_comment =
|
let orig_comment =
|
||||||
blocking(pool, move |conn| CommentView::read(&conn, comment_id, None)).await??;
|
blocking(pool, move |conn| CommentView::read(&conn, comment_id, None)).await??;
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let post_id = orig_comment.post_id;
|
let post_id = orig_comment.post_id;
|
||||||
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
|
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
|
||||||
let community_id = post.community_id;
|
check_community_ban(user.id, post.community_id, pool).await?;
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
let comment_id = data.comment_id;
|
||||||
let comment = blocking(pool, move |conn| Comment::read(conn, comment_id)).await??;
|
let comment = blocking(pool, move |conn| Comment::read(conn, comment_id)).await??;
|
||||||
|
@ -725,7 +625,7 @@ impl Perform for Oper<CreateCommentLike> {
|
||||||
match comment.parent_id {
|
match comment.parent_id {
|
||||||
Some(parent_id) => {
|
Some(parent_id) => {
|
||||||
let parent_comment = blocking(pool, move |conn| Comment::read(conn, parent_id)).await??;
|
let parent_comment = blocking(pool, move |conn| Comment::read(conn, parent_id)).await??;
|
||||||
if parent_comment.creator_id != user_id {
|
if parent_comment.creator_id != user.id {
|
||||||
let parent_user = blocking(pool, move |conn| {
|
let parent_user = blocking(pool, move |conn| {
|
||||||
User_::read(conn, parent_comment.creator_id)
|
User_::read(conn, parent_comment.creator_id)
|
||||||
})
|
})
|
||||||
|
@ -741,7 +641,7 @@ impl Perform for Oper<CreateCommentLike> {
|
||||||
let like_form = CommentLikeForm {
|
let like_form = CommentLikeForm {
|
||||||
comment_id: data.comment_id,
|
comment_id: data.comment_id,
|
||||||
post_id,
|
post_id,
|
||||||
user_id,
|
user_id: user.id,
|
||||||
score: data.score,
|
score: data.score,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -769,6 +669,7 @@ impl Perform for Oper<CreateCommentLike> {
|
||||||
|
|
||||||
// Have to refetch the comment to get the current state
|
// Have to refetch the comment to get the current state
|
||||||
let comment_id = data.comment_id;
|
let comment_id = data.comment_id;
|
||||||
|
let user_id = user.id;
|
||||||
let liked_comment = blocking(pool, move |conn| {
|
let liked_comment = blocking(pool, move |conn| {
|
||||||
CommentView::read(conn, comment_id, Some(user_id))
|
CommentView::read(conn, comment_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -806,19 +707,8 @@ impl Perform for Oper<GetComments> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetCommentsResponse, LemmyError> {
|
) -> Result<GetCommentsResponse, LemmyError> {
|
||||||
let data: &GetComments = &self.data;
|
let data: &GetComments = &self.data;
|
||||||
|
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
|
||||||
let user_claims: Option<Claims> = match &data.auth {
|
let user_id = user.map(|u| u.id);
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
|
||||||
Ok(claims) => Some(claims.claims),
|
|
||||||
Err(_e) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = match &user_claims {
|
|
||||||
Some(claims) => Some(claims.id),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_ = ListingType::from_str(&data.type_)?;
|
let type_ = ListingType::from_str(&data.type_)?;
|
||||||
let sort = SortType::from_str(&data.sort)?;
|
let sort = SortType::from_str(&data.sort)?;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{claims::Claims, is_admin, is_mod_or_admin, APIError, Oper, Perform},
|
api::{is_admin, is_mod_or_admin, APIError, Oper, Perform},
|
||||||
apub::ActorType,
|
apub::ActorType,
|
||||||
blocking,
|
blocking,
|
||||||
websocket::{
|
websocket::{
|
||||||
|
@ -16,8 +16,6 @@ use lemmy_utils::{
|
||||||
is_valid_community_name,
|
is_valid_community_name,
|
||||||
make_apub_endpoint,
|
make_apub_endpoint,
|
||||||
naive_from_unix,
|
naive_from_unix,
|
||||||
slur_check,
|
|
||||||
slurs_vec_to_str,
|
|
||||||
EndpointType,
|
EndpointType,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -154,17 +152,8 @@ impl Perform for Oper<GetCommunity> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetCommunityResponse, LemmyError> {
|
) -> Result<GetCommunityResponse, LemmyError> {
|
||||||
let data: &GetCommunity = &self.data;
|
let data: &GetCommunity = &self.data;
|
||||||
|
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
|
||||||
let user_id: Option<i32> = match &data.auth {
|
let user_id = user.map(|u| u.id);
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
|
||||||
Ok(claims) => {
|
|
||||||
let user_id = claims.claims.id;
|
|
||||||
Some(user_id)
|
|
||||||
}
|
|
||||||
Err(_e) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
|
let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
|
||||||
let community = match data.id {
|
let community = match data.id {
|
||||||
|
@ -234,38 +223,16 @@ impl Perform for Oper<CreateCommunity> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &CreateCommunity = &self.data;
|
let data: &CreateCommunity = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
check_slurs(&data.name)?;
|
||||||
Ok(claims) => claims.claims,
|
check_slurs(&data.title)?;
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
check_slurs_opt(&data.description)?;
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(slurs) = slur_check(&data.name) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(slurs) = slur_check(&data.title) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(description) = &data.description {
|
|
||||||
if let Err(slurs) = slur_check(description) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_valid_community_name(&data.name) {
|
if !is_valid_community_name(&data.name) {
|
||||||
return Err(APIError::err("invalid_community_name").into());
|
return Err(APIError::err("invalid_community_name").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user_view = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
|
|
||||||
if user_view.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double check for duplicate community actor_ids
|
// Double check for duplicate community actor_ids
|
||||||
let actor_id = make_apub_endpoint(EndpointType::Community, &data.name).to_string();
|
let actor_id = make_apub_endpoint(EndpointType::Community, &data.name).to_string();
|
||||||
let actor_id_cloned = actor_id.to_owned();
|
let actor_id_cloned = actor_id.to_owned();
|
||||||
|
@ -285,7 +252,7 @@ impl Perform for Oper<CreateCommunity> {
|
||||||
title: data.title.to_owned(),
|
title: data.title.to_owned(),
|
||||||
description: data.description.to_owned(),
|
description: data.description.to_owned(),
|
||||||
category_id: data.category_id,
|
category_id: data.category_id,
|
||||||
creator_id: user_id,
|
creator_id: user.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
nsfw: data.nsfw,
|
nsfw: data.nsfw,
|
||||||
|
@ -306,7 +273,7 @@ impl Perform for Oper<CreateCommunity> {
|
||||||
|
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
user_id,
|
user_id: user.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
|
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
|
||||||
|
@ -316,7 +283,7 @@ impl Perform for Oper<CreateCommunity> {
|
||||||
|
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
user_id,
|
user_id: user.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
|
let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
|
||||||
|
@ -324,6 +291,7 @@ impl Perform for Oper<CreateCommunity> {
|
||||||
return Err(APIError::err("community_follower_already_exists").into());
|
return Err(APIError::err("community_follower_already_exists").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let user_id = user.id;
|
||||||
let community_view = blocking(pool, move |conn| {
|
let community_view = blocking(pool, move |conn| {
|
||||||
CommunityView::read(conn, inserted_community.id, Some(user_id))
|
CommunityView::read(conn, inserted_community.id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -345,29 +313,10 @@ impl Perform for Oper<EditCommunity> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &EditCommunity = &self.data;
|
let data: &EditCommunity = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
|
|
||||||
if let Err(slurs) = slur_check(&data.title) {
|
check_slurs(&data.title)?;
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
check_slurs_opt(&data.description)?;
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(description) = &data.description {
|
|
||||||
if let Err(slurs) = slur_check(description) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify its a mod (only mods can edit it)
|
// Verify its a mod (only mods can edit it)
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
@ -376,7 +325,7 @@ impl Perform for Oper<EditCommunity> {
|
||||||
.map(|v| v.into_iter().map(|m| m.user_id).collect())
|
.map(|v| v.into_iter().map(|m| m.user_id).collect())
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
if !mods.contains(&user_id) {
|
if !mods.contains(&user.id) {
|
||||||
return Err(APIError::err("not_a_moderator").into());
|
return Err(APIError::err("not_a_moderator").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,6 +364,7 @@ impl Perform for Oper<EditCommunity> {
|
||||||
// process for communities and users
|
// process for communities and users
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
let user_id = user.id;
|
||||||
let community_view = blocking(pool, move |conn| {
|
let community_view = blocking(pool, move |conn| {
|
||||||
CommunityView::read(conn, edit_id, Some(user_id))
|
CommunityView::read(conn, edit_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -440,24 +390,12 @@ impl Perform for Oper<DeleteCommunity> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &DeleteCommunity = &self.data;
|
let data: &DeleteCommunity = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify its the creator (only a creator can delete the community)
|
// Verify its the creator (only a creator can delete the community)
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let read_community = blocking(pool, move |conn| Community::read(conn, edit_id)).await??;
|
let read_community = blocking(pool, move |conn| Community::read(conn, edit_id)).await??;
|
||||||
if read_community.creator_id != user_id {
|
if read_community.creator_id != user.id {
|
||||||
return Err(APIError::err("no_community_edit_allowed").into());
|
return Err(APIError::err("no_community_edit_allowed").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,6 +423,7 @@ impl Perform for Oper<DeleteCommunity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
let user_id = user.id;
|
||||||
let community_view = blocking(pool, move |conn| {
|
let community_view = blocking(pool, move |conn| {
|
||||||
CommunityView::read(conn, edit_id, Some(user_id))
|
CommunityView::read(conn, edit_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -510,22 +449,10 @@ impl Perform for Oper<RemoveCommunity> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &RemoveCommunity = &self.data;
|
let data: &RemoveCommunity = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify its an admin (only an admin can remove a community)
|
// Verify its an admin (only an admin can remove a community)
|
||||||
is_admin(pool, user_id).await?;
|
is_admin(pool, user.id).await?;
|
||||||
|
|
||||||
// Do the remove
|
// Do the remove
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
@ -545,7 +472,7 @@ impl Perform for Oper<RemoveCommunity> {
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
let form = ModRemoveCommunityForm {
|
let form = ModRemoveCommunityForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
community_id: data.edit_id,
|
community_id: data.edit_id,
|
||||||
removed: Some(removed),
|
removed: Some(removed),
|
||||||
reason: data.reason.to_owned(),
|
reason: data.reason.to_owned(),
|
||||||
|
@ -565,6 +492,7 @@ impl Perform for Oper<RemoveCommunity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
let user_id = user.id;
|
||||||
let community_view = blocking(pool, move |conn| {
|
let community_view = blocking(pool, move |conn| {
|
||||||
CommunityView::read(conn, edit_id, Some(user_id))
|
CommunityView::read(conn, edit_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -590,19 +518,7 @@ impl Perform for Oper<ListCommunities> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<ListCommunitiesResponse, LemmyError> {
|
) -> Result<ListCommunitiesResponse, LemmyError> {
|
||||||
let data: &ListCommunities = &self.data;
|
let data: &ListCommunities = &self.data;
|
||||||
|
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
|
||||||
// For logged in users, you need to get back subscribed, and settings
|
|
||||||
let user: Option<User_> = match &data.auth {
|
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
|
||||||
Ok(claims) => {
|
|
||||||
let user_id = claims.claims.id;
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
Some(user)
|
|
||||||
}
|
|
||||||
Err(_e) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = match &user {
|
let user_id = match &user {
|
||||||
Some(user) => Some(user.id),
|
Some(user) => Some(user.id),
|
||||||
|
@ -644,19 +560,13 @@ impl Perform for Oper<FollowCommunity> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<CommunityResponse, LemmyError> {
|
||||||
let data: &FollowCommunity = &self.data;
|
let data: &FollowCommunity = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
|
let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
user_id,
|
user_id: user.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
if community.local {
|
if community.local {
|
||||||
|
@ -672,10 +582,7 @@ impl Perform for Oper<FollowCommunity> {
|
||||||
return Err(APIError::err("community_follower_already_exists").into());
|
return Err(APIError::err("community_follower_already_exists").into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if data.follow {
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
|
|
||||||
if data.follow {
|
|
||||||
// Dont actually add to the community followers here, because you need
|
// Dont actually add to the community followers here, because you need
|
||||||
// to wait for the accept
|
// to wait for the accept
|
||||||
user
|
user
|
||||||
|
@ -685,16 +592,15 @@ impl Perform for Oper<FollowCommunity> {
|
||||||
user
|
user
|
||||||
.send_unfollow(&community.actor_id, &self.client, pool)
|
.send_unfollow(&community.actor_id, &self.client, pool)
|
||||||
.await?;
|
.await?;
|
||||||
let unfollow =
|
let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
|
||||||
move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
|
|
||||||
if blocking(pool, unfollow).await?.is_err() {
|
if blocking(pool, unfollow).await?.is_err() {
|
||||||
return Err(APIError::err("community_follower_already_exists").into());
|
return Err(APIError::err("community_follower_already_exists").into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: this needs to return a "pending" state, until Accept is received from the remote server
|
// TODO: this needs to return a "pending" state, until Accept is received from the remote server
|
||||||
}
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
let user_id = user.id;
|
||||||
let community_view = blocking(pool, move |conn| {
|
let community_view = blocking(pool, move |conn| {
|
||||||
CommunityView::read(conn, community_id, Some(user_id))
|
CommunityView::read(conn, community_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -716,14 +622,9 @@ impl Perform for Oper<GetFollowedCommunities> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetFollowedCommunitiesResponse, LemmyError> {
|
) -> Result<GetFollowedCommunitiesResponse, LemmyError> {
|
||||||
let data: &GetFollowedCommunities = &self.data;
|
let data: &GetFollowedCommunities = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let user_id = user.id;
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let communities = match blocking(pool, move |conn| {
|
let communities = match blocking(pool, move |conn| {
|
||||||
CommunityFollowerView::for_user(conn, user_id)
|
CommunityFollowerView::for_user(conn, user_id)
|
||||||
})
|
})
|
||||||
|
@ -748,18 +649,12 @@ impl Perform for Oper<BanFromCommunity> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<BanFromCommunityResponse, LemmyError> {
|
) -> Result<BanFromCommunityResponse, LemmyError> {
|
||||||
let data: &BanFromCommunity = &self.data;
|
let data: &BanFromCommunity = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
|
||||||
// Verify that only mods or admins can ban
|
// Verify that only mods or admins can ban
|
||||||
is_mod_or_admin(pool, user_id, community_id).await?;
|
is_mod_or_admin(pool, user.id, community_id).await?;
|
||||||
|
|
||||||
let community_user_ban_form = CommunityUserBanForm {
|
let community_user_ban_form = CommunityUserBanForm {
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
|
@ -786,7 +681,7 @@ impl Perform for Oper<BanFromCommunity> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let form = ModBanFromCommunityForm {
|
let form = ModBanFromCommunityForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
other_user_id: data.user_id,
|
other_user_id: data.user_id,
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
reason: data.reason.to_owned(),
|
reason: data.reason.to_owned(),
|
||||||
|
@ -826,13 +721,7 @@ impl Perform for Oper<AddModToCommunity> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<AddModToCommunityResponse, LemmyError> {
|
) -> Result<AddModToCommunityResponse, LemmyError> {
|
||||||
let data: &AddModToCommunity = &self.data;
|
let data: &AddModToCommunity = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
|
@ -842,7 +731,7 @@ impl Perform for Oper<AddModToCommunity> {
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
|
||||||
// Verify that only mods or admins can add mod
|
// Verify that only mods or admins can add mod
|
||||||
is_mod_or_admin(pool, user_id, community_id).await?;
|
is_mod_or_admin(pool, user.id, community_id).await?;
|
||||||
|
|
||||||
if data.added {
|
if data.added {
|
||||||
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
|
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
|
||||||
|
@ -858,7 +747,7 @@ impl Perform for Oper<AddModToCommunity> {
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
let form = ModAddCommunityForm {
|
let form = ModAddCommunityForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
other_user_id: data.user_id,
|
other_user_id: data.user_id,
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
removed: Some(!data.added),
|
removed: Some(!data.added),
|
||||||
|
@ -896,13 +785,7 @@ impl Perform for Oper<TransferCommunity> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetCommunityResponse, LemmyError> {
|
) -> Result<GetCommunityResponse, LemmyError> {
|
||||||
let data: &TransferCommunity = &self.data;
|
let data: &TransferCommunity = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let read_community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
|
let read_community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
|
||||||
|
@ -917,7 +800,7 @@ impl Perform for Oper<TransferCommunity> {
|
||||||
admins.insert(0, creator_user);
|
admins.insert(0, creator_user);
|
||||||
|
|
||||||
// Make sure user is the creator, or an admin
|
// Make sure user is the creator, or an admin
|
||||||
if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) {
|
if user.id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user.id) {
|
||||||
return Err(APIError::err("not_an_admin").into());
|
return Err(APIError::err("not_an_admin").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -962,7 +845,7 @@ impl Perform for Oper<TransferCommunity> {
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
let form = ModAddCommunityForm {
|
let form = ModAddCommunityForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
other_user_id: data.user_id,
|
other_user_id: data.user_id,
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
removed: Some(false),
|
removed: Some(false),
|
||||||
|
@ -970,6 +853,7 @@ impl Perform for Oper<TransferCommunity> {
|
||||||
blocking(pool, move |conn| ModAddCommunity::create(conn, &form)).await??;
|
blocking(pool, move |conn| ModAddCommunity::create(conn, &form)).await??;
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
let user_id = user.id;
|
||||||
let community_view = match blocking(pool, move |conn| {
|
let community_view = match blocking(pool, move |conn| {
|
||||||
CommunityView::read(conn, community_id, Some(user_id))
|
CommunityView::read(conn, community_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{blocking, websocket::WebsocketInfo, DbPool, LemmyError};
|
use crate::{api::claims::Claims, blocking, websocket::WebsocketInfo, DbPool, LemmyError};
|
||||||
use actix_web::client::Client;
|
use actix_web::client::Client;
|
||||||
use lemmy_db::{
|
use lemmy_db::{
|
||||||
community::*,
|
community::*,
|
||||||
|
@ -9,6 +9,7 @@ use lemmy_db::{
|
||||||
user_view::*,
|
user_view::*,
|
||||||
Crud,
|
Crud,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::{slur_check, slurs_vec_to_str};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub mod claims;
|
pub mod claims;
|
||||||
|
@ -75,3 +76,56 @@ pub async fn is_admin(pool: &DbPool, user_id: i32) -> Result<(), LemmyError> {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in crate::api) async fn get_user_from_jwt(
|
||||||
|
jwt: &str,
|
||||||
|
pool: &DbPool,
|
||||||
|
) -> Result<User_, LemmyError> {
|
||||||
|
let claims = match Claims::decode(&jwt) {
|
||||||
|
Ok(claims) => claims.claims,
|
||||||
|
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
||||||
|
};
|
||||||
|
let user_id = claims.id;
|
||||||
|
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
||||||
|
// Check for a site ban
|
||||||
|
if user.banned {
|
||||||
|
return Err(APIError::err("site_ban").into());
|
||||||
|
}
|
||||||
|
Ok(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::api) async fn get_user_from_jwt_opt(
|
||||||
|
jwt: &Option<String>,
|
||||||
|
pool: &DbPool,
|
||||||
|
) -> Result<Option<User_>, LemmyError> {
|
||||||
|
match jwt {
|
||||||
|
Some(jwt) => Ok(Some(get_user_from_jwt(jwt, pool).await?)),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::api) fn check_slurs(text: &str) -> Result<(), APIError> {
|
||||||
|
if let Err(slurs) = slur_check(text) {
|
||||||
|
Err(APIError::err(&slurs_vec_to_str(slurs)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(in crate::api) fn check_slurs_opt(text: &Option<String>) -> Result<(), APIError> {
|
||||||
|
match text {
|
||||||
|
Some(t) => check_slurs(t),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(in crate::api) async fn check_community_ban(
|
||||||
|
user_id: i32,
|
||||||
|
community_id: i32,
|
||||||
|
pool: &DbPool,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let is_banned = move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
||||||
|
if blocking(pool, is_banned).await? {
|
||||||
|
Err(APIError::err("community_ban").into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{claims::Claims, is_mod_or_admin, APIError, Oper, Perform},
|
api::{
|
||||||
|
check_community_ban,
|
||||||
|
check_slurs,
|
||||||
|
check_slurs_opt,
|
||||||
|
get_user_from_jwt,
|
||||||
|
get_user_from_jwt_opt,
|
||||||
|
is_mod_or_admin,
|
||||||
|
APIError,
|
||||||
|
Oper,
|
||||||
|
Perform,
|
||||||
|
},
|
||||||
apub::{ApubLikeableType, ApubObjectType},
|
apub::{ApubLikeableType, ApubObjectType},
|
||||||
blocking,
|
blocking,
|
||||||
fetch_iframely_and_pictrs_data,
|
fetch_iframely_and_pictrs_data,
|
||||||
|
@ -19,20 +29,13 @@ use lemmy_db::{
|
||||||
post::*,
|
post::*,
|
||||||
post_view::*,
|
post_view::*,
|
||||||
site_view::*,
|
site_view::*,
|
||||||
user::*,
|
|
||||||
Crud,
|
Crud,
|
||||||
Likeable,
|
Likeable,
|
||||||
ListingType,
|
ListingType,
|
||||||
Saveable,
|
Saveable,
|
||||||
SortType,
|
SortType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{is_valid_post_title, make_apub_endpoint, EndpointType};
|
||||||
is_valid_post_title,
|
|
||||||
make_apub_endpoint,
|
|
||||||
slur_check,
|
|
||||||
slurs_vec_to_str,
|
|
||||||
EndpointType,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -146,41 +149,16 @@ impl Perform for Oper<CreatePost> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &CreatePost = &self.data;
|
let data: &CreatePost = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
check_slurs(&data.name)?;
|
||||||
Ok(claims) => claims.claims,
|
check_slurs_opt(&data.body)?;
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(slurs) = slur_check(&data.name) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(body) = &data.body {
|
|
||||||
if let Err(slurs) = slur_check(body) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_valid_post_title(&data.name) {
|
if !is_valid_post_title(&data.name) {
|
||||||
return Err(APIError::err("invalid_post_title").into());
|
return Err(APIError::err("invalid_post_title").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_id = claims.id;
|
check_community_ban(user.id, data.community_id, pool).await?;
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let community_id = data.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(url) = data.url.as_ref() {
|
if let Some(url) = data.url.as_ref() {
|
||||||
match Url::parse(url) {
|
match Url::parse(url) {
|
||||||
|
@ -198,7 +176,7 @@ impl Perform for Oper<CreatePost> {
|
||||||
url: data.url.to_owned(),
|
url: data.url.to_owned(),
|
||||||
body: data.body.to_owned(),
|
body: data.body.to_owned(),
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
creator_id: user_id,
|
creator_id: user.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
nsfw: data.nsfw,
|
nsfw: data.nsfw,
|
||||||
|
@ -244,7 +222,7 @@ impl Perform for Oper<CreatePost> {
|
||||||
// They like their own post by default
|
// They like their own post by default
|
||||||
let like_form = PostLikeForm {
|
let like_form = PostLikeForm {
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
user_id,
|
user_id: user.id,
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -258,7 +236,7 @@ impl Perform for Oper<CreatePost> {
|
||||||
// Refetch the view
|
// Refetch the view
|
||||||
let inserted_post_id = inserted_post.id;
|
let inserted_post_id = inserted_post.id;
|
||||||
let post_view = match blocking(pool, move |conn| {
|
let post_view = match blocking(pool, move |conn| {
|
||||||
PostView::read(conn, inserted_post_id, Some(user_id))
|
PostView::read(conn, inserted_post_id, Some(user.id))
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
@ -290,17 +268,8 @@ impl Perform for Oper<GetPost> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetPostResponse, LemmyError> {
|
) -> Result<GetPostResponse, LemmyError> {
|
||||||
let data: &GetPost = &self.data;
|
let data: &GetPost = &self.data;
|
||||||
|
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
|
||||||
let user_id: Option<i32> = match &data.auth {
|
let user_id = user.map(|u| u.id);
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
|
||||||
Ok(claims) => {
|
|
||||||
let user_id = claims.claims.id;
|
|
||||||
Some(user_id)
|
|
||||||
}
|
|
||||||
Err(_e) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = data.id;
|
let id = data.id;
|
||||||
let post_view = match blocking(pool, move |conn| PostView::read(conn, id, user_id)).await? {
|
let post_view = match blocking(pool, move |conn| PostView::read(conn, id, user_id)).await? {
|
||||||
|
@ -369,19 +338,7 @@ impl Perform for Oper<GetPosts> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetPostsResponse, LemmyError> {
|
) -> Result<GetPostsResponse, LemmyError> {
|
||||||
let data: &GetPosts = &self.data;
|
let data: &GetPosts = &self.data;
|
||||||
|
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
|
||||||
// For logged in users, you need to get back subscribed, and settings
|
|
||||||
let user: Option<User_> = match &data.auth {
|
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
|
||||||
Ok(claims) => {
|
|
||||||
let user_id = claims.claims.id;
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
Some(user)
|
|
||||||
}
|
|
||||||
Err(_e) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = match &user {
|
let user_id = match &user {
|
||||||
Some(user) => Some(user.id),
|
Some(user) => Some(user.id),
|
||||||
|
@ -446,13 +403,7 @@ impl Perform for Oper<CreatePostLike> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &CreatePostLike = &self.data;
|
let data: &CreatePostLike = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Don't do a downvote if site has downvotes disabled
|
// Don't do a downvote if site has downvotes disabled
|
||||||
if data.score == -1 {
|
if data.score == -1 {
|
||||||
|
@ -466,22 +417,11 @@ impl Perform for Oper<CreatePostLike> {
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
|
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
|
||||||
|
|
||||||
let community_id = post.community_id;
|
check_community_ban(user.id, post.community_id, pool).await?;
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let like_form = PostLikeForm {
|
let like_form = PostLikeForm {
|
||||||
post_id: data.post_id,
|
post_id: data.post_id,
|
||||||
user_id,
|
user_id: user.id,
|
||||||
score: data.score,
|
score: data.score,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -508,6 +448,7 @@ impl Perform for Oper<CreatePostLike> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
|
let user_id = user.id;
|
||||||
let post_view = match blocking(pool, move |conn| {
|
let post_view = match blocking(pool, move |conn| {
|
||||||
PostView::read(conn, post_id, Some(user_id))
|
PostView::read(conn, post_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -541,47 +482,22 @@ impl Perform for Oper<EditPost> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &EditPost = &self.data;
|
let data: &EditPost = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
|
|
||||||
if let Err(slurs) = slur_check(&data.name) {
|
check_slurs(&data.name)?;
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
check_slurs_opt(&data.body)?;
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(body) = &data.body {
|
|
||||||
if let Err(slurs) = slur_check(body) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_valid_post_title(&data.name) {
|
if !is_valid_post_title(&data.name) {
|
||||||
return Err(APIError::err("invalid_post_title").into());
|
return Err(APIError::err("invalid_post_title").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
||||||
|
|
||||||
// Check for a community ban
|
check_community_ban(user.id, orig_post.community_id, pool).await?;
|
||||||
let community_id = orig_post.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that only the creator can edit
|
// Verify that only the creator can edit
|
||||||
if !Post::is_post_creator(user_id, orig_post.creator_id) {
|
if !Post::is_post_creator(user.id, orig_post.creator_id) {
|
||||||
return Err(APIError::err("no_post_edit_allowed").into());
|
return Err(APIError::err("no_post_edit_allowed").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -630,7 +546,7 @@ impl Perform for Oper<EditPost> {
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let post_view = blocking(pool, move |conn| {
|
let post_view = blocking(pool, move |conn| {
|
||||||
PostView::read(conn, edit_id, Some(user_id))
|
PostView::read(conn, edit_id, Some(user.id))
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
|
@ -658,33 +574,15 @@ impl Perform for Oper<DeletePost> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &DeletePost = &self.data;
|
let data: &DeletePost = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
||||||
|
|
||||||
// Check for a site ban
|
check_community_ban(user.id, orig_post.community_id, pool).await?;
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let community_id = orig_post.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that only the creator can delete
|
// Verify that only the creator can delete
|
||||||
if !Post::is_post_creator(user_id, orig_post.creator_id) {
|
if !Post::is_post_creator(user.id, orig_post.creator_id) {
|
||||||
return Err(APIError::err("no_post_edit_allowed").into());
|
return Err(APIError::err("no_post_edit_allowed").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -708,7 +606,7 @@ impl Perform for Oper<DeletePost> {
|
||||||
// Refetch the post
|
// Refetch the post
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let post_view = blocking(pool, move |conn| {
|
let post_view = blocking(pool, move |conn| {
|
||||||
PostView::read(conn, edit_id, Some(user_id))
|
PostView::read(conn, edit_id, Some(user.id))
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
|
@ -736,33 +634,15 @@ impl Perform for Oper<RemovePost> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &RemovePost = &self.data;
|
let data: &RemovePost = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
||||||
|
|
||||||
// Check for a site ban
|
check_community_ban(user.id, orig_post.community_id, pool).await?;
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let community_id = orig_post.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that only the mods can remove
|
// Verify that only the mods can remove
|
||||||
is_mod_or_admin(pool, user_id, community_id).await?;
|
is_mod_or_admin(pool, user.id, orig_post.community_id).await?;
|
||||||
|
|
||||||
// Update the post
|
// Update the post
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
@ -774,7 +654,7 @@ impl Perform for Oper<RemovePost> {
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
let form = ModRemovePostForm {
|
let form = ModRemovePostForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
post_id: data.edit_id,
|
post_id: data.edit_id,
|
||||||
removed: Some(removed),
|
removed: Some(removed),
|
||||||
reason: data.reason.to_owned(),
|
reason: data.reason.to_owned(),
|
||||||
|
@ -792,6 +672,7 @@ impl Perform for Oper<RemovePost> {
|
||||||
|
|
||||||
// Refetch the post
|
// Refetch the post
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
let user_id = user.id;
|
||||||
let post_view = blocking(pool, move |conn| {
|
let post_view = blocking(pool, move |conn| {
|
||||||
PostView::read(conn, edit_id, Some(user_id))
|
PostView::read(conn, edit_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
@ -821,33 +702,15 @@ impl Perform for Oper<LockPost> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &LockPost = &self.data;
|
let data: &LockPost = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
||||||
|
|
||||||
// Check for a site ban
|
check_community_ban(user.id, orig_post.community_id, pool).await?;
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let community_id = orig_post.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that only the mods can lock
|
// Verify that only the mods can lock
|
||||||
is_mod_or_admin(pool, user_id, community_id).await?;
|
is_mod_or_admin(pool, user.id, orig_post.community_id).await?;
|
||||||
|
|
||||||
// Update the post
|
// Update the post
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
@ -857,7 +720,7 @@ impl Perform for Oper<LockPost> {
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
let form = ModLockPostForm {
|
let form = ModLockPostForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
post_id: data.edit_id,
|
post_id: data.edit_id,
|
||||||
locked: Some(locked),
|
locked: Some(locked),
|
||||||
};
|
};
|
||||||
|
@ -869,7 +732,7 @@ impl Perform for Oper<LockPost> {
|
||||||
// Refetch the post
|
// Refetch the post
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let post_view = blocking(pool, move |conn| {
|
let post_view = blocking(pool, move |conn| {
|
||||||
PostView::read(conn, edit_id, Some(user_id))
|
PostView::read(conn, edit_id, Some(user.id))
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
|
@ -897,33 +760,15 @@ impl Perform for Oper<StickyPost> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &StickyPost = &self.data;
|
let data: &StickyPost = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
let orig_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??;
|
||||||
|
|
||||||
// Check for a site ban
|
check_community_ban(user.id, orig_post.community_id, pool).await?;
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a community ban
|
|
||||||
let community_id = orig_post.community_id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(APIError::err("community_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that only the mods can sticky
|
// Verify that only the mods can sticky
|
||||||
is_mod_or_admin(pool, user_id, community_id).await?;
|
is_mod_or_admin(pool, user.id, orig_post.community_id).await?;
|
||||||
|
|
||||||
// Update the post
|
// Update the post
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
|
@ -935,7 +780,7 @@ impl Perform for Oper<StickyPost> {
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
let form = ModStickyPostForm {
|
let form = ModStickyPostForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
post_id: data.edit_id,
|
post_id: data.edit_id,
|
||||||
stickied: Some(stickied),
|
stickied: Some(stickied),
|
||||||
};
|
};
|
||||||
|
@ -948,7 +793,7 @@ impl Perform for Oper<StickyPost> {
|
||||||
// Refetch the post
|
// Refetch the post
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let post_view = blocking(pool, move |conn| {
|
let post_view = blocking(pool, move |conn| {
|
||||||
PostView::read(conn, edit_id, Some(user_id))
|
PostView::read(conn, edit_id, Some(user.id))
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
|
@ -976,17 +821,11 @@ impl Perform for Oper<SavePost> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<PostResponse, LemmyError> {
|
||||||
let data: &SavePost = &self.data;
|
let data: &SavePost = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let post_saved_form = PostSavedForm {
|
let post_saved_form = PostSavedForm {
|
||||||
post_id: data.post_id,
|
post_id: data.post_id,
|
||||||
user_id,
|
user_id: user.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
if data.save {
|
if data.save {
|
||||||
|
@ -1002,6 +841,7 @@ impl Perform for Oper<SavePost> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
|
let user_id = user.id;
|
||||||
let post_view = blocking(pool, move |conn| {
|
let post_view = blocking(pool, move |conn| {
|
||||||
PostView::read(conn, post_id, Some(user_id))
|
PostView::read(conn, post_id, Some(user_id))
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
use super::user::Register;
|
use super::user::Register;
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{claims::Claims, is_admin, APIError, Oper, Perform},
|
api::{
|
||||||
|
check_slurs,
|
||||||
|
check_slurs_opt,
|
||||||
|
get_user_from_jwt,
|
||||||
|
get_user_from_jwt_opt,
|
||||||
|
is_admin,
|
||||||
|
APIError,
|
||||||
|
Oper,
|
||||||
|
Perform,
|
||||||
|
},
|
||||||
apub::fetcher::search_by_apub_id,
|
apub::fetcher::search_by_apub_id,
|
||||||
blocking,
|
blocking,
|
||||||
version,
|
version,
|
||||||
|
@ -24,7 +33,7 @@ use lemmy_db::{
|
||||||
SearchType,
|
SearchType,
|
||||||
SortType,
|
SortType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::{settings::Settings, slur_check, slurs_vec_to_str};
|
use lemmy_utils::settings::Settings;
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -243,30 +252,18 @@ impl Perform for Oper<CreateSite> {
|
||||||
) -> Result<SiteResponse, LemmyError> {
|
) -> Result<SiteResponse, LemmyError> {
|
||||||
let data: &CreateSite = &self.data;
|
let data: &CreateSite = &self.data;
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(slurs) = slur_check(&data.name) {
|
check_slurs(&data.name)?;
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
check_slurs_opt(&data.description)?;
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(description) = &data.description {
|
|
||||||
if let Err(slurs) = slur_check(description) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(pool, user_id).await?;
|
is_admin(pool, user.id).await?;
|
||||||
|
|
||||||
let site_form = SiteForm {
|
let site_form = SiteForm {
|
||||||
name: data.name.to_owned(),
|
name: data.name.to_owned(),
|
||||||
description: data.description.to_owned(),
|
description: data.description.to_owned(),
|
||||||
creator_id: user_id,
|
creator_id: user.id,
|
||||||
enable_downvotes: data.enable_downvotes,
|
enable_downvotes: data.enable_downvotes,
|
||||||
open_registration: data.open_registration,
|
open_registration: data.open_registration,
|
||||||
enable_nsfw: data.enable_nsfw,
|
enable_nsfw: data.enable_nsfw,
|
||||||
|
@ -293,26 +290,13 @@ impl Perform for Oper<EditSite> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<SiteResponse, LemmyError> {
|
) -> Result<SiteResponse, LemmyError> {
|
||||||
let data: &EditSite = &self.data;
|
let data: &EditSite = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
check_slurs(&data.name)?;
|
||||||
Ok(claims) => claims.claims,
|
check_slurs_opt(&data.description)?;
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(slurs) = slur_check(&data.name) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(description) = &data.description {
|
|
||||||
if let Err(slurs) = slur_check(description) {
|
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(pool, user_id).await?;
|
is_admin(pool, user.id).await?;
|
||||||
|
|
||||||
let found_site = blocking(pool, move |conn| Site::read(conn, 1)).await??;
|
let found_site = blocking(pool, move |conn| Site::read(conn, 1)).await??;
|
||||||
|
|
||||||
|
@ -421,21 +405,12 @@ impl Perform for Oper<GetSite> {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Giving back your user, if you're logged in
|
let my_user = get_user_from_jwt_opt(&data.auth, pool).await?.map(|mut u| {
|
||||||
let my_user: Option<User_> = match &data.auth {
|
u.password_encrypted = "".to_string();
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
u.private_key = None;
|
||||||
Ok(claims) => {
|
u.public_key = None;
|
||||||
let user_id = claims.claims.id;
|
u
|
||||||
let mut user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
});
|
||||||
user.password_encrypted = "".to_string();
|
|
||||||
user.private_key = None;
|
|
||||||
user.public_key = None;
|
|
||||||
Some(user)
|
|
||||||
}
|
|
||||||
Err(_e) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
site: site_view,
|
site: site_view,
|
||||||
|
@ -466,16 +441,8 @@ impl Perform for Oper<Search> {
|
||||||
Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e),
|
Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_id: Option<i32> = match &data.auth {
|
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
let user_id = user.map(|u| u.id);
|
||||||
Ok(claims) => {
|
|
||||||
let user_id = claims.claims.id;
|
|
||||||
Some(user_id)
|
|
||||||
}
|
|
||||||
Err(_e) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_ = SearchType::from_str(&data.type_)?;
|
let type_ = SearchType::from_str(&data.type_)?;
|
||||||
|
|
||||||
|
@ -630,14 +597,8 @@ impl Perform for Oper<TransferSite> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetSiteResponse, LemmyError> {
|
) -> Result<GetSiteResponse, LemmyError> {
|
||||||
let data: &TransferSite = &self.data;
|
let data: &TransferSite = &self.data;
|
||||||
|
let mut user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
let mut user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
// TODO add a User_::read_safe() for this.
|
// TODO add a User_::read_safe() for this.
|
||||||
user.password_encrypted = "".to_string();
|
user.password_encrypted = "".to_string();
|
||||||
user.private_key = None;
|
user.private_key = None;
|
||||||
|
@ -646,7 +607,7 @@ impl Perform for Oper<TransferSite> {
|
||||||
let read_site = blocking(pool, move |conn| Site::read(conn, 1)).await??;
|
let read_site = blocking(pool, move |conn| Site::read(conn, 1)).await??;
|
||||||
|
|
||||||
// Make sure user is the creator
|
// Make sure user is the creator
|
||||||
if read_site.creator_id != user_id {
|
if read_site.creator_id != user.id {
|
||||||
return Err(APIError::err("not_an_admin").into());
|
return Err(APIError::err("not_an_admin").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,7 +628,7 @@ impl Perform for Oper<TransferSite> {
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
let form = ModAddForm {
|
let form = ModAddForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
other_user_id: data.user_id,
|
other_user_id: data.user_id,
|
||||||
removed: Some(false),
|
removed: Some(false),
|
||||||
};
|
};
|
||||||
|
@ -707,16 +668,10 @@ impl Perform for Oper<GetSiteConfig> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetSiteConfigResponse, LemmyError> {
|
) -> Result<GetSiteConfigResponse, LemmyError> {
|
||||||
let data: &GetSiteConfig = &self.data;
|
let data: &GetSiteConfig = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Only let admins read this
|
// Only let admins read this
|
||||||
is_admin(pool, user_id).await?;
|
is_admin(pool, user.id).await?;
|
||||||
|
|
||||||
let config_hjson = Settings::read_config_file()?;
|
let config_hjson = Settings::read_config_file()?;
|
||||||
|
|
||||||
|
@ -734,19 +689,13 @@ impl Perform for Oper<SaveSiteConfig> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetSiteConfigResponse, LemmyError> {
|
) -> Result<GetSiteConfigResponse, LemmyError> {
|
||||||
let data: &SaveSiteConfig = &self.data;
|
let data: &SaveSiteConfig = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Only let admins read this
|
// Only let admins read this
|
||||||
let admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
|
let admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
|
||||||
let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
|
let admin_ids: Vec<i32> = admins.into_iter().map(|m| m.id).collect();
|
||||||
|
|
||||||
if !admin_ids.contains(&user_id) {
|
if !admin_ids.contains(&user.id) {
|
||||||
return Err(APIError::err("not_an_admin").into());
|
return Err(APIError::err("not_an_admin").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{claims::Claims, is_admin, APIError, Oper, Perform},
|
api::{
|
||||||
|
check_slurs,
|
||||||
|
claims::Claims,
|
||||||
|
get_user_from_jwt,
|
||||||
|
get_user_from_jwt_opt,
|
||||||
|
is_admin,
|
||||||
|
APIError,
|
||||||
|
Oper,
|
||||||
|
Perform,
|
||||||
|
},
|
||||||
apub::ApubObjectType,
|
apub::ApubObjectType,
|
||||||
blocking,
|
blocking,
|
||||||
captcha_espeak_wav_base64,
|
captcha_espeak_wav_base64,
|
||||||
|
@ -47,8 +56,6 @@ use lemmy_utils::{
|
||||||
remove_slurs,
|
remove_slurs,
|
||||||
send_email,
|
send_email,
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
slur_check,
|
|
||||||
slurs_vec_to_str,
|
|
||||||
EndpointType,
|
EndpointType,
|
||||||
};
|
};
|
||||||
use log::error;
|
use log::error;
|
||||||
|
@ -366,9 +373,7 @@ impl Perform for Oper<Register> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(slurs) = slur_check(&data.username) {
|
check_slurs(&data.username)?;
|
||||||
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure there are no admins
|
// Make sure there are no admins
|
||||||
let any_admins = blocking(pool, move |conn| {
|
let any_admins = blocking(pool, move |conn| {
|
||||||
|
@ -543,14 +548,9 @@ impl Perform for Oper<SaveUserSettings> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<LoginResponse, LemmyError> {
|
) -> Result<LoginResponse, LemmyError> {
|
||||||
let data: &SaveUserSettings = &self.data;
|
let data: &SaveUserSettings = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let user_id = user.id;
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let read_user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
let read_user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
||||||
|
|
||||||
let email = match &data.email {
|
let email = match &data.email {
|
||||||
|
@ -665,24 +665,7 @@ impl Perform for Oper<GetUserDetails> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetUserDetailsResponse, LemmyError> {
|
) -> Result<GetUserDetailsResponse, LemmyError> {
|
||||||
let data: &GetUserDetails = &self.data;
|
let data: &GetUserDetails = &self.data;
|
||||||
|
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
|
||||||
// For logged in users, you need to get back subscribed, and settings
|
|
||||||
let user: Option<User_> = match &data.auth {
|
|
||||||
Some(auth) => match Claims::decode(&auth) {
|
|
||||||
Ok(claims) => {
|
|
||||||
let user_id = claims.claims.id;
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
Some(user)
|
|
||||||
}
|
|
||||||
Err(_e) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = match &user {
|
|
||||||
Some(user) => Some(user.id),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let show_nsfw = match &user {
|
let show_nsfw = match &user {
|
||||||
Some(user) => user.show_nsfw,
|
Some(user) => user.show_nsfw,
|
||||||
|
@ -712,6 +695,7 @@ impl Perform for Oper<GetUserDetails> {
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let saved_only = data.saved_only;
|
let saved_only = data.saved_only;
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
let user_id = user.map(|u| u.id);
|
||||||
let (posts, comments) = blocking(pool, move |conn| {
|
let (posts, comments) = blocking(pool, move |conn| {
|
||||||
let mut posts_query = PostQueryBuilder::create(conn)
|
let mut posts_query = PostQueryBuilder::create(conn)
|
||||||
.sort(&sort)
|
.sort(&sort)
|
||||||
|
@ -780,16 +764,10 @@ impl Perform for Oper<AddAdmin> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<AddAdminResponse, LemmyError> {
|
) -> Result<AddAdminResponse, LemmyError> {
|
||||||
let data: &AddAdmin = &self.data;
|
let data: &AddAdmin = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(pool, user_id).await?;
|
is_admin(pool, user.id).await?;
|
||||||
|
|
||||||
let added = data.added;
|
let added = data.added;
|
||||||
let added_user_id = data.user_id;
|
let added_user_id = data.user_id;
|
||||||
|
@ -800,7 +778,7 @@ impl Perform for Oper<AddAdmin> {
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
let form = ModAddForm {
|
let form = ModAddForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
other_user_id: data.user_id,
|
other_user_id: data.user_id,
|
||||||
removed: Some(!data.added),
|
removed: Some(!data.added),
|
||||||
};
|
};
|
||||||
|
@ -839,16 +817,10 @@ impl Perform for Oper<BanUser> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<BanUserResponse, LemmyError> {
|
) -> Result<BanUserResponse, LemmyError> {
|
||||||
let data: &BanUser = &self.data;
|
let data: &BanUser = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Make sure user is an admin
|
// Make sure user is an admin
|
||||||
is_admin(pool, user_id).await?;
|
is_admin(pool, user.id).await?;
|
||||||
|
|
||||||
let ban = data.ban;
|
let ban = data.ban;
|
||||||
let banned_user_id = data.user_id;
|
let banned_user_id = data.user_id;
|
||||||
|
@ -864,7 +836,7 @@ impl Perform for Oper<BanUser> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let form = ModBanForm {
|
let form = ModBanForm {
|
||||||
mod_user_id: user_id,
|
mod_user_id: user.id,
|
||||||
other_user_id: data.user_id,
|
other_user_id: data.user_id,
|
||||||
reason: data.reason.to_owned(),
|
reason: data.reason.to_owned(),
|
||||||
banned: Some(data.ban),
|
banned: Some(data.ban),
|
||||||
|
@ -903,19 +875,14 @@ impl Perform for Oper<GetReplies> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetRepliesResponse, LemmyError> {
|
) -> Result<GetRepliesResponse, LemmyError> {
|
||||||
let data: &GetReplies = &self.data;
|
let data: &GetReplies = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let sort = SortType::from_str(&data.sort)?;
|
let sort = SortType::from_str(&data.sort)?;
|
||||||
|
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let unread_only = data.unread_only;
|
let unread_only = data.unread_only;
|
||||||
|
let user_id = user.id;
|
||||||
let replies = blocking(pool, move |conn| {
|
let replies = blocking(pool, move |conn| {
|
||||||
ReplyQueryBuilder::create(conn, user_id)
|
ReplyQueryBuilder::create(conn, user_id)
|
||||||
.sort(&sort)
|
.sort(&sort)
|
||||||
|
@ -940,19 +907,14 @@ impl Perform for Oper<GetUserMentions> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetUserMentionsResponse, LemmyError> {
|
) -> Result<GetUserMentionsResponse, LemmyError> {
|
||||||
let data: &GetUserMentions = &self.data;
|
let data: &GetUserMentions = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let sort = SortType::from_str(&data.sort)?;
|
let sort = SortType::from_str(&data.sort)?;
|
||||||
|
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let unread_only = data.unread_only;
|
let unread_only = data.unread_only;
|
||||||
|
let user_id = user.id;
|
||||||
let mentions = blocking(pool, move |conn| {
|
let mentions = blocking(pool, move |conn| {
|
||||||
UserMentionQueryBuilder::create(conn, user_id)
|
UserMentionQueryBuilder::create(conn, user_id)
|
||||||
.sort(&sort)
|
.sort(&sort)
|
||||||
|
@ -977,19 +939,13 @@ impl Perform for Oper<MarkUserMentionAsRead> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<UserMentionResponse, LemmyError> {
|
) -> Result<UserMentionResponse, LemmyError> {
|
||||||
let data: &MarkUserMentionAsRead = &self.data;
|
let data: &MarkUserMentionAsRead = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let user_mention_id = data.user_mention_id;
|
let user_mention_id = data.user_mention_id;
|
||||||
let read_user_mention =
|
let read_user_mention =
|
||||||
blocking(pool, move |conn| UserMention::read(conn, user_mention_id)).await??;
|
blocking(pool, move |conn| UserMention::read(conn, user_mention_id)).await??;
|
||||||
|
|
||||||
if user_id != read_user_mention.recipient_id {
|
if user.id != read_user_mention.recipient_id {
|
||||||
return Err(APIError::err("couldnt_update_comment").into());
|
return Err(APIError::err("couldnt_update_comment").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1001,6 +957,7 @@ impl Perform for Oper<MarkUserMentionAsRead> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_mention_id = read_user_mention.id;
|
let user_mention_id = read_user_mention.id;
|
||||||
|
let user_id = user.id;
|
||||||
let user_mention_view = blocking(pool, move |conn| {
|
let user_mention_view = blocking(pool, move |conn| {
|
||||||
UserMentionView::read(conn, user_mention_id, user_id)
|
UserMentionView::read(conn, user_mention_id, user_id)
|
||||||
})
|
})
|
||||||
|
@ -1022,14 +979,9 @@ impl Perform for Oper<MarkAllAsRead> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<GetRepliesResponse, LemmyError> {
|
) -> Result<GetRepliesResponse, LemmyError> {
|
||||||
let data: &MarkAllAsRead = &self.data;
|
let data: &MarkAllAsRead = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
|
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let user_id = user.id;
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let replies = blocking(pool, move |conn| {
|
let replies = blocking(pool, move |conn| {
|
||||||
ReplyQueryBuilder::create(conn, user_id)
|
ReplyQueryBuilder::create(conn, user_id)
|
||||||
.unread_only(true)
|
.unread_only(true)
|
||||||
|
@ -1076,15 +1028,7 @@ impl Perform for Oper<DeleteAccount> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<LoginResponse, LemmyError> {
|
) -> Result<LoginResponse, LemmyError> {
|
||||||
let data: &DeleteAccount = &self.data;
|
let data: &DeleteAccount = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
|
|
||||||
// Verify the password
|
// Verify the password
|
||||||
let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
|
let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
|
||||||
|
@ -1093,6 +1037,7 @@ impl Perform for Oper<DeleteAccount> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
|
let user_id = user.id;
|
||||||
let comments = blocking(pool, move |conn| {
|
let comments = blocking(pool, move |conn| {
|
||||||
CommentQueryBuilder::create(conn)
|
CommentQueryBuilder::create(conn)
|
||||||
.for_creator_id(user_id)
|
.for_creator_id(user_id)
|
||||||
|
@ -1230,27 +1175,15 @@ impl Perform for Oper<CreatePrivateMessage> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
let data: &CreatePrivateMessage = &self.data;
|
let data: &CreatePrivateMessage = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let hostname = &format!("https://{}", Settings::get().hostname);
|
let hostname = &format!("https://{}", Settings::get().hostname);
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let content_slurs_removed = remove_slurs(&data.content.to_owned());
|
let content_slurs_removed = remove_slurs(&data.content.to_owned());
|
||||||
|
|
||||||
let private_message_form = PrivateMessageForm {
|
let private_message_form = PrivateMessageForm {
|
||||||
content: content_slurs_removed.to_owned(),
|
content: content_slurs_removed.to_owned(),
|
||||||
creator_id: user_id,
|
creator_id: user.id,
|
||||||
recipient_id: data.recipient_id,
|
recipient_id: data.recipient_id,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
read: None,
|
read: None,
|
||||||
|
@ -1341,25 +1274,13 @@ impl Perform for Oper<EditPrivateMessage> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
let data: &EditPrivateMessage = &self.data;
|
let data: &EditPrivateMessage = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking permissions
|
// Checking permissions
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_private_message =
|
let orig_private_message =
|
||||||
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
||||||
if user_id != orig_private_message.creator_id {
|
if user.id != orig_private_message.creator_id {
|
||||||
return Err(APIError::err("no_private_message_edit_allowed").into());
|
return Err(APIError::err("no_private_message_edit_allowed").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1409,25 +1330,13 @@ impl Perform for Oper<DeletePrivateMessage> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
let data: &DeletePrivateMessage = &self.data;
|
let data: &DeletePrivateMessage = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking permissions
|
// Checking permissions
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_private_message =
|
let orig_private_message =
|
||||||
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
||||||
if user_id != orig_private_message.creator_id {
|
if user.id != orig_private_message.creator_id {
|
||||||
return Err(APIError::err("no_private_message_edit_allowed").into());
|
return Err(APIError::err("no_private_message_edit_allowed").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1483,25 +1392,13 @@ impl Perform for Oper<MarkPrivateMessageAsRead> {
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
) -> Result<PrivateMessageResponse, LemmyError> {
|
||||||
let data: &MarkPrivateMessageAsRead = &self.data;
|
let data: &MarkPrivateMessageAsRead = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
// Check for a site ban
|
|
||||||
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
|
|
||||||
if user.banned {
|
|
||||||
return Err(APIError::err("site_ban").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking permissions
|
// Checking permissions
|
||||||
let edit_id = data.edit_id;
|
let edit_id = data.edit_id;
|
||||||
let orig_private_message =
|
let orig_private_message =
|
||||||
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
|
||||||
if user_id != orig_private_message.recipient_id {
|
if user.id != orig_private_message.recipient_id {
|
||||||
return Err(APIError::err("couldnt_update_private_message").into());
|
return Err(APIError::err("couldnt_update_private_message").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1548,13 +1445,8 @@ impl Perform for Oper<GetPrivateMessages> {
|
||||||
_websocket_info: Option<WebsocketInfo>,
|
_websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<PrivateMessagesResponse, LemmyError> {
|
) -> Result<PrivateMessagesResponse, LemmyError> {
|
||||||
let data: &GetPrivateMessages = &self.data;
|
let data: &GetPrivateMessages = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
let user_id = user.id;
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
|
@ -1578,24 +1470,21 @@ impl Perform for Oper<UserJoin> {
|
||||||
|
|
||||||
async fn perform(
|
async fn perform(
|
||||||
&self,
|
&self,
|
||||||
_pool: &DbPool,
|
pool: &DbPool,
|
||||||
websocket_info: Option<WebsocketInfo>,
|
websocket_info: Option<WebsocketInfo>,
|
||||||
) -> Result<UserJoinResponse, LemmyError> {
|
) -> Result<UserJoinResponse, LemmyError> {
|
||||||
let data: &UserJoin = &self.data;
|
let data: &UserJoin = &self.data;
|
||||||
|
let user = get_user_from_jwt(&data.auth, pool).await?;
|
||||||
let claims = match Claims::decode(&data.auth) {
|
|
||||||
Ok(claims) => claims.claims,
|
|
||||||
Err(_e) => return Err(APIError::err("not_logged_in").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = claims.id;
|
|
||||||
|
|
||||||
if let Some(ws) = websocket_info {
|
if let Some(ws) = websocket_info {
|
||||||
if let Some(id) = ws.id {
|
if let Some(id) = ws.id {
|
||||||
ws.chatserver.do_send(JoinUserRoom { user_id, id });
|
ws.chatserver.do_send(JoinUserRoom {
|
||||||
|
user_id: user.id,
|
||||||
|
id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(UserJoinResponse { user_id })
|
Ok(UserJoinResponse { user_id: user.id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
apub::{
|
apub::{
|
||||||
|
check_is_apub_id_valid,
|
||||||
community::do_announce,
|
community::do_announce,
|
||||||
extensions::signatures::sign,
|
extensions::signatures::sign,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
is_apub_id_valid,
|
|
||||||
ActorType,
|
ActorType,
|
||||||
},
|
},
|
||||||
request::retry_custom,
|
request::retry_custom,
|
||||||
|
@ -50,10 +50,7 @@ pub async fn send_activity(
|
||||||
|
|
||||||
for t in to {
|
for t in to {
|
||||||
let to_url = Url::parse(&t)?;
|
let to_url = Url::parse(&t)?;
|
||||||
if !is_apub_id_valid(&to_url) {
|
check_is_apub_id_valid(&to_url)?;
|
||||||
debug!("Not sending activity to {} (invalid or blocklisted)", t);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = retry_custom(|| async {
|
let res = retry_custom(|| async {
|
||||||
let request = client.post(&t).header("Content-Type", "application/json");
|
let request = client.post(&t).header("Content-Type", "application/json");
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
api::site::SearchResponse,
|
api::site::SearchResponse,
|
||||||
apub::{
|
apub::{
|
||||||
is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
ActorType,
|
ActorType,
|
||||||
FromApub,
|
FromApub,
|
||||||
GroupExt,
|
GroupExt,
|
||||||
|
@ -66,9 +66,7 @@ pub async fn fetch_remote_object<Response>(
|
||||||
where
|
where
|
||||||
Response: for<'de> Deserialize<'de>,
|
Response: for<'de> Deserialize<'de>,
|
||||||
{
|
{
|
||||||
if !is_apub_id_valid(&url) {
|
check_is_apub_id_valid(&url)?;
|
||||||
return Err(anyhow!("Activitypub uri invalid or blocked: {}", url).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeout = Duration::from_secs(60);
|
let timeout = Duration::from_secs(60);
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ pub async fn receive_create(
|
||||||
chat_server: ChatServerParam,
|
chat_server: ChatServerParam,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let create = Create::from_any_base(activity)?.unwrap();
|
let create = Create::from_any_base(activity)?.unwrap();
|
||||||
dbg!(create.object().as_single_kind_str());
|
|
||||||
match create.object().as_single_kind_str() {
|
match create.object().as_single_kind_str() {
|
||||||
Some("Page") => receive_create_post(create, client, pool, chat_server).await,
|
Some("Page") => receive_create_post(create, client, pool, chat_server).await,
|
||||||
Some("Note") => receive_create_comment(create, client, pool, chat_server).await,
|
Some("Note") => receive_create_comment(create, client, pool, chat_server).await,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
apub::{
|
apub::{
|
||||||
|
check_is_apub_id_valid,
|
||||||
extensions::signatures::verify,
|
extensions::signatures::verify,
|
||||||
fetcher::{get_or_fetch_and_upsert_community, get_or_fetch_and_upsert_user},
|
fetcher::get_or_fetch_and_upsert_user,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
ActorType,
|
ActorType,
|
||||||
},
|
},
|
||||||
|
@ -10,7 +11,8 @@ use crate::{
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Follow, Undo},
|
activity::{ActorAndObject, Follow, Undo},
|
||||||
|
base::AnyBase,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use actix_web::{client::Client, web, HttpRequest, HttpResponse};
|
use actix_web::{client::Client, web, HttpRequest, HttpResponse};
|
||||||
|
@ -21,37 +23,28 @@ use lemmy_db::{
|
||||||
Followable,
|
Followable,
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[serde(untagged)]
|
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||||
#[derive(Deserialize, Debug)]
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub enum CommunityAcceptedObjects {
|
pub enum ValidTypes {
|
||||||
Follow(Follow),
|
Follow,
|
||||||
Undo(Undo),
|
Undo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommunityAcceptedObjects {
|
pub type AcceptedActivities = ActorAndObject<ValidTypes>;
|
||||||
fn follow(&self) -> Result<Follow, LemmyError> {
|
|
||||||
match self {
|
|
||||||
CommunityAcceptedObjects::Follow(f) => Ok(f.to_owned()),
|
|
||||||
CommunityAcceptedObjects::Undo(u) => {
|
|
||||||
Ok(Follow::from_any_base(u.object().as_one().unwrap().to_owned())?.unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler for all incoming activities to community inboxes.
|
/// Handler for all incoming activities to community inboxes.
|
||||||
pub async fn community_inbox(
|
pub async fn community_inbox(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
input: web::Json<CommunityAcceptedObjects>,
|
input: web::Json<AcceptedActivities>,
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
db: DbPoolParam,
|
db: DbPoolParam,
|
||||||
client: web::Data<Client>,
|
client: web::Data<Client>,
|
||||||
_chat_server: ChatServerParam,
|
_chat_server: ChatServerParam,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let input = input.into_inner();
|
let activity = input.into_inner();
|
||||||
|
|
||||||
let path = path.into_inner();
|
let path = path.into_inner();
|
||||||
let community = blocking(&db, move |conn| Community::read_from_name(&conn, &path)).await??;
|
let community = blocking(&db, move |conn| Community::read_from_name(&conn, &path)).await??;
|
||||||
|
@ -67,34 +60,35 @@ pub async fn community_inbox(
|
||||||
}
|
}
|
||||||
debug!(
|
debug!(
|
||||||
"Community {} received activity {:?}",
|
"Community {} received activity {:?}",
|
||||||
&community.name, &input
|
&community.name, &activity
|
||||||
);
|
);
|
||||||
let follow = input.follow()?;
|
let user_uri = activity.actor()?.as_single_xsd_any_uri().unwrap();
|
||||||
let user_uri = follow.actor()?.as_single_xsd_any_uri().unwrap();
|
check_is_apub_id_valid(user_uri)?;
|
||||||
let community_uri = follow.object().as_single_xsd_any_uri().unwrap();
|
|
||||||
|
|
||||||
let user = get_or_fetch_and_upsert_user(&user_uri, &client, &db).await?;
|
let user = get_or_fetch_and_upsert_user(&user_uri, &client, &db).await?;
|
||||||
let community = get_or_fetch_and_upsert_community(community_uri, &client, &db).await?;
|
|
||||||
|
|
||||||
verify(&request, &user)?;
|
verify(&request, &user)?;
|
||||||
|
|
||||||
match input {
|
insert_activity(user.id, activity.clone(), false, &db).await?;
|
||||||
CommunityAcceptedObjects::Follow(f) => handle_follow(f, user, community, &client, db).await,
|
|
||||||
CommunityAcceptedObjects::Undo(u) => handle_undo_follow(u, user, community, db).await,
|
let any_base = activity.clone().into_any_base()?;
|
||||||
|
let kind = activity.kind().unwrap();
|
||||||
|
match kind {
|
||||||
|
ValidTypes::Follow => handle_follow(any_base, user, community, &client, db).await,
|
||||||
|
ValidTypes::Undo => handle_undo_follow(any_base, user, community, db).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a follow request from a remote user, adding it to the local database and returning an
|
/// Handle a follow request from a remote user, adding it to the local database and returning an
|
||||||
/// Accept activity.
|
/// Accept activity.
|
||||||
async fn handle_follow(
|
async fn handle_follow(
|
||||||
follow: Follow,
|
activity: AnyBase,
|
||||||
user: User_,
|
user: User_,
|
||||||
community: Community,
|
community: Community,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
db: DbPoolParam,
|
db: DbPoolParam,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
insert_activity(user.id, follow.clone(), false, &db).await?;
|
let follow = Follow::from_any_base(activity)?.unwrap();
|
||||||
|
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
community_id: community.id,
|
community_id: community.id,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
|
@ -112,12 +106,12 @@ async fn handle_follow(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_undo_follow(
|
async fn handle_undo_follow(
|
||||||
undo: Undo,
|
activity: AnyBase,
|
||||||
user: User_,
|
user: User_,
|
||||||
community: Community,
|
community: Community,
|
||||||
db: DbPoolParam,
|
db: DbPoolParam,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
insert_activity(user.id, undo, false, &db).await?;
|
let _undo = Undo::from_any_base(activity)?.unwrap();
|
||||||
|
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
community_id: community.id,
|
community_id: community.id,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
apub::{
|
apub::{
|
||||||
|
check_is_apub_id_valid,
|
||||||
community::do_announce,
|
community::do_announce,
|
||||||
extensions::signatures::verify,
|
extensions::signatures::verify,
|
||||||
fetcher::{
|
fetcher::{
|
||||||
|
@ -32,11 +33,11 @@ use activitystreams::{
|
||||||
use actix_web::{client::Client, web, HttpRequest, HttpResponse};
|
use actix_web::{client::Client, web, HttpRequest, HttpResponse};
|
||||||
use lemmy_db::user::User_;
|
use lemmy_db::user::User_;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::Serialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub enum ValidTypes {
|
pub enum ValidTypes {
|
||||||
Create,
|
Create,
|
||||||
|
@ -67,16 +68,19 @@ pub async fn shared_inbox(
|
||||||
debug!("Shared inbox received activity: {}", json);
|
debug!("Shared inbox received activity: {}", json);
|
||||||
|
|
||||||
let sender = &activity.actor()?.to_owned().single_xsd_any_uri().unwrap();
|
let sender = &activity.actor()?.to_owned().single_xsd_any_uri().unwrap();
|
||||||
|
|
||||||
// TODO: pass this actor in instead of using get_user_from_activity()
|
// TODO: pass this actor in instead of using get_user_from_activity()
|
||||||
let actor = get_or_fetch_and_upsert_actor(sender, &client, &pool).await?;
|
let actor = get_or_fetch_and_upsert_actor(sender, &client, &pool).await?;
|
||||||
|
|
||||||
|
let community = get_community_id_from_activity(&activity).await;
|
||||||
|
|
||||||
|
check_is_apub_id_valid(sender)?;
|
||||||
|
check_is_apub_id_valid(&community)?;
|
||||||
verify(&request, actor.as_ref())?;
|
verify(&request, actor.as_ref())?;
|
||||||
|
|
||||||
insert_activity(actor.user_id(), activity.clone(), false, &pool).await?;
|
insert_activity(actor.user_id(), activity.clone(), false, &pool).await?;
|
||||||
|
|
||||||
let any_base = activity.clone().into_any_base()?;
|
let any_base = activity.clone().into_any_base()?;
|
||||||
let kind = activity.kind().unwrap();
|
let kind = activity.kind().unwrap();
|
||||||
dbg!(kind);
|
|
||||||
match kind {
|
match kind {
|
||||||
ValidTypes::Announce => receive_announce(any_base, &client, &pool, chat_server).await,
|
ValidTypes::Announce => receive_announce(any_base, &client, &pool, chat_server).await,
|
||||||
ValidTypes::Create => receive_create(any_base, &client, &pool, chat_server).await,
|
ValidTypes::Create => receive_create(any_base, &client, &pool, chat_server).await,
|
||||||
|
@ -112,6 +116,15 @@ where
|
||||||
get_or_fetch_and_upsert_user(&user_uri, client, pool).await
|
get_or_fetch_and_upsert_user(&user_uri, client, pool).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in crate::apub::inbox) async fn get_community_id_from_activity<T, A>(activity: &T) -> Url
|
||||||
|
where
|
||||||
|
T: AsBase<A> + ActorAndObjectRef + AsObject<A>,
|
||||||
|
{
|
||||||
|
let cc = activity.cc().unwrap();
|
||||||
|
let cc = cc.as_many().unwrap();
|
||||||
|
cc.first().unwrap().as_xsd_any_uri().unwrap().to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
pub(in crate::apub::inbox) async fn announce_if_community_is_local<T, Kind>(
|
pub(in crate::apub::inbox) async fn announce_if_community_is_local<T, Kind>(
|
||||||
activity: T,
|
activity: T,
|
||||||
user: &User_,
|
user: &User_,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
api::user::PrivateMessageResponse,
|
api::user::PrivateMessageResponse,
|
||||||
apub::{
|
apub::{
|
||||||
|
check_is_apub_id_valid,
|
||||||
extensions::signatures::verify,
|
extensions::signatures::verify,
|
||||||
fetcher::{get_or_fetch_and_upsert_community, get_or_fetch_and_upsert_user},
|
fetcher::{get_or_fetch_and_upsert_actor, get_or_fetch_and_upsert_community},
|
||||||
insert_activity,
|
insert_activity,
|
||||||
FromApub,
|
FromApub,
|
||||||
},
|
},
|
||||||
|
@ -13,7 +14,8 @@ use crate::{
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Accept, Create, Delete, Undo, Update},
|
activity::{Accept, ActorAndObject, Create, Delete, Undo, Update},
|
||||||
|
base::AnyBase,
|
||||||
object::Note,
|
object::Note,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
@ -28,68 +30,76 @@ use lemmy_db::{
|
||||||
Followable,
|
Followable,
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[serde(untagged)]
|
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||||
#[derive(Deserialize, Debug)]
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub enum UserAcceptedObjects {
|
pub enum ValidTypes {
|
||||||
Accept(Box<Accept>),
|
Accept,
|
||||||
Create(Box<Create>),
|
Create,
|
||||||
Update(Box<Update>),
|
Update,
|
||||||
Delete(Box<Delete>),
|
Delete,
|
||||||
Undo(Box<Undo>),
|
Undo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type AcceptedActivities = ActorAndObject<ValidTypes>;
|
||||||
|
|
||||||
/// Handler for all incoming activities to user inboxes.
|
/// Handler for all incoming activities to user inboxes.
|
||||||
pub async fn user_inbox(
|
pub async fn user_inbox(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
input: web::Json<UserAcceptedObjects>,
|
input: web::Json<AcceptedActivities>,
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
client: web::Data<Client>,
|
client: web::Data<Client>,
|
||||||
db: DbPoolParam,
|
pool: DbPoolParam,
|
||||||
chat_server: ChatServerParam,
|
chat_server: ChatServerParam,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
// TODO: would be nice if we could do the signature check here, but we cant access the actor property
|
let activity = input.into_inner();
|
||||||
let input = input.into_inner();
|
|
||||||
let username = path.into_inner();
|
let username = path.into_inner();
|
||||||
debug!("User {} received activity: {:?}", &username, &input);
|
debug!("User {} received activity: {:?}", &username, &activity);
|
||||||
|
|
||||||
match input {
|
let actor_uri = activity.actor()?.as_single_xsd_any_uri().unwrap();
|
||||||
UserAcceptedObjects::Accept(a) => receive_accept(*a, &request, &username, &client, &db).await,
|
|
||||||
UserAcceptedObjects::Create(c) => {
|
check_is_apub_id_valid(actor_uri)?;
|
||||||
receive_create_private_message(*c, &request, &client, &db, chat_server).await
|
|
||||||
|
let actor = get_or_fetch_and_upsert_actor(actor_uri, &client, &pool).await?;
|
||||||
|
verify(&request, actor.as_ref())?;
|
||||||
|
|
||||||
|
insert_activity(actor.user_id(), activity.clone(), false, &pool).await?;
|
||||||
|
|
||||||
|
let any_base = activity.clone().into_any_base()?;
|
||||||
|
let kind = activity.kind().unwrap();
|
||||||
|
match kind {
|
||||||
|
ValidTypes::Accept => receive_accept(any_base, username, &client, &pool).await,
|
||||||
|
ValidTypes::Create => {
|
||||||
|
receive_create_private_message(any_base, &client, &pool, chat_server).await
|
||||||
}
|
}
|
||||||
UserAcceptedObjects::Update(u) => {
|
ValidTypes::Update => {
|
||||||
receive_update_private_message(*u, &request, &client, &db, chat_server).await
|
receive_update_private_message(any_base, &client, &pool, chat_server).await
|
||||||
}
|
}
|
||||||
UserAcceptedObjects::Delete(d) => {
|
ValidTypes::Delete => {
|
||||||
receive_delete_private_message(*d, &request, &client, &db, chat_server).await
|
receive_delete_private_message(any_base, &client, &pool, chat_server).await
|
||||||
}
|
}
|
||||||
UserAcceptedObjects::Undo(u) => {
|
ValidTypes::Undo => {
|
||||||
receive_undo_delete_private_message(*u, &request, &client, &db, chat_server).await
|
receive_undo_delete_private_message(any_base, &client, &pool, chat_server).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle accepted follows.
|
/// Handle accepted follows.
|
||||||
async fn receive_accept(
|
async fn receive_accept(
|
||||||
accept: Accept,
|
activity: AnyBase,
|
||||||
request: &HttpRequest,
|
username: String,
|
||||||
username: &str,
|
|
||||||
client: &Client,
|
client: &Client,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
|
let accept = Accept::from_any_base(activity)?.unwrap();
|
||||||
let community_uri = accept.actor()?.to_owned().single_xsd_any_uri().unwrap();
|
let community_uri = accept.actor()?.to_owned().single_xsd_any_uri().unwrap();
|
||||||
|
|
||||||
let community = get_or_fetch_and_upsert_community(&community_uri, client, pool).await?;
|
let community = get_or_fetch_and_upsert_community(&community_uri, client, pool).await?;
|
||||||
verify(request, &community)?;
|
|
||||||
|
|
||||||
let username = username.to_owned();
|
|
||||||
let user = blocking(pool, move |conn| User_::read_from_name(conn, &username)).await??;
|
let user = blocking(pool, move |conn| User_::read_from_name(conn, &username)).await??;
|
||||||
|
|
||||||
insert_activity(community.creator_id, accept, false, pool).await?;
|
|
||||||
|
|
||||||
// Now you need to add this to the community follower
|
// Now you need to add this to the community follower
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
community_id: community.id,
|
community_id: community.id,
|
||||||
|
@ -98,29 +108,23 @@ async fn receive_accept(
|
||||||
|
|
||||||
// This will fail if they're already a follower
|
// This will fail if they're already a follower
|
||||||
blocking(pool, move |conn| {
|
blocking(pool, move |conn| {
|
||||||
CommunityFollower::follow(conn, &community_follower_form)
|
CommunityFollower::follow(conn, &community_follower_form).ok()
|
||||||
})
|
})
|
||||||
.await??;
|
.await?;
|
||||||
|
|
||||||
// TODO: make sure that we actually requested a follow
|
// TODO: make sure that we actually requested a follow
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn receive_create_private_message(
|
async fn receive_create_private_message(
|
||||||
create: Create,
|
activity: AnyBase,
|
||||||
request: &HttpRequest,
|
|
||||||
client: &Client,
|
client: &Client,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
chat_server: ChatServerParam,
|
chat_server: ChatServerParam,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let user_uri = &create.actor()?.to_owned().single_xsd_any_uri().unwrap();
|
let create = Create::from_any_base(activity)?.unwrap();
|
||||||
let note = Note::from_any_base(create.object().as_one().unwrap().to_owned())?.unwrap();
|
let note = Note::from_any_base(create.object().as_one().unwrap().to_owned())?.unwrap();
|
||||||
|
|
||||||
let user = get_or_fetch_and_upsert_user(user_uri, client, pool).await?;
|
|
||||||
verify(request, &user)?;
|
|
||||||
|
|
||||||
insert_activity(user.id, create, false, pool).await?;
|
|
||||||
|
|
||||||
let private_message = PrivateMessageForm::from_apub(¬e, client, pool).await?;
|
let private_message = PrivateMessageForm::from_apub(¬e, client, pool).await?;
|
||||||
|
|
||||||
let inserted_private_message = blocking(pool, move |conn| {
|
let inserted_private_message = blocking(pool, move |conn| {
|
||||||
|
@ -148,20 +152,14 @@ async fn receive_create_private_message(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn receive_update_private_message(
|
async fn receive_update_private_message(
|
||||||
update: Update,
|
activity: AnyBase,
|
||||||
request: &HttpRequest,
|
|
||||||
client: &Client,
|
client: &Client,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
chat_server: ChatServerParam,
|
chat_server: ChatServerParam,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let user_uri = &update.actor()?.to_owned().single_xsd_any_uri().unwrap();
|
let update = Update::from_any_base(activity)?.unwrap();
|
||||||
let note = Note::from_any_base(update.object().as_one().unwrap().to_owned())?.unwrap();
|
let note = Note::from_any_base(update.object().as_one().unwrap().to_owned())?.unwrap();
|
||||||
|
|
||||||
let user = get_or_fetch_and_upsert_user(&user_uri, client, pool).await?;
|
|
||||||
verify(request, &user)?;
|
|
||||||
|
|
||||||
insert_activity(user.id, update, false, pool).await?;
|
|
||||||
|
|
||||||
let private_message_form = PrivateMessageForm::from_apub(¬e, client, pool).await?;
|
let private_message_form = PrivateMessageForm::from_apub(¬e, client, pool).await?;
|
||||||
|
|
||||||
let private_message_ap_id = private_message_form.ap_id.clone();
|
let private_message_ap_id = private_message_form.ap_id.clone();
|
||||||
|
@ -197,20 +195,14 @@ async fn receive_update_private_message(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn receive_delete_private_message(
|
async fn receive_delete_private_message(
|
||||||
delete: Delete,
|
activity: AnyBase,
|
||||||
request: &HttpRequest,
|
|
||||||
client: &Client,
|
client: &Client,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
chat_server: ChatServerParam,
|
chat_server: ChatServerParam,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let user_uri = &delete.actor()?.to_owned().single_xsd_any_uri().unwrap();
|
let delete = Delete::from_any_base(activity)?.unwrap();
|
||||||
let note = Note::from_any_base(delete.object().as_one().unwrap().to_owned())?.unwrap();
|
let note = Note::from_any_base(delete.object().as_one().unwrap().to_owned())?.unwrap();
|
||||||
|
|
||||||
let user = get_or_fetch_and_upsert_user(&user_uri, client, pool).await?;
|
|
||||||
verify(request, &user)?;
|
|
||||||
|
|
||||||
insert_activity(user.id, delete, false, pool).await?;
|
|
||||||
|
|
||||||
let private_message_form = PrivateMessageForm::from_apub(¬e, client, pool).await?;
|
let private_message_form = PrivateMessageForm::from_apub(¬e, client, pool).await?;
|
||||||
|
|
||||||
let private_message_ap_id = private_message_form.ap_id;
|
let private_message_ap_id = private_message_form.ap_id;
|
||||||
|
@ -258,20 +250,14 @@ async fn receive_delete_private_message(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn receive_undo_delete_private_message(
|
async fn receive_undo_delete_private_message(
|
||||||
undo: Undo,
|
activity: AnyBase,
|
||||||
request: &HttpRequest,
|
|
||||||
client: &Client,
|
client: &Client,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
chat_server: ChatServerParam,
|
chat_server: ChatServerParam,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
|
let undo = Undo::from_any_base(activity)?.unwrap();
|
||||||
let delete = Delete::from_any_base(undo.object().as_one().unwrap().to_owned())?.unwrap();
|
let delete = Delete::from_any_base(undo.object().as_one().unwrap().to_owned())?.unwrap();
|
||||||
let note = Note::from_any_base(delete.object().as_one().unwrap().to_owned())?.unwrap();
|
let note = Note::from_any_base(delete.object().as_one().unwrap().to_owned())?.unwrap();
|
||||||
let user_uri = &delete.actor()?.to_owned().single_xsd_any_uri().unwrap();
|
|
||||||
|
|
||||||
let user = get_or_fetch_and_upsert_user(&user_uri, client, pool).await?;
|
|
||||||
verify(request, &user)?;
|
|
||||||
|
|
||||||
insert_activity(user.id, delete, false, pool).await?;
|
|
||||||
|
|
||||||
let private_message = PrivateMessageForm::from_apub(¬e, client, pool).await?;
|
let private_message = PrivateMessageForm::from_apub(¬e, client, pool).await?;
|
||||||
|
|
||||||
|
|
|
@ -63,33 +63,34 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the ID has a valid format, correct scheme, and is in the allowed instance list.
|
// Checks if the ID has a valid format, correct scheme, and is in the allowed instance list.
|
||||||
fn is_apub_id_valid(apub_id: &Url) -> bool {
|
fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
|
||||||
debug!("Checking {}", apub_id);
|
|
||||||
if apub_id.scheme() != get_apub_protocol_string() {
|
if apub_id.scheme() != get_apub_protocol_string() {
|
||||||
debug!("invalid scheme: {:?}", apub_id.scheme());
|
return Err(anyhow!("invalid apub id scheme: {:?}", apub_id.scheme()).into());
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let allowed_instances: Vec<String> = Settings::get()
|
let mut allowed_instances: Vec<String> = Settings::get()
|
||||||
.federation
|
.federation
|
||||||
.allowed_instances
|
.allowed_instances
|
||||||
.split(',')
|
.split(',')
|
||||||
.map(|d| d.to_string())
|
.map(|d| d.to_string())
|
||||||
.collect();
|
.collect();
|
||||||
|
// need to allow this explicitly because apub activities might contain objects from our local
|
||||||
|
// instance. replace is needed to remove the port in our federation test setup.
|
||||||
|
let settings = Settings::get();
|
||||||
|
let local_instance = settings.hostname.split(':').collect::<Vec<&str>>();
|
||||||
|
allowed_instances.push(local_instance.first().unwrap().to_string());
|
||||||
|
|
||||||
match apub_id.domain() {
|
match apub_id.domain() {
|
||||||
Some(d) => {
|
Some(d) => {
|
||||||
let contains = allowed_instances.contains(&d.to_owned());
|
let contains = allowed_instances.contains(&d.to_owned());
|
||||||
|
|
||||||
if !contains {
|
if !contains {
|
||||||
debug!("{} not in {:?}", d, allowed_instances);
|
return Err(anyhow!("{} not in federation allowlist", d).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
contains
|
Ok(())
|
||||||
}
|
|
||||||
None => {
|
|
||||||
debug!("missing domain");
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
None => Err(anyhow!("federation allowlist is empty").into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
ui/package.json
vendored
2
ui/package.json
vendored
|
@ -6,7 +6,7 @@
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"api-test": "jest src/api_tests/api.spec.ts",
|
"api-test": "jest src/api_tests/ -i --verbose",
|
||||||
"build": "node fuse prod",
|
"build": "node fuse prod",
|
||||||
"lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src",
|
"lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src",
|
||||||
"prebuild": "node generate_translations.js",
|
"prebuild": "node generate_translations.js",
|
||||||
|
|
1570
ui/src/api_tests/api.spec.ts
vendored
1570
ui/src/api_tests/api.spec.ts
vendored
File diff suppressed because it is too large
Load diff
308
ui/src/api_tests/comment.spec.ts
vendored
Normal file
308
ui/src/api_tests/comment.spec.ts
vendored
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
import {
|
||||||
|
alpha,
|
||||||
|
beta,
|
||||||
|
gamma,
|
||||||
|
setupLogins,
|
||||||
|
createPost,
|
||||||
|
getPost,
|
||||||
|
searchComment,
|
||||||
|
likeComment,
|
||||||
|
followBeta,
|
||||||
|
searchForBetaCommunity,
|
||||||
|
createComment,
|
||||||
|
updateComment,
|
||||||
|
deleteComment,
|
||||||
|
removeComment,
|
||||||
|
getMentions,
|
||||||
|
searchPost,
|
||||||
|
unfollowRemotes,
|
||||||
|
} from './shared';
|
||||||
|
|
||||||
|
import { PostResponse } from '../interfaces';
|
||||||
|
|
||||||
|
let postRes: PostResponse;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await setupLogins();
|
||||||
|
await followBeta(alpha);
|
||||||
|
await followBeta(gamma);
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
postRes = await createPost(
|
||||||
|
alpha,
|
||||||
|
search.communities.filter(c => c.local == false)[0].id
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await unfollowRemotes(alpha);
|
||||||
|
await unfollowRemotes(gamma);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Create a comment', async () => {
|
||||||
|
let commentRes = await createComment(alpha, postRes.post.id);
|
||||||
|
expect(commentRes.comment.content).toBeDefined();
|
||||||
|
expect(commentRes.comment.community_local).toBe(false);
|
||||||
|
expect(commentRes.comment.creator_local).toBe(true);
|
||||||
|
expect(commentRes.comment.score).toBe(1);
|
||||||
|
|
||||||
|
// Make sure that comment is liked on beta
|
||||||
|
let searchBeta = await searchComment(beta, commentRes.comment);
|
||||||
|
let betaComment = searchBeta.comments[0];
|
||||||
|
expect(betaComment).toBeDefined();
|
||||||
|
expect(betaComment.community_local).toBe(true);
|
||||||
|
expect(betaComment.creator_local).toBe(false);
|
||||||
|
expect(betaComment.score).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Update a comment', async () => {
|
||||||
|
let commentRes = await createComment(alpha, postRes.post.id);
|
||||||
|
let updateCommentRes = await updateComment(alpha, commentRes.comment.id);
|
||||||
|
expect(updateCommentRes.comment.content).toBe(
|
||||||
|
'A jest test federated comment update'
|
||||||
|
);
|
||||||
|
expect(updateCommentRes.comment.community_local).toBe(false);
|
||||||
|
expect(updateCommentRes.comment.creator_local).toBe(true);
|
||||||
|
|
||||||
|
// Make sure that post is updated on beta
|
||||||
|
let searchBeta = await searchComment(beta, commentRes.comment);
|
||||||
|
let betaComment = searchBeta.comments[0];
|
||||||
|
expect(betaComment.content).toBe('A jest test federated comment update');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Delete a comment', async () => {
|
||||||
|
let commentRes = await createComment(alpha, postRes.post.id);
|
||||||
|
let deleteCommentRes = await deleteComment(
|
||||||
|
alpha,
|
||||||
|
true,
|
||||||
|
commentRes.comment.id
|
||||||
|
);
|
||||||
|
expect(deleteCommentRes.comment.deleted).toBe(true);
|
||||||
|
|
||||||
|
// Make sure that comment is deleted on beta
|
||||||
|
// The search doesnt work below, because it returns a tombstone / http::gone
|
||||||
|
// let searchBeta = await searchComment(beta, commentRes.comment);
|
||||||
|
// console.log(searchBeta);
|
||||||
|
// let betaComment = searchBeta.comments[0];
|
||||||
|
// Create a fake post, just to get the previous new post id
|
||||||
|
let createdBetaPostJustToGetId = await createPost(beta, 2);
|
||||||
|
let betaPost = await getPost(beta, createdBetaPostJustToGetId.post.id - 1);
|
||||||
|
let betaComment = betaPost.comments[0];
|
||||||
|
expect(betaComment.deleted).toBe(true);
|
||||||
|
|
||||||
|
let undeleteCommentRes = await deleteComment(
|
||||||
|
alpha,
|
||||||
|
false,
|
||||||
|
commentRes.comment.id
|
||||||
|
);
|
||||||
|
expect(undeleteCommentRes.comment.deleted).toBe(false);
|
||||||
|
|
||||||
|
// Make sure that comment is undeleted on beta
|
||||||
|
let searchBeta2 = await searchComment(beta, commentRes.comment);
|
||||||
|
let betaComment2 = searchBeta2.comments[0];
|
||||||
|
expect(betaComment2.deleted).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Remove a comment', async () => {
|
||||||
|
let commentRes = await createComment(alpha, postRes.post.id);
|
||||||
|
let removeCommentRes = await removeComment(
|
||||||
|
alpha,
|
||||||
|
true,
|
||||||
|
commentRes.comment.id
|
||||||
|
);
|
||||||
|
expect(removeCommentRes.comment.removed).toBe(true);
|
||||||
|
|
||||||
|
// Make sure that comment is removed on beta
|
||||||
|
let searchBeta = await searchComment(beta, commentRes.comment);
|
||||||
|
let betaComment = searchBeta.comments[0];
|
||||||
|
expect(betaComment.removed).toBe(true);
|
||||||
|
|
||||||
|
let unremoveCommentRes = await removeComment(
|
||||||
|
alpha,
|
||||||
|
false,
|
||||||
|
commentRes.comment.id
|
||||||
|
);
|
||||||
|
expect(unremoveCommentRes.comment.removed).toBe(false);
|
||||||
|
|
||||||
|
// Make sure that comment is unremoved on beta
|
||||||
|
let searchBeta2 = await searchComment(beta, commentRes.comment);
|
||||||
|
let betaComment2 = searchBeta2.comments[0];
|
||||||
|
expect(betaComment2.removed).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Unlike a comment', async () => {
|
||||||
|
let commentRes = await createComment(alpha, postRes.post.id);
|
||||||
|
let unlike = await likeComment(alpha, 0, commentRes.comment);
|
||||||
|
expect(unlike.comment.score).toBe(0);
|
||||||
|
|
||||||
|
// Make sure that post is unliked on beta
|
||||||
|
let searchBeta = await searchComment(beta, commentRes.comment);
|
||||||
|
let betaComment = searchBeta.comments[0];
|
||||||
|
expect(betaComment).toBeDefined();
|
||||||
|
expect(betaComment.community_local).toBe(true);
|
||||||
|
expect(betaComment.creator_local).toBe(false);
|
||||||
|
expect(betaComment.score).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Federated comment like', async () => {
|
||||||
|
let commentRes = await createComment(alpha, postRes.post.id);
|
||||||
|
|
||||||
|
// Find the comment on beta
|
||||||
|
let searchBeta = await searchComment(beta, commentRes.comment);
|
||||||
|
let betaComment = searchBeta.comments[0];
|
||||||
|
|
||||||
|
let like = await likeComment(beta, 1, betaComment);
|
||||||
|
expect(like.comment.score).toBe(2);
|
||||||
|
|
||||||
|
// Get the post from alpha, check the likes
|
||||||
|
let post = await getPost(alpha, postRes.post.id);
|
||||||
|
expect(post.comments[0].score).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Reply to a comment', async () => {
|
||||||
|
// Create a comment on alpha, find it on beta
|
||||||
|
let commentRes = await createComment(alpha, postRes.post.id);
|
||||||
|
let searchBeta = await searchComment(beta, commentRes.comment);
|
||||||
|
let betaComment = searchBeta.comments[0];
|
||||||
|
|
||||||
|
// find that comment id on beta
|
||||||
|
|
||||||
|
// Reply from beta
|
||||||
|
let replyRes = await createComment(beta, betaComment.post_id, betaComment.id);
|
||||||
|
expect(replyRes.comment.content).toBeDefined();
|
||||||
|
expect(replyRes.comment.community_local).toBe(true);
|
||||||
|
expect(replyRes.comment.creator_local).toBe(true);
|
||||||
|
expect(replyRes.comment.parent_id).toBe(betaComment.id);
|
||||||
|
expect(replyRes.comment.score).toBe(1);
|
||||||
|
|
||||||
|
// Make sure that comment is seen on alpha
|
||||||
|
// TODO not sure why, but a searchComment back to alpha, for the ap_id of betas
|
||||||
|
// comment, isn't working.
|
||||||
|
// let searchAlpha = await searchComment(alpha, replyRes.comment);
|
||||||
|
let post = await getPost(alpha, postRes.post.id);
|
||||||
|
let alphaComment = post.comments[0];
|
||||||
|
expect(alphaComment.content).toBeDefined();
|
||||||
|
expect(alphaComment.parent_id).toBe(post.comments[1].id);
|
||||||
|
expect(alphaComment.community_local).toBe(false);
|
||||||
|
expect(alphaComment.creator_local).toBe(false);
|
||||||
|
expect(alphaComment.score).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Mention beta', async () => {
|
||||||
|
// Create a mention on alpha
|
||||||
|
let mentionContent = 'A test mention of @lemmy_beta@lemmy-beta:8550';
|
||||||
|
let commentRes = await createComment(alpha, postRes.post.id);
|
||||||
|
let mentionRes = await createComment(
|
||||||
|
alpha,
|
||||||
|
postRes.post.id,
|
||||||
|
commentRes.comment.id,
|
||||||
|
mentionContent
|
||||||
|
);
|
||||||
|
expect(mentionRes.comment.content).toBeDefined();
|
||||||
|
expect(mentionRes.comment.community_local).toBe(false);
|
||||||
|
expect(mentionRes.comment.creator_local).toBe(true);
|
||||||
|
expect(mentionRes.comment.score).toBe(1);
|
||||||
|
|
||||||
|
let mentionsRes = await getMentions(beta);
|
||||||
|
expect(mentionsRes.mentions[0].content).toBeDefined();
|
||||||
|
expect(mentionsRes.mentions[0].community_local).toBe(true);
|
||||||
|
expect(mentionsRes.mentions[0].creator_local).toBe(false);
|
||||||
|
expect(mentionsRes.mentions[0].score).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Comment Search', async () => {
|
||||||
|
let commentRes = await createComment(alpha, postRes.post.id);
|
||||||
|
let searchBeta = await searchComment(beta, commentRes.comment);
|
||||||
|
expect(searchBeta.comments[0].ap_id).toBe(commentRes.comment.ap_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('A and G subscribe to B (center) A posts, G mentions B, it gets announced to A', async () => {
|
||||||
|
// Create a local post
|
||||||
|
let alphaPost = await createPost(alpha, 2);
|
||||||
|
expect(alphaPost.post.community_local).toBe(true);
|
||||||
|
|
||||||
|
// Make sure gamma sees it
|
||||||
|
let search = await searchPost(gamma, alphaPost.post);
|
||||||
|
let gammaPost = search.posts[0];
|
||||||
|
|
||||||
|
let commentContent =
|
||||||
|
'A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8550';
|
||||||
|
let commentRes = await createComment(
|
||||||
|
gamma,
|
||||||
|
gammaPost.id,
|
||||||
|
undefined,
|
||||||
|
commentContent
|
||||||
|
);
|
||||||
|
expect(commentRes.comment.content).toBe(commentContent);
|
||||||
|
expect(commentRes.comment.community_local).toBe(false);
|
||||||
|
expect(commentRes.comment.creator_local).toBe(true);
|
||||||
|
expect(commentRes.comment.score).toBe(1);
|
||||||
|
|
||||||
|
// Make sure alpha sees it
|
||||||
|
let alphaPost2 = await getPost(alpha, alphaPost.post.id);
|
||||||
|
expect(alphaPost2.comments[0].content).toBe(commentContent);
|
||||||
|
expect(alphaPost2.comments[0].community_local).toBe(true);
|
||||||
|
expect(alphaPost2.comments[0].creator_local).toBe(false);
|
||||||
|
expect(alphaPost2.comments[0].score).toBe(1);
|
||||||
|
|
||||||
|
// Make sure beta has mentions
|
||||||
|
let mentionsRes = await getMentions(beta);
|
||||||
|
expect(mentionsRes.mentions[0].content).toBe(commentContent);
|
||||||
|
expect(mentionsRes.mentions[0].community_local).toBe(false);
|
||||||
|
expect(mentionsRes.mentions[0].creator_local).toBe(false);
|
||||||
|
// TODO this is failing because fetchInReplyTos aren't getting score
|
||||||
|
// expect(mentionsRes.mentions[0].score).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedded comments, A subs to B, B updates the lowest level comment, A fetches both the post and all the inreplyto comments for that post.', async () => {
|
||||||
|
// Unfollow all remote communities
|
||||||
|
let followed = await unfollowRemotes(alpha);
|
||||||
|
expect(
|
||||||
|
followed.communities.filter(c => c.community_local == false).length
|
||||||
|
).toBe(0);
|
||||||
|
|
||||||
|
// B creates a post, and two comments, should be invisible to A
|
||||||
|
let postRes = await createPost(beta, 2);
|
||||||
|
expect(postRes.post.name).toBeDefined();
|
||||||
|
|
||||||
|
let parentCommentContent = 'An invisible top level comment from beta';
|
||||||
|
let parentCommentRes = await createComment(
|
||||||
|
beta,
|
||||||
|
postRes.post.id,
|
||||||
|
undefined,
|
||||||
|
parentCommentContent
|
||||||
|
);
|
||||||
|
expect(parentCommentRes.comment.content).toBe(parentCommentContent);
|
||||||
|
|
||||||
|
// B creates a comment, then a child one of that.
|
||||||
|
let childCommentContent = 'An invisible child comment from beta';
|
||||||
|
let childCommentRes = await createComment(
|
||||||
|
beta,
|
||||||
|
postRes.post.id,
|
||||||
|
parentCommentRes.comment.id,
|
||||||
|
childCommentContent
|
||||||
|
);
|
||||||
|
expect(childCommentRes.comment.content).toBe(childCommentContent);
|
||||||
|
|
||||||
|
// Follow beta again
|
||||||
|
let follow = await followBeta(alpha);
|
||||||
|
expect(follow.community.local).toBe(false);
|
||||||
|
expect(follow.community.name).toBe('main');
|
||||||
|
|
||||||
|
// An update to the child comment on beta, should push the post, parent, and child to alpha now
|
||||||
|
let updatedCommentContent = 'An update child comment from beta';
|
||||||
|
let updateRes = await updateComment(
|
||||||
|
beta,
|
||||||
|
childCommentRes.comment.id,
|
||||||
|
updatedCommentContent
|
||||||
|
);
|
||||||
|
expect(updateRes.comment.content).toBe(updatedCommentContent);
|
||||||
|
|
||||||
|
// Get the post from alpha
|
||||||
|
let createFakeAlphaPostToGetId = await createPost(alpha, 2);
|
||||||
|
let alphaPost = await getPost(alpha, createFakeAlphaPostToGetId.post.id - 1);
|
||||||
|
expect(alphaPost.post.name).toBeDefined();
|
||||||
|
expect(alphaPost.comments[1].content).toBe(parentCommentContent);
|
||||||
|
expect(alphaPost.comments[0].content).toBe(updatedCommentContent);
|
||||||
|
expect(alphaPost.post.community_local).toBe(false);
|
||||||
|
expect(alphaPost.post.creator_local).toBe(false);
|
||||||
|
});
|
88
ui/src/api_tests/community.spec.ts
vendored
Normal file
88
ui/src/api_tests/community.spec.ts
vendored
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import {
|
||||||
|
alpha,
|
||||||
|
beta,
|
||||||
|
setupLogins,
|
||||||
|
searchForBetaCommunity,
|
||||||
|
createCommunity,
|
||||||
|
deleteCommunity,
|
||||||
|
removeCommunity,
|
||||||
|
} from './shared';
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await setupLogins();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Create community', async () => {
|
||||||
|
let communityRes = await createCommunity(alpha);
|
||||||
|
expect(communityRes.community.name).toBeDefined();
|
||||||
|
|
||||||
|
// A dupe check
|
||||||
|
let prevName = communityRes.community.name;
|
||||||
|
let communityRes2 = await createCommunity(alpha, prevName);
|
||||||
|
expect(communityRes2['error']).toBe('community_already_exists');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Delete community', async () => {
|
||||||
|
let communityRes = await createCommunity(beta);
|
||||||
|
let deleteCommunityRes = await deleteCommunity(
|
||||||
|
beta,
|
||||||
|
true,
|
||||||
|
communityRes.community.id
|
||||||
|
);
|
||||||
|
expect(deleteCommunityRes.community.deleted).toBe(true);
|
||||||
|
|
||||||
|
// Make sure it got deleted on A
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let communityA = search.communities[0];
|
||||||
|
// TODO this fails currently, because no updates are pushed
|
||||||
|
// expect(communityA.deleted).toBe(true);
|
||||||
|
|
||||||
|
// Undelete
|
||||||
|
let undeleteCommunityRes = await deleteCommunity(
|
||||||
|
beta,
|
||||||
|
false,
|
||||||
|
communityRes.community.id
|
||||||
|
);
|
||||||
|
expect(undeleteCommunityRes.community.deleted).toBe(false);
|
||||||
|
|
||||||
|
// Make sure it got undeleted on A
|
||||||
|
let search2 = await searchForBetaCommunity(alpha);
|
||||||
|
let communityA2 = search2.communities[0];
|
||||||
|
// TODO this fails currently, because no updates are pushed
|
||||||
|
// expect(communityA2.deleted).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Remove community', async () => {
|
||||||
|
let communityRes = await createCommunity(beta);
|
||||||
|
let removeCommunityRes = await removeCommunity(
|
||||||
|
beta,
|
||||||
|
true,
|
||||||
|
communityRes.community.id
|
||||||
|
);
|
||||||
|
expect(removeCommunityRes.community.removed).toBe(true);
|
||||||
|
|
||||||
|
// Make sure it got removed on A
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let communityA = search.communities[0];
|
||||||
|
// TODO this fails currently, because no updates are pushed
|
||||||
|
// expect(communityA.removed).toBe(true);
|
||||||
|
|
||||||
|
// unremove
|
||||||
|
let unremoveCommunityRes = await removeCommunity(
|
||||||
|
beta,
|
||||||
|
false,
|
||||||
|
communityRes.community.id
|
||||||
|
);
|
||||||
|
expect(unremoveCommunityRes.community.removed).toBe(false);
|
||||||
|
|
||||||
|
// Make sure it got unremoved on A
|
||||||
|
let search2 = await searchForBetaCommunity(alpha);
|
||||||
|
let communityA2 = search2.communities[0];
|
||||||
|
// TODO this fails currently, because no updates are pushed
|
||||||
|
// expect(communityA2.removed).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Search for beta community', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
expect(search.communities[0].name).toBe('main');
|
||||||
|
});
|
40
ui/src/api_tests/follow.spec.ts
vendored
Normal file
40
ui/src/api_tests/follow.spec.ts
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import {
|
||||||
|
alpha,
|
||||||
|
setupLogins,
|
||||||
|
searchForBetaCommunity,
|
||||||
|
followCommunity,
|
||||||
|
checkFollowedCommunities,
|
||||||
|
unfollowRemotes,
|
||||||
|
} from './shared';
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await setupLogins();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await unfollowRemotes(alpha);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Follow federated community', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha); // TODO sometimes this is returning null?
|
||||||
|
let follow = await followCommunity(alpha, true, search.communities[0].id);
|
||||||
|
|
||||||
|
// Make sure the follow response went through
|
||||||
|
expect(follow.community.local).toBe(false);
|
||||||
|
expect(follow.community.name).toBe('main');
|
||||||
|
|
||||||
|
// Check it from local
|
||||||
|
let followCheck = await checkFollowedCommunities(alpha);
|
||||||
|
let remoteCommunityId = followCheck.communities.filter(
|
||||||
|
c => c.community_local == false
|
||||||
|
)[0].community_id;
|
||||||
|
expect(remoteCommunityId).toBeDefined();
|
||||||
|
|
||||||
|
// Test an unfollow
|
||||||
|
let unfollow = await followCommunity(alpha, false, remoteCommunityId);
|
||||||
|
expect(unfollow.community.local).toBe(false);
|
||||||
|
|
||||||
|
// Make sure you are unsubbed locally
|
||||||
|
let unfollowCheck = await checkFollowedCommunities(alpha);
|
||||||
|
expect(unfollowCheck.communities.length).toBeGreaterThanOrEqual(1);
|
||||||
|
});
|
192
ui/src/api_tests/post.spec.ts
vendored
Normal file
192
ui/src/api_tests/post.spec.ts
vendored
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
import {
|
||||||
|
alpha,
|
||||||
|
beta,
|
||||||
|
gamma,
|
||||||
|
setupLogins,
|
||||||
|
createPost,
|
||||||
|
updatePost,
|
||||||
|
stickyPost,
|
||||||
|
lockPost,
|
||||||
|
searchPost,
|
||||||
|
likePost,
|
||||||
|
followBeta,
|
||||||
|
searchForBetaCommunity,
|
||||||
|
createComment,
|
||||||
|
deletePost,
|
||||||
|
removePost,
|
||||||
|
getPost,
|
||||||
|
unfollowRemotes,
|
||||||
|
} from './shared';
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await setupLogins();
|
||||||
|
await followBeta(alpha);
|
||||||
|
await followBeta(gamma);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await unfollowRemotes(alpha);
|
||||||
|
await unfollowRemotes(gamma);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Create a post', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let postRes = await createPost(alpha, search.communities[0].id);
|
||||||
|
expect(postRes.post).toBeDefined();
|
||||||
|
expect(postRes.post.community_local).toBe(false);
|
||||||
|
expect(postRes.post.creator_local).toBe(true);
|
||||||
|
expect(postRes.post.score).toBe(1);
|
||||||
|
|
||||||
|
// Make sure that post is liked on beta
|
||||||
|
let searchBeta = await searchPost(beta, postRes.post);
|
||||||
|
let betaPost = searchBeta.posts[0];
|
||||||
|
|
||||||
|
expect(betaPost).toBeDefined();
|
||||||
|
expect(betaPost.community_local).toBe(true);
|
||||||
|
expect(betaPost.creator_local).toBe(false);
|
||||||
|
expect(betaPost.score).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Unlike a post', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let postRes = await createPost(alpha, search.communities[0].id);
|
||||||
|
let unlike = await likePost(alpha, 0, postRes.post);
|
||||||
|
expect(unlike.post.score).toBe(0);
|
||||||
|
|
||||||
|
// Make sure that post is unliked on beta
|
||||||
|
let searchBeta = await searchPost(beta, postRes.post);
|
||||||
|
let betaPost = searchBeta.posts[0];
|
||||||
|
|
||||||
|
expect(betaPost).toBeDefined();
|
||||||
|
expect(betaPost.community_local).toBe(true);
|
||||||
|
expect(betaPost.creator_local).toBe(false);
|
||||||
|
expect(betaPost.score).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Update a post', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let postRes = await createPost(alpha, search.communities[0].id);
|
||||||
|
|
||||||
|
let updatedPost = await updatePost(alpha, postRes.post);
|
||||||
|
expect(updatedPost.post.name).toBe('A jest test federated post, updated');
|
||||||
|
expect(updatedPost.post.community_local).toBe(false);
|
||||||
|
expect(updatedPost.post.creator_local).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Sticky a post', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let postRes = await createPost(alpha, search.communities[0].id);
|
||||||
|
|
||||||
|
let stickiedPostRes = await stickyPost(alpha, true, postRes.post);
|
||||||
|
expect(stickiedPostRes.post.stickied).toBe(true);
|
||||||
|
|
||||||
|
// Make sure that post is stickied on beta
|
||||||
|
let searchBeta = await searchPost(beta, postRes.post);
|
||||||
|
let betaPost = searchBeta.posts[0];
|
||||||
|
expect(betaPost.community_local).toBe(true);
|
||||||
|
expect(betaPost.creator_local).toBe(false);
|
||||||
|
expect(betaPost.stickied).toBe(true);
|
||||||
|
|
||||||
|
// Unsticky a post
|
||||||
|
let unstickiedPost = await stickyPost(alpha, false, postRes.post);
|
||||||
|
expect(unstickiedPost.post.stickied).toBe(false);
|
||||||
|
|
||||||
|
// Make sure that post is unstickied on beta
|
||||||
|
let searchBeta2 = await searchPost(beta, postRes.post);
|
||||||
|
let betaPost2 = searchBeta2.posts[0];
|
||||||
|
expect(betaPost2.community_local).toBe(true);
|
||||||
|
expect(betaPost2.creator_local).toBe(false);
|
||||||
|
expect(betaPost2.stickied).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Lock a post', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let postRes = await createPost(alpha, search.communities[0].id);
|
||||||
|
|
||||||
|
let lockedPostRes = await lockPost(alpha, true, postRes.post);
|
||||||
|
expect(lockedPostRes.post.locked).toBe(true);
|
||||||
|
|
||||||
|
// Make sure that post is locked on beta
|
||||||
|
let searchBeta = await searchPost(beta, postRes.post);
|
||||||
|
let betaPost = searchBeta.posts[0];
|
||||||
|
expect(betaPost.community_local).toBe(true);
|
||||||
|
expect(betaPost.creator_local).toBe(false);
|
||||||
|
expect(betaPost.locked).toBe(true);
|
||||||
|
|
||||||
|
// Try to make a new comment there, on alpha
|
||||||
|
let comment = await createComment(alpha, postRes.post.id);
|
||||||
|
expect(comment['error']).toBe('locked');
|
||||||
|
|
||||||
|
// Try to create a new comment, on beta
|
||||||
|
let commentBeta = await createComment(beta, betaPost.id);
|
||||||
|
expect(commentBeta['error']).toBe('locked');
|
||||||
|
|
||||||
|
// Unlock a post
|
||||||
|
let unlockedPost = await lockPost(alpha, false, postRes.post);
|
||||||
|
expect(unlockedPost.post.locked).toBe(false);
|
||||||
|
|
||||||
|
// Make sure that post is unlocked on beta
|
||||||
|
let searchBeta2 = await searchPost(beta, postRes.post);
|
||||||
|
let betaPost2 = searchBeta2.posts[0];
|
||||||
|
expect(betaPost2.community_local).toBe(true);
|
||||||
|
expect(betaPost2.creator_local).toBe(false);
|
||||||
|
expect(betaPost2.locked).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Delete a post', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let postRes = await createPost(alpha, search.communities[0].id);
|
||||||
|
|
||||||
|
let deletedPost = await deletePost(alpha, true, postRes.post);
|
||||||
|
expect(deletedPost.post.deleted).toBe(true);
|
||||||
|
|
||||||
|
// Make sure lemmy beta sees post is deleted
|
||||||
|
let createFakeBetaPostToGetId = (await createPost(beta, 2)).post.id - 1;
|
||||||
|
let betaPost = await getPost(beta, createFakeBetaPostToGetId);
|
||||||
|
expect(betaPost.post.deleted).toBe(true);
|
||||||
|
|
||||||
|
// Undelete
|
||||||
|
let undeletedPost = await deletePost(alpha, false, postRes.post);
|
||||||
|
expect(undeletedPost.post.deleted).toBe(false);
|
||||||
|
|
||||||
|
// Make sure lemmy beta sees post is undeleted
|
||||||
|
let betaPost2 = await getPost(beta, createFakeBetaPostToGetId);
|
||||||
|
expect(betaPost2.post.deleted).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Remove a post', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let postRes = await createPost(alpha, search.communities[0].id);
|
||||||
|
|
||||||
|
let removedPost = await removePost(alpha, true, postRes.post);
|
||||||
|
expect(removedPost.post.removed).toBe(true);
|
||||||
|
|
||||||
|
// Make sure lemmy beta sees post is removed
|
||||||
|
let createFakeBetaPostToGetId = (await createPost(beta, 2)).post.id - 1;
|
||||||
|
let betaPost = await getPost(beta, createFakeBetaPostToGetId);
|
||||||
|
expect(betaPost.post.removed).toBe(true);
|
||||||
|
|
||||||
|
// Undelete
|
||||||
|
let undeletedPost = await removePost(alpha, false, postRes.post);
|
||||||
|
expect(undeletedPost.post.removed).toBe(false);
|
||||||
|
|
||||||
|
// Make sure lemmy beta sees post is undeleted
|
||||||
|
let betaPost2 = await getPost(beta, createFakeBetaPostToGetId);
|
||||||
|
expect(betaPost2.post.removed).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Search for a post', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let postRes = await createPost(alpha, search.communities[0].id);
|
||||||
|
let searchBeta = await searchPost(beta, postRes.post);
|
||||||
|
|
||||||
|
expect(searchBeta.posts[0].name).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('A and G subscribe to B (center) A posts, it gets announced to G', async () => {
|
||||||
|
let search = await searchForBetaCommunity(alpha);
|
||||||
|
let postRes = await createPost(alpha, search.communities[0].id);
|
||||||
|
|
||||||
|
let search2 = await searchPost(gamma, postRes.post);
|
||||||
|
expect(search2.posts[0].name).toBeDefined();
|
||||||
|
});
|
71
ui/src/api_tests/private_message.spec.ts
vendored
Normal file
71
ui/src/api_tests/private_message.spec.ts
vendored
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import {
|
||||||
|
alpha,
|
||||||
|
beta,
|
||||||
|
setupLogins,
|
||||||
|
followBeta,
|
||||||
|
createPrivateMessage,
|
||||||
|
updatePrivateMessage,
|
||||||
|
listPrivateMessages,
|
||||||
|
deletePrivateMessage,
|
||||||
|
unfollowRemotes,
|
||||||
|
} from './shared';
|
||||||
|
|
||||||
|
let recipient_id: number;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await setupLogins();
|
||||||
|
recipient_id = (await followBeta(alpha)).community.creator_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await unfollowRemotes(alpha);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Create a private message', async () => {
|
||||||
|
let pmRes = await createPrivateMessage(alpha, recipient_id);
|
||||||
|
expect(pmRes.message.content).toBeDefined();
|
||||||
|
expect(pmRes.message.local).toBe(true);
|
||||||
|
expect(pmRes.message.creator_local).toBe(true);
|
||||||
|
expect(pmRes.message.recipient_local).toBe(false);
|
||||||
|
|
||||||
|
let betaPms = await listPrivateMessages(beta);
|
||||||
|
expect(betaPms.messages[0].content).toBeDefined();
|
||||||
|
expect(betaPms.messages[0].local).toBe(false);
|
||||||
|
expect(betaPms.messages[0].creator_local).toBe(false);
|
||||||
|
expect(betaPms.messages[0].recipient_local).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Update a private message', async () => {
|
||||||
|
let updatedContent = 'A jest test federated private message edited';
|
||||||
|
|
||||||
|
let pmRes = await createPrivateMessage(alpha, recipient_id);
|
||||||
|
let pmUpdated = await updatePrivateMessage(alpha, pmRes.message.id);
|
||||||
|
expect(pmUpdated.message.content).toBe(updatedContent);
|
||||||
|
|
||||||
|
let betaPms = await listPrivateMessages(beta);
|
||||||
|
expect(betaPms.messages[0].content).toBe(updatedContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Delete a private message', async () => {
|
||||||
|
let pmRes = await createPrivateMessage(alpha, recipient_id);
|
||||||
|
let betaPms1 = await listPrivateMessages(beta);
|
||||||
|
let deletedPmRes = await deletePrivateMessage(alpha, true, pmRes.message.id);
|
||||||
|
expect(deletedPmRes.message.deleted).toBe(true);
|
||||||
|
|
||||||
|
// The GetPrivateMessages filters out deleted,
|
||||||
|
// even though they are in the actual database.
|
||||||
|
// no reason to show them
|
||||||
|
let betaPms2 = await listPrivateMessages(beta);
|
||||||
|
expect(betaPms2.messages.length).toBe(betaPms1.messages.length - 1);
|
||||||
|
|
||||||
|
// Undelete
|
||||||
|
let undeletedPmRes = await deletePrivateMessage(
|
||||||
|
alpha,
|
||||||
|
false,
|
||||||
|
pmRes.message.id
|
||||||
|
);
|
||||||
|
expect(undeletedPmRes.message.deleted).toBe(false);
|
||||||
|
|
||||||
|
let betaPms3 = await listPrivateMessages(beta);
|
||||||
|
expect(betaPms3.messages.length).toBe(betaPms1.messages.length);
|
||||||
|
});
|
675
ui/src/api_tests/shared.ts
vendored
Normal file
675
ui/src/api_tests/shared.ts
vendored
Normal file
|
@ -0,0 +1,675 @@
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
|
||||||
|
import {
|
||||||
|
LoginForm,
|
||||||
|
LoginResponse,
|
||||||
|
Post,
|
||||||
|
PostForm,
|
||||||
|
Comment,
|
||||||
|
DeletePostForm,
|
||||||
|
RemovePostForm,
|
||||||
|
StickyPostForm,
|
||||||
|
LockPostForm,
|
||||||
|
PostResponse,
|
||||||
|
SearchResponse,
|
||||||
|
FollowCommunityForm,
|
||||||
|
CommunityResponse,
|
||||||
|
GetFollowedCommunitiesResponse,
|
||||||
|
GetPostResponse,
|
||||||
|
CommentForm,
|
||||||
|
DeleteCommentForm,
|
||||||
|
RemoveCommentForm,
|
||||||
|
CommentResponse,
|
||||||
|
CommunityForm,
|
||||||
|
DeleteCommunityForm,
|
||||||
|
RemoveCommunityForm,
|
||||||
|
CommentLikeForm,
|
||||||
|
CreatePostLikeForm,
|
||||||
|
PrivateMessageForm,
|
||||||
|
EditPrivateMessageForm,
|
||||||
|
DeletePrivateMessageForm,
|
||||||
|
PrivateMessageResponse,
|
||||||
|
PrivateMessagesResponse,
|
||||||
|
GetUserMentionsResponse,
|
||||||
|
} from '../interfaces';
|
||||||
|
|
||||||
|
export interface API {
|
||||||
|
url: string;
|
||||||
|
auth?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function apiUrl(api: API) {
|
||||||
|
return `${api.url}/api/v1`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export let alpha: API = {
|
||||||
|
url: 'http://localhost:8540',
|
||||||
|
};
|
||||||
|
|
||||||
|
export let beta: API = {
|
||||||
|
url: 'http://localhost:8550',
|
||||||
|
};
|
||||||
|
|
||||||
|
export let gamma: API = {
|
||||||
|
url: 'http://localhost:8560',
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function setupLogins() {
|
||||||
|
let form: LoginForm = {
|
||||||
|
username_or_email: 'lemmy_alpha',
|
||||||
|
password: 'lemmy',
|
||||||
|
};
|
||||||
|
|
||||||
|
let resA: Promise<LoginResponse> = fetch(`${apiUrl(alpha)}/user/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(form),
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
let formB = {
|
||||||
|
username_or_email: 'lemmy_beta',
|
||||||
|
password: 'lemmy',
|
||||||
|
};
|
||||||
|
|
||||||
|
let resB: Promise<LoginResponse> = fetch(`${apiUrl(beta)}/user/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(formB),
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
let formC = {
|
||||||
|
username_or_email: 'lemmy_gamma',
|
||||||
|
password: 'lemmy',
|
||||||
|
};
|
||||||
|
|
||||||
|
let resG: Promise<LoginResponse> = fetch(`${apiUrl(gamma)}/user/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(formC),
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
let res = await Promise.all([resA, resB, resG]);
|
||||||
|
alpha.auth = res[0].jwt;
|
||||||
|
beta.auth = res[1].jwt;
|
||||||
|
gamma.auth = res[2].jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createPost(
|
||||||
|
api: API,
|
||||||
|
community_id: number
|
||||||
|
): Promise<PostResponse> {
|
||||||
|
let name = 'A jest test post';
|
||||||
|
let postForm: PostForm = {
|
||||||
|
name,
|
||||||
|
auth: api.auth,
|
||||||
|
community_id,
|
||||||
|
nsfw: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createPostRes: PostResponse = await fetch(`${apiUrl(api)}/post`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(postForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
return createPostRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updatePost(api: API, post: Post): Promise<PostResponse> {
|
||||||
|
let name = 'A jest test federated post, updated';
|
||||||
|
let postForm: PostForm = {
|
||||||
|
name,
|
||||||
|
edit_id: post.id,
|
||||||
|
auth: api.auth,
|
||||||
|
nsfw: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateResponse: PostResponse = await fetch(`${apiUrl(api)}/post`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(postForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
return updateResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deletePost(
|
||||||
|
api: API,
|
||||||
|
deleted: boolean,
|
||||||
|
post: Post
|
||||||
|
): Promise<PostResponse> {
|
||||||
|
let deletePostForm: DeletePostForm = {
|
||||||
|
edit_id: post.id,
|
||||||
|
deleted: deleted,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let deletePostRes: PostResponse = await fetch(`${apiUrl(api)}/post/delete`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(deletePostForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
return deletePostRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removePost(
|
||||||
|
api: API,
|
||||||
|
removed: boolean,
|
||||||
|
post: Post
|
||||||
|
): Promise<PostResponse> {
|
||||||
|
let removePostForm: RemovePostForm = {
|
||||||
|
edit_id: post.id,
|
||||||
|
removed,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let removePostRes: PostResponse = await fetch(`${apiUrl(api)}/post/remove`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(removePostForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
return removePostRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stickyPost(
|
||||||
|
api: API,
|
||||||
|
stickied: boolean,
|
||||||
|
post: Post
|
||||||
|
): Promise<PostResponse> {
|
||||||
|
let stickyPostForm: StickyPostForm = {
|
||||||
|
edit_id: post.id,
|
||||||
|
stickied,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let stickyRes: PostResponse = await fetch(`${apiUrl(api)}/post/sticky`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(stickyPostForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
return stickyRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function lockPost(
|
||||||
|
api: API,
|
||||||
|
locked: boolean,
|
||||||
|
post: Post
|
||||||
|
): Promise<PostResponse> {
|
||||||
|
let lockPostForm: LockPostForm = {
|
||||||
|
edit_id: post.id,
|
||||||
|
locked,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let lockRes: PostResponse = await fetch(`${apiUrl(api)}/post/lock`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(lockPostForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
return lockRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function searchPost(
|
||||||
|
api: API,
|
||||||
|
post: Post
|
||||||
|
): Promise<SearchResponse> {
|
||||||
|
let searchUrl = `${apiUrl(api)}/search?q=${post.ap_id}&type_=All&sort=TopAll`;
|
||||||
|
let searchResponse: SearchResponse = await fetch(searchUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
return searchResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPost(
|
||||||
|
api: API,
|
||||||
|
post_id: number
|
||||||
|
): Promise<GetPostResponse> {
|
||||||
|
let getPostUrl = `${apiUrl(api)}/post?id=${post_id}`;
|
||||||
|
let getPostRes: GetPostResponse = await fetch(getPostUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
return getPostRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function searchComment(
|
||||||
|
api: API,
|
||||||
|
comment: Comment
|
||||||
|
): Promise<SearchResponse> {
|
||||||
|
let searchUrl = `${apiUrl(api)}/search?q=${
|
||||||
|
comment.ap_id
|
||||||
|
}&type_=All&sort=TopAll`;
|
||||||
|
let searchResponse: SearchResponse = await fetch(searchUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
return searchResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function searchForBetaCommunity(
|
||||||
|
api: API
|
||||||
|
): Promise<SearchResponse> {
|
||||||
|
// Make sure lemmy-beta/c/main is cached on lemmy_alpha
|
||||||
|
// Use short-hand search url
|
||||||
|
let searchUrl = `${apiUrl(
|
||||||
|
api
|
||||||
|
)}/search?q=!main@lemmy-beta:8550&type_=All&sort=TopAll`;
|
||||||
|
|
||||||
|
let searchResponse: SearchResponse = await fetch(searchUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
return searchResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function followCommunity(
|
||||||
|
api: API,
|
||||||
|
follow: boolean,
|
||||||
|
community_id: number
|
||||||
|
): Promise<CommunityResponse> {
|
||||||
|
let followForm: FollowCommunityForm = {
|
||||||
|
community_id,
|
||||||
|
follow,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let followRes: CommunityResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/community/follow`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(followForm),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(d => d.json())
|
||||||
|
.catch(_e => {});
|
||||||
|
|
||||||
|
return followRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkFollowedCommunities(
|
||||||
|
api: API
|
||||||
|
): Promise<GetFollowedCommunitiesResponse> {
|
||||||
|
let followedCommunitiesUrl = `${apiUrl(
|
||||||
|
api
|
||||||
|
)}/user/followed_communities?&auth=${api.auth}`;
|
||||||
|
let followedCommunitiesRes: GetFollowedCommunitiesResponse = await fetch(
|
||||||
|
followedCommunitiesUrl,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return followedCommunitiesRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function likePost(
|
||||||
|
api: API,
|
||||||
|
score: number,
|
||||||
|
post: Post
|
||||||
|
): Promise<PostResponse> {
|
||||||
|
let likePostForm: CreatePostLikeForm = {
|
||||||
|
post_id: post.id,
|
||||||
|
score: score,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let likePostRes: PostResponse = await fetch(`${apiUrl(api)}/post/like`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(likePostForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
return likePostRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createComment(
|
||||||
|
api: API,
|
||||||
|
post_id: number,
|
||||||
|
parent_id?: number,
|
||||||
|
content = 'a jest test comment'
|
||||||
|
): Promise<CommentResponse> {
|
||||||
|
let commentForm: CommentForm = {
|
||||||
|
content,
|
||||||
|
post_id,
|
||||||
|
parent_id,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createResponse: CommentResponse = await fetch(`${apiUrl(api)}/comment`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(commentForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
return createResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateComment(
|
||||||
|
api: API,
|
||||||
|
edit_id: number,
|
||||||
|
content = 'A jest test federated comment update'
|
||||||
|
): Promise<CommentResponse> {
|
||||||
|
let commentForm: CommentForm = {
|
||||||
|
content,
|
||||||
|
edit_id,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateResponse: CommentResponse = await fetch(`${apiUrl(api)}/comment`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(commentForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
return updateResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteComment(
|
||||||
|
api: API,
|
||||||
|
deleted: boolean,
|
||||||
|
edit_id: number
|
||||||
|
): Promise<CommentResponse> {
|
||||||
|
let deleteCommentForm: DeleteCommentForm = {
|
||||||
|
edit_id,
|
||||||
|
deleted,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteCommentRes: CommentResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/comment/delete`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(deleteCommentForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return deleteCommentRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeComment(
|
||||||
|
api: API,
|
||||||
|
removed: boolean,
|
||||||
|
edit_id: number
|
||||||
|
): Promise<CommentResponse> {
|
||||||
|
let removeCommentForm: RemoveCommentForm = {
|
||||||
|
edit_id,
|
||||||
|
removed,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let removeCommentRes: CommentResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/comment/remove`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(removeCommentForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return removeCommentRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMentions(api: API): Promise<GetUserMentionsResponse> {
|
||||||
|
let getMentionUrl = `${apiUrl(
|
||||||
|
api
|
||||||
|
)}/user/mention?sort=New&unread_only=false&auth=${api.auth}`;
|
||||||
|
let getMentionsRes: GetUserMentionsResponse = await fetch(getMentionUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
return getMentionsRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function likeComment(
|
||||||
|
api: API,
|
||||||
|
score: number,
|
||||||
|
comment: Comment
|
||||||
|
): Promise<CommentResponse> {
|
||||||
|
let likeCommentForm: CommentLikeForm = {
|
||||||
|
comment_id: comment.id,
|
||||||
|
score,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let likeCommentRes: CommentResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/comment/like`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(likeCommentForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return likeCommentRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createCommunity(
|
||||||
|
api: API,
|
||||||
|
name_: string = randomString(5)
|
||||||
|
): Promise<CommunityResponse> {
|
||||||
|
let communityForm: CommunityForm = {
|
||||||
|
name: name_,
|
||||||
|
title: name_,
|
||||||
|
category_id: 1,
|
||||||
|
nsfw: false,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createCommunityRes: CommunityResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/community`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(communityForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return createCommunityRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteCommunity(
|
||||||
|
api: API,
|
||||||
|
deleted: boolean,
|
||||||
|
edit_id: number
|
||||||
|
): Promise<CommunityResponse> {
|
||||||
|
let deleteCommunityForm: DeleteCommunityForm = {
|
||||||
|
edit_id,
|
||||||
|
deleted,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteResponse: CommunityResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/community/delete`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(deleteCommunityForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return deleteResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeCommunity(
|
||||||
|
api: API,
|
||||||
|
removed: boolean,
|
||||||
|
edit_id: number
|
||||||
|
): Promise<CommunityResponse> {
|
||||||
|
let removeCommunityForm: RemoveCommunityForm = {
|
||||||
|
edit_id,
|
||||||
|
removed,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let removeResponse: CommunityResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/community/remove`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(removeCommunityForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return removeResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createPrivateMessage(
|
||||||
|
api: API,
|
||||||
|
recipient_id: number
|
||||||
|
): Promise<PrivateMessageResponse> {
|
||||||
|
let content = 'A jest test federated private message';
|
||||||
|
let privateMessageForm: PrivateMessageForm = {
|
||||||
|
content,
|
||||||
|
recipient_id,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createRes: PrivateMessageResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/private_message`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(privateMessageForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return createRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updatePrivateMessage(
|
||||||
|
api: API,
|
||||||
|
edit_id: number
|
||||||
|
): Promise<PrivateMessageResponse> {
|
||||||
|
let updatedContent = 'A jest test federated private message edited';
|
||||||
|
let updatePrivateMessageForm: EditPrivateMessageForm = {
|
||||||
|
content: updatedContent,
|
||||||
|
edit_id,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateRes: PrivateMessageResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/private_message`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(updatePrivateMessageForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return updateRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deletePrivateMessage(
|
||||||
|
api: API,
|
||||||
|
deleted: boolean,
|
||||||
|
edit_id: number
|
||||||
|
): Promise<PrivateMessageResponse> {
|
||||||
|
let deletePrivateMessageForm: DeletePrivateMessageForm = {
|
||||||
|
deleted,
|
||||||
|
edit_id,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteRes: PrivateMessageResponse = await fetch(
|
||||||
|
`${apiUrl(api)}/private_message/delete`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(deletePrivateMessageForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
return deleteRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listPrivateMessages(
|
||||||
|
api: API
|
||||||
|
): Promise<PrivateMessagesResponse> {
|
||||||
|
let getPrivateMessagesUrl = `${apiUrl(api)}/private_message/list?auth=${
|
||||||
|
api.auth
|
||||||
|
}&unread_only=false&limit=999`;
|
||||||
|
|
||||||
|
let getPrivateMessagesRes: PrivateMessagesResponse = await fetch(
|
||||||
|
getPrivateMessagesUrl,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
return getPrivateMessagesRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unfollowRemotes(
|
||||||
|
api: API
|
||||||
|
): Promise<GetFollowedCommunitiesResponse> {
|
||||||
|
// Unfollow all remote communities
|
||||||
|
let followed = await checkFollowedCommunities(api);
|
||||||
|
let remoteFollowed = followed.communities.filter(
|
||||||
|
c => c.community_local == false
|
||||||
|
);
|
||||||
|
for (let cu of remoteFollowed) {
|
||||||
|
await followCommunity(api, false, cu.community_id);
|
||||||
|
}
|
||||||
|
let followed2 = await checkFollowedCommunities(api);
|
||||||
|
return followed2;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function followBeta(api: API): Promise<CommunityResponse> {
|
||||||
|
await unfollowRemotes(api);
|
||||||
|
|
||||||
|
// Cache it
|
||||||
|
let search = await searchForBetaCommunity(api);
|
||||||
|
|
||||||
|
// Unfollow first
|
||||||
|
let follow = await followCommunity(
|
||||||
|
api,
|
||||||
|
true,
|
||||||
|
search.communities.filter(c => c.local == false)[0].id
|
||||||
|
);
|
||||||
|
return follow;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wrapper(form: any): string {
|
||||||
|
return JSON.stringify(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomString(length: number): string {
|
||||||
|
var result = '';
|
||||||
|
var characters = 'abcdefghijklmnopqrstuvwxyz0123456789_';
|
||||||
|
var charactersLength = characters.length;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
Loading…
Reference in a new issue