"dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"isahc 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
"strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
percent-encoding = "2.1.0"
isahc = "0.9"
comrak = "0.7"
+tokio = "0.2.18"
+futures = "0.3.4"
comments: Vec<CommentView>,
}
-impl Perform<CommentResponse> for Oper<CreateComment> {
+impl Perform for Oper<CreateComment> {
+ type Response = CommentResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<CommentResponse, Error> {
let data: &CreateComment = &self.data;
let hostname = &format!("https://{}", Settings::get().hostname);
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Check for a community ban
}
}
-impl Perform<CommentResponse> for Oper<EditComment> {
+impl Perform for Oper<EditComment> {
+ type Response = CommentResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<CommentResponse, Error> {
let data: &EditComment = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let orig_comment = CommentView::read(&conn, data.edit_id, None)?;
}
}
-impl Perform<CommentResponse> for Oper<SaveComment> {
+impl Perform for Oper<SaveComment> {
+ type Response = CommentResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<CommentResponse, Error> {
let data: &SaveComment = &self.data;
user_id,
};
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
if data.save {
}
}
-impl Perform<CommentResponse> for Oper<CreateCommentLike> {
+impl Perform for Oper<CreateCommentLike> {
+ type Response = CommentResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<CommentResponse, Error> {
let data: &CreateCommentLike = &self.data;
let mut recipient_ids = Vec::new();
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Don't do a downvote if site has downvotes disabled
}
}
-impl Perform<GetCommentsResponse> for Oper<GetComments> {
+impl Perform for Oper<GetComments> {
+ type Response = GetCommentsResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetCommentsResponse, Error> {
let data: &GetComments = &self.data;
let type_ = ListingType::from_str(&data.type_)?;
let sort = SortType::from_str(&data.sort)?;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let comments = match CommentQueryBuilder::create(&conn)
auth: String,
}
-impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
+impl Perform for Oper<GetCommunity> {
+ type Response = GetCommunityResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetCommunityResponse, Error> {
let data: &GetCommunity = &self.data;
None => None,
};
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let community_id = match data.id {
}
}
-impl Perform<CommunityResponse> for Oper<CreateCommunity> {
+impl Perform for Oper<CreateCommunity> {
+ type Response = CommunityResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<CommunityResponse, Error> {
let data: &CreateCommunity = &self.data;
let user_id = claims.id;
- if let Some(rl) = &rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_register(&rl.ip, true)?;
- }
-
let conn = pool.get()?;
// Check for a site ban
let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_register(&rl.ip, false)?;
- }
-
Ok(CommunityResponse {
community: community_view,
})
}
}
-impl Perform<CommunityResponse> for Oper<EditCommunity> {
+impl Perform for Oper<EditCommunity> {
+ type Response = CommunityResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<CommunityResponse, Error> {
let data: &EditCommunity = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Check for a site ban
}
}
-impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
+impl Perform for Oper<ListCommunities> {
+ type Response = ListCommunitiesResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<ListCommunitiesResponse, Error> {
let data: &ListCommunities = &self.data;
let sort = SortType::from_str(&data.sort)?;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let communities = CommunityQueryBuilder::create(&conn)
}
}
-impl Perform<CommunityResponse> for Oper<FollowCommunity> {
+impl Perform for Oper<FollowCommunity> {
+ type Response = CommunityResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<CommunityResponse, Error> {
let data: &FollowCommunity = &self.data;
user_id,
};
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
if data.follow {
}
}
-impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
+impl Perform for Oper<GetFollowedCommunities> {
+ type Response = GetFollowedCommunitiesResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetFollowedCommunitiesResponse, Error> {
let data: &GetFollowedCommunities = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let communities: Vec<CommunityFollowerView> =
}
}
-impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
+impl Perform for Oper<BanFromCommunity> {
+ type Response = BanFromCommunityResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<BanFromCommunityResponse, Error> {
let data: &BanFromCommunity = &self.data;
user_id: data.user_id,
};
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
if data.ban {
}
}
-impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
+impl Perform for Oper<AddModToCommunity> {
+ type Response = AddModToCommunityResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<AddModToCommunityResponse, Error> {
let data: &AddModToCommunity = &self.data;
user_id: data.user_id,
};
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
if data.added {
}
}
-impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
+impl Perform for Oper<TransferCommunity> {
+ type Response = GetCommunityResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetCommunityResponse, Error> {
let data: &TransferCommunity = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let read_community = Community::read(&conn, data.community_id)?;
naive_now, remove_slurs, send_email, slur_check, slurs_vec_to_str,
};
-use crate::rate_limit::RateLimitInfo;
use crate::settings::Settings;
use crate::websocket::UserOperation;
use crate::websocket::{
}
}
-pub trait Perform<T> {
+pub trait Perform {
+ type Response: serde::ser::Serialize;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
- ) -> Result<T, Error>
- where
- T: Sized;
+ ) -> Result<Self::Response, Error>;
}
auth: String,
}
-impl Perform<PostResponse> for Oper<CreatePost> {
+impl Perform for Oper<CreatePost> {
+ type Response = PostResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<PostResponse, Error> {
let data: &CreatePost = &self.data;
let user_id = claims.id;
- if let Some(rl) = &rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_post(&rl.ip, true)?;
- }
-
let conn = pool.get()?;
// Check for a community ban
Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
};
- if let Some(rl) = &rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_post(&rl.ip, false)?;
- }
-
let res = PostResponse { post: post_view };
if let Some(ws) = websocket_info {
}
}
-impl Perform<GetPostResponse> for Oper<GetPost> {
+impl Perform for Oper<GetPost> {
+ type Response = GetPostResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetPostResponse, Error> {
let data: &GetPost = &self.data;
None => None,
};
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let post_view = match PostView::read(&conn, data.id, user_id) {
}
}
-impl Perform<GetPostsResponse> for Oper<GetPosts> {
+impl Perform for Oper<GetPosts> {
+ type Response = GetPostsResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetPostsResponse, Error> {
let data: &GetPosts = &self.data;
let type_ = ListingType::from_str(&data.type_)?;
let sort = SortType::from_str(&data.sort)?;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let posts = match PostQueryBuilder::create(&conn)
}
}
-impl Perform<PostResponse> for Oper<CreatePostLike> {
+impl Perform for Oper<CreatePostLike> {
+ type Response = PostResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<PostResponse, Error> {
let data: &CreatePostLike = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Don't do a downvote if site has downvotes disabled
}
}
-impl Perform<PostResponse> for Oper<EditPost> {
+impl Perform for Oper<EditPost> {
+ type Response = PostResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<PostResponse, Error> {
let data: &EditPost = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Verify its the creator or a mod or admin
}
}
-impl Perform<PostResponse> for Oper<SavePost> {
+impl Perform for Oper<SavePost> {
+ type Response = PostResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<PostResponse, Error> {
let data: &SavePost = &self.data;
user_id,
};
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
if data.save {
auth: String,
}
-impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
+impl Perform for Oper<ListCategories> {
+ type Response = ListCategoriesResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<ListCategoriesResponse, Error> {
let _data: &ListCategories = &self.data;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let categories: Vec<Category> = Category::list_all(&conn)?;
}
}
-impl Perform<GetModlogResponse> for Oper<GetModlog> {
+impl Perform for Oper<GetModlog> {
+ type Response = GetModlogResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetModlogResponse, Error> {
let data: &GetModlog = &self.data;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let removed_posts = ModRemovePostView::list(
}
}
-impl Perform<SiteResponse> for Oper<CreateSite> {
+impl Perform for Oper<CreateSite> {
+ type Response = SiteResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<SiteResponse, Error> {
let data: &CreateSite = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Make sure user is an admin
}
}
-impl Perform<SiteResponse> for Oper<EditSite> {
+impl Perform for Oper<EditSite> {
+ type Response = SiteResponse;
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<SiteResponse, Error> {
let data: &EditSite = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Make sure user is an admin
}
}
-impl Perform<GetSiteResponse> for Oper<GetSite> {
+impl Perform for Oper<GetSite> {
+ type Response = GetSiteResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetSiteResponse, Error> {
let _data: &GetSite = &self.data;
- if let Some(rl) = &rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// TODO refactor this a little
admin: true,
show_nsfw: true,
};
- let login_response = Oper::new(register).perform(
- pool.clone(),
- websocket_info.clone(),
- rate_limit_info.clone(),
- )?;
+ let login_response = Oper::new(register).perform(pool.clone(), websocket_info.clone())?;
info!("Admin {} created", setup.admin_username);
let create_site = CreateSite {
enable_nsfw: false,
auth: login_response.jwt,
};
- Oper::new(create_site).perform(pool, websocket_info.clone(), rate_limit_info)?;
+ Oper::new(create_site).perform(pool, websocket_info.clone())?;
info!("Site {} created", setup.site_name);
Some(SiteView::read(&conn)?)
} else {
}
}
-impl Perform<SearchResponse> for Oper<Search> {
+impl Perform for Oper<Search> {
+ type Response = SearchResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<SearchResponse, Error> {
let data: &Search = &self.data;
// TODO no clean / non-nsfw searching rn
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
match type_ {
}
}
-impl Perform<GetSiteResponse> for Oper<TransferSite> {
+impl Perform for Oper<TransferSite> {
+ type Response = GetSiteResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetSiteResponse, Error> {
let data: &TransferSite = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let read_site = Site::read(&conn, 1)?;
}
}
-impl Perform<GetSiteConfigResponse> for Oper<GetSiteConfig> {
+impl Perform for Oper<GetSiteConfig> {
+ type Response = GetSiteConfigResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetSiteConfigResponse, Error> {
let data: &GetSiteConfig = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Only let admins read this
}
}
-impl Perform<GetSiteConfigResponse> for Oper<SaveSiteConfig> {
+impl Perform for Oper<SaveSiteConfig> {
+ type Response = GetSiteConfigResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetSiteConfigResponse, Error> {
let data: &SaveSiteConfig = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Only let admins read this
pub user_id: i32,
}
-impl Perform<LoginResponse> for Oper<Login> {
+impl Perform for Oper<Login> {
+ type Response = LoginResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<LoginResponse, Error> {
let data: &Login = &self.data;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Fetch that username / email
}
}
-impl Perform<LoginResponse> for Oper<Register> {
+impl Perform for Oper<Register> {
+ type Response = LoginResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<LoginResponse, Error> {
let data: &Register = &self.data;
- if let Some(rl) = &rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_register(&rl.ip, true)?;
- }
-
let conn = pool.get()?;
// Make sure site has open registration
};
}
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_register(&rl.ip, false)?;
- }
-
// Return the jwt
Ok(LoginResponse {
jwt: inserted_user.jwt(),
}
}
-impl Perform<LoginResponse> for Oper<SaveUserSettings> {
+impl Perform for Oper<SaveUserSettings> {
+ type Response = LoginResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<LoginResponse, Error> {
let data: &SaveUserSettings = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let read_user = User_::read(&conn, user_id)?;
}
}
-impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
+impl Perform for Oper<GetUserDetails> {
+ type Response = GetUserDetailsResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetUserDetailsResponse, Error> {
let data: &GetUserDetails = &self.data;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let user_claims: Option<Claims> = match &data.auth {
}
}
-impl Perform<AddAdminResponse> for Oper<AddAdmin> {
+impl Perform for Oper<AddAdmin> {
+ type Response = AddAdminResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<AddAdminResponse, Error> {
let data: &AddAdmin = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Make sure user is an admin
}
}
-impl Perform<BanUserResponse> for Oper<BanUser> {
+impl Perform for Oper<BanUser> {
+ type Response = BanUserResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<BanUserResponse, Error> {
let data: &BanUser = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Make sure user is an admin
}
}
-impl Perform<GetRepliesResponse> for Oper<GetReplies> {
+impl Perform for Oper<GetReplies> {
+ type Response = GetRepliesResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetRepliesResponse, Error> {
let data: &GetReplies = &self.data;
let sort = SortType::from_str(&data.sort)?;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let replies = ReplyQueryBuilder::create(&conn, user_id)
}
}
-impl Perform<GetUserMentionsResponse> for Oper<GetUserMentions> {
+impl Perform for Oper<GetUserMentions> {
+ type Response = GetUserMentionsResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetUserMentionsResponse, Error> {
let data: &GetUserMentions = &self.data;
let sort = SortType::from_str(&data.sort)?;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let mentions = UserMentionQueryBuilder::create(&conn, user_id)
}
}
-impl Perform<UserMentionResponse> for Oper<EditUserMention> {
+impl Perform for Oper<EditUserMention> {
+ type Response = UserMentionResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<UserMentionResponse, Error> {
let data: &EditUserMention = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let user_mention = UserMention::read(&conn, data.user_mention_id)?;
}
}
-impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
+impl Perform for Oper<MarkAllAsRead> {
+ type Response = GetRepliesResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<GetRepliesResponse, Error> {
let data: &MarkAllAsRead = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let replies = ReplyQueryBuilder::create(&conn, user_id)
}
}
-impl Perform<LoginResponse> for Oper<DeleteAccount> {
+impl Perform for Oper<DeleteAccount> {
+ type Response = LoginResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<LoginResponse, Error> {
let data: &DeleteAccount = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let user: User_ = User_::read(&conn, user_id)?;
}
}
-impl Perform<PasswordResetResponse> for Oper<PasswordReset> {
+impl Perform for Oper<PasswordReset> {
+ type Response = PasswordResetResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<PasswordResetResponse, Error> {
let data: &PasswordReset = &self.data;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Fetch that email
}
}
-impl Perform<LoginResponse> for Oper<PasswordChange> {
+impl Perform for Oper<PasswordChange> {
+ type Response = LoginResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<LoginResponse, Error> {
let data: &PasswordChange = &self.data;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Fetch the user_id from the token
}
}
-impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
+impl Perform for Oper<CreatePrivateMessage> {
+ type Response = PrivateMessageResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<PrivateMessageResponse, Error> {
let data: &CreatePrivateMessage = &self.data;
let hostname = &format!("https://{}", Settings::get().hostname);
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
// Check for a site ban
}
}
-impl Perform<PrivateMessageResponse> for Oper<EditPrivateMessage> {
+impl Perform for Oper<EditPrivateMessage> {
+ type Response = PrivateMessageResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<PrivateMessageResponse, Error> {
let data: &EditPrivateMessage = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let orig_private_message = PrivateMessage::read(&conn, data.edit_id)?;
}
}
-impl Perform<PrivateMessagesResponse> for Oper<GetPrivateMessages> {
+impl Perform for Oper<GetPrivateMessages> {
+ type Response = PrivateMessagesResponse;
+
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
_websocket_info: Option<WebsocketInfo>,
- rate_limit_info: Option<RateLimitInfo>,
) -> Result<PrivateMessagesResponse, Error> {
let data: &GetPrivateMessages = &self.data;
let user_id = claims.id;
- if let Some(rl) = rate_limit_info {
- rl.rate_limiter
- .lock()
- .unwrap()
- .check_rate_limit_message(&rl.ip, false)?;
- }
-
let conn = pool.get()?;
let messages = PrivateMessageQueryBuilder::create(&conn, user_id)
}
}
-impl Perform<UserJoinResponse> for Oper<UserJoin> {
+impl Perform for Oper<UserJoin> {
+ type Response = UserJoinResponse;
+
fn perform(
&self,
_pool: Pool<ConnectionManager<PgConnection>>,
websocket_info: Option<WebsocketInfo>,
- _rate_limit_info: Option<RateLimitInfo>,
) -> Result<UserJoinResponse, Error> {
let data: &UserJoin = &self.data;
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
use lemmy_server::{
- rate_limit::rate_limiter::RateLimiter,
+ rate_limit::{rate_limiter::RateLimiter, RateLimit},
routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket},
settings::Settings,
websocket::server::*,
};
-use std::{
- io,
- sync::{Arc, Mutex},
-};
+use std::{io, sync::Arc};
+use tokio::sync::Mutex;
embed_migrations!();
embedded_migrations::run(&conn).unwrap();
// Set up the rate limiter
- let rate_limiter = Arc::new(Mutex::new(RateLimiter::default()));
+ let rate_limiter = RateLimit(Arc::new(Mutex::new(RateLimiter::default())));
// Set up websocket server
let server = ChatServer::startup(pool.clone(), rate_limiter.clone()).start();
// Create Http server with websocket support
HttpServer::new(move || {
let settings = Settings::get();
+ let rate_limiter = rate_limiter.clone();
App::new()
.wrap(middleware::Logger::default())
.data(pool.clone())
.data(server.clone())
- .data(rate_limiter.clone())
// The routes
- .configure(api::config)
+ .configure(move |cfg| api::config(cfg, &rate_limiter))
.configure(federation::config)
.configure(feeds::config)
.configure(index::config)
use super::{IPAddr, Settings};
use crate::api::APIError;
+use crate::settings::RateLimitConfig;
+use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use failure::Error;
+use futures::future::{ok, Ready};
use log::warn;
-use rate_limiter::RateLimiter;
+use rate_limiter::{RateLimitType, RateLimiter};
use std::collections::HashMap;
+use std::future::Future;
+use std::pin::Pin;
use std::sync::Arc;
-use std::sync::Mutex;
+use std::task::{Context, Poll};
use std::time::SystemTime;
use strum::IntoEnumIterator;
+use tokio::sync::Mutex;
#[derive(Debug, Clone)]
-pub struct RateLimitInfo {
- pub rate_limiter: Arc<Mutex<RateLimiter>>,
- pub ip: IPAddr,
+pub struct RateLimit(pub Arc<Mutex<RateLimiter>>);
+
+#[derive(Debug, Clone)]
+pub struct RateLimited(Arc<Mutex<RateLimiter>>, RateLimitType);
+
+pub struct RateLimitedMiddleware<S>(RateLimited, S);
+
+impl RateLimit {
+ pub fn message(&self) -> RateLimited {
+ self.kind(RateLimitType::Message)
+ }
+
+ pub fn post(&self) -> RateLimited {
+ self.kind(RateLimitType::Post)
+ }
+
+ pub fn register(&self) -> RateLimited {
+ self.kind(RateLimitType::Register)
+ }
+
+ fn kind(&self, type_: RateLimitType) -> RateLimited {
+ RateLimited(self.0.clone(), type_)
+ }
+}
+
+impl RateLimited {
+ pub async fn wrap<T, E>(
+ self,
+ ip_addr: String,
+ fut: impl Future<Output = Result<T, E>>,
+ ) -> Result<T, E>
+ where
+ E: From<failure::Error>,
+ {
+ let rate_limit: RateLimitConfig = actix_web::web::block(move || {
+ // needs to be in a web::block because the RwLock in settings is from stdlib
+ Ok(Settings::get().rate_limit.clone()) as Result<_, failure::Error>
+ })
+ .await
+ .map_err(|e| match e {
+ actix_web::error::BlockingError::Error(e) => e,
+ _ => APIError::err("Operation canceled").into(),
+ })?;
+
+ // before
+ {
+ let mut limiter = self.0.lock().await;
+
+ match self.1 {
+ RateLimitType::Message => {
+ limiter.check_rate_limit_full(
+ self.1,
+ &ip_addr,
+ rate_limit.message,
+ rate_limit.message_per_second,
+ false,
+ )?;
+
+ return fut.await;
+ }
+ RateLimitType::Post => {
+ limiter.check_rate_limit_full(
+ self.1.clone(),
+ &ip_addr,
+ rate_limit.post,
+ rate_limit.post_per_second,
+ true,
+ )?;
+ }
+ RateLimitType::Register => {
+ limiter.check_rate_limit_full(
+ self.1,
+ &ip_addr,
+ rate_limit.register,
+ rate_limit.register_per_second,
+ true,
+ )?;
+ }
+ };
+ }
+
+ let res = fut.await;
+
+ // after
+ {
+ let mut limiter = self.0.lock().await;
+ if res.is_ok() {
+ match self.1 {
+ RateLimitType::Post => {
+ limiter.check_rate_limit_full(
+ self.1,
+ &ip_addr,
+ rate_limit.post,
+ rate_limit.post_per_second,
+ false,
+ )?;
+ }
+ RateLimitType::Register => {
+ limiter.check_rate_limit_full(
+ self.1,
+ &ip_addr,
+ rate_limit.register,
+ rate_limit.register_per_second,
+ false,
+ )?;
+ }
+ _ => (),
+ };
+ }
+ }
+
+ res
+ }
+}
+
+impl<S> Transform<S> for RateLimited
+where
+ S: Service<Request = ServiceRequest, Response = ServiceResponse, Error = actix_web::Error>,
+ S::Future: 'static,
+{
+ type Request = S::Request;
+ type Response = S::Response;
+ type Error = actix_web::Error;
+ type InitError = ();
+ type Transform = RateLimitedMiddleware<S>;
+ type Future = Ready<Result<Self::Transform, Self::InitError>>;
+
+ fn new_transform(&self, service: S) -> Self::Future {
+ ok(RateLimitedMiddleware(self.clone(), service))
+ }
+}
+
+impl<S> Service for RateLimitedMiddleware<S>
+where
+ S: Service<Request = ServiceRequest, Response = ServiceResponse, Error = actix_web::Error>,
+ S::Future: 'static,
+{
+ type Request = S::Request;
+ type Response = S::Response;
+ type Error = actix_web::Error;
+ type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
+
+ fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ self.1.poll_ready(cx)
+ }
+
+ fn call(&mut self, req: S::Request) -> Self::Future {
+ let ip_addr = req
+ .connection_info()
+ .remote()
+ .unwrap_or("127.0.0.1:12345")
+ .split(':')
+ .next()
+ .unwrap_or("127.0.0.1")
+ .to_string();
+
+ let fut = self.0.clone().wrap(ip_addr, self.1.call(req));
+
+ Box::pin(async move { fut.await.map_err(actix_web::Error::from) })
+ }
}
}
#[allow(clippy::float_cmp)]
- fn check_rate_limit_full(
+ pub(super) fn check_rate_limit_full(
&mut self,
type_: RateLimitType,
ip: &str,
use crate::api::post::*;
use crate::api::site::*;
use crate::api::user::*;
+use crate::rate_limit::RateLimit;
+use actix_web::guard;
#[rustfmt::skip]
-pub fn config(cfg: &mut web::ServiceConfig) {
- cfg
- // Site
- .route("/api/v1/site", web::get().to(route_get::<GetSite, GetSiteResponse>))
- .route("/api/v1/categories", web::get().to(route_get::<ListCategories, ListCategoriesResponse>))
- .route("/api/v1/modlog", web::get().to(route_get::<GetModlog, GetModlogResponse>))
- .route("/api/v1/search", web::get().to(route_get::<Search, SearchResponse>))
- // Community
- .route("/api/v1/community", web::post().to(route_post::<CreateCommunity, CommunityResponse>))
- .route("/api/v1/community", web::get().to(route_get::<GetCommunity, GetCommunityResponse>))
- .route("/api/v1/community", web::put().to(route_post::<EditCommunity, CommunityResponse>))
- .route("/api/v1/community/list", web::get().to(route_get::<ListCommunities, ListCommunitiesResponse>))
- .route("/api/v1/community/follow", web::post().to(route_post::<FollowCommunity, CommunityResponse>))
- // Post
- .route("/api/v1/post", web::post().to(route_post::<CreatePost, PostResponse>))
- .route("/api/v1/post", web::put().to(route_post::<EditPost, PostResponse>))
- .route("/api/v1/post", web::get().to(route_get::<GetPost, GetPostResponse>))
- .route("/api/v1/post/list", web::get().to(route_get::<GetPosts, GetPostsResponse>))
- .route("/api/v1/post/like", web::post().to(route_post::<CreatePostLike, PostResponse>))
- .route("/api/v1/post/save", web::put().to(route_post::<SavePost, PostResponse>))
- // Comment
- .route("/api/v1/comment", web::post().to(route_post::<CreateComment, CommentResponse>))
- .route("/api/v1/comment", web::put().to(route_post::<EditComment, CommentResponse>))
- .route("/api/v1/comment/like", web::post().to(route_post::<CreateCommentLike, CommentResponse>))
- .route("/api/v1/comment/save", web::put().to(route_post::<SaveComment, CommentResponse>))
- // User
- .route("/api/v1/user", web::get().to(route_get::<GetUserDetails, GetUserDetailsResponse>))
- .route("/api/v1/user/mention", web::get().to(route_get::<GetUserMentions, GetUserMentionsResponse>))
- .route("/api/v1/user/mention", web::put().to(route_post::<EditUserMention, UserMentionResponse>))
- .route("/api/v1/user/replies", web::get().to(route_get::<GetReplies, GetRepliesResponse>))
- .route("/api/v1/user/followed_communities", web::get().to(route_get::<GetFollowedCommunities, GetFollowedCommunitiesResponse>))
- // Mod actions
- .route("/api/v1/community/transfer", web::post().to(route_post::<TransferCommunity, GetCommunityResponse>))
- .route("/api/v1/community/ban_user", web::post().to(route_post::<BanFromCommunity, BanFromCommunityResponse>))
- .route("/api/v1/community/mod", web::post().to(route_post::<AddModToCommunity, AddModToCommunityResponse>))
- // Admin actions
- .route("/api/v1/site", web::post().to(route_post::<CreateSite, SiteResponse>))
- .route("/api/v1/site", web::put().to(route_post::<EditSite, SiteResponse>))
- .route("/api/v1/site/transfer", web::post().to(route_post::<TransferSite, GetSiteResponse>))
- .route("/api/v1/site/config", web::get().to(route_get::<GetSiteConfig, GetSiteConfigResponse>))
- .route("/api/v1/site/config", web::put().to(route_post::<SaveSiteConfig, GetSiteConfigResponse>))
- .route("/api/v1/admin/add", web::post().to(route_post::<AddAdmin, AddAdminResponse>))
- .route("/api/v1/user/ban", web::post().to(route_post::<BanUser, BanUserResponse>))
- // User account actions
- .route("/api/v1/user/login", web::post().to(route_post::<Login, LoginResponse>))
- .route("/api/v1/user/register", web::post().to(route_post::<Register, LoginResponse>))
- .route("/api/v1/user/delete_account", web::post().to(route_post::<DeleteAccount, LoginResponse>))
- .route("/api/v1/user/password_reset", web::post().to(route_post::<PasswordReset, PasswordResetResponse>))
- .route("/api/v1/user/password_change", web::post().to(route_post::<PasswordChange, LoginResponse>))
- .route("/api/v1/user/mark_all_as_read", web::post().to(route_post::<MarkAllAsRead, GetRepliesResponse>))
- .route("/api/v1/user/save_user_settings", web::put().to(route_post::<SaveUserSettings, LoginResponse>));
+pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
+ cfg.service(
+ web::scope("/api/v1")
+ // Site
+ .service(
+ web::scope("/site")
+ .wrap(rate_limit.message())
+ .route("", web::get().to(route_get::<GetSite>))
+ // Admin Actions
+ .route("", web::post().to(route_post::<CreateSite>))
+ .route("", web::put().to(route_post::<EditSite>))
+ .route("/transfer", web::post().to(route_post::<TransferSite>))
+ .route("/config", web::get().to(route_get::<GetSiteConfig>))
+ .route("/config", web::put().to(route_post::<SaveSiteConfig>)),
+ )
+ .service(
+ web::resource("/categories")
+ .wrap(rate_limit.message())
+ .route(web::get().to(route_get::<ListCategories>)),
+ )
+ .service(
+ web::resource("/modlog")
+ .wrap(rate_limit.message())
+ .route(web::get().to(route_get::<GetModlog>)),
+ )
+ .service(
+ web::resource("/search")
+ .wrap(rate_limit.message())
+ .route(web::get().to(route_get::<Search>)),
+ )
+ // Community
+ .service(
+ web::scope("/community")
+ .wrap(rate_limit.message())
+ .route("", web::post().to(route_post::<CreateCommunity>))
+ .route("", web::get().to(route_get::<GetCommunity>))
+ .route("", web::put().to(route_post::<EditCommunity>))
+ .route("/list", web::get().to(route_get::<ListCommunities>))
+ .route("/follow", web::post().to(route_post::<FollowCommunity>))
+ // Mod Actions
+ .route("/transfer", web::post().to(route_post::<TransferCommunity>))
+ .route("/ban_user", web::post().to(route_post::<BanFromCommunity>))
+ .route("/mod", web::post().to(route_post::<AddModToCommunity>)),
+ )
+ // Post
+ .service(
+ // Handle POST to /post separately to add the post() rate limitter
+ web::resource("/post")
+ .guard(guard::Post())
+ .wrap(rate_limit.post())
+ .route(web::post().to(route_post::<CreatePost>)),
+ )
+ .service(
+ web::scope("/post")
+ .wrap(rate_limit.message())
+ .route("", web::get().to(route_get::<GetPost>))
+ .route("", web::put().to(route_post::<EditPost>))
+ .route("/list", web::get().to(route_get::<GetPosts>))
+ .route("/like", web::post().to(route_post::<CreatePostLike>))
+ .route("/save", web::put().to(route_post::<SavePost>)),
+ )
+ // Comment
+ .service(
+ web::scope("/comment")
+ .wrap(rate_limit.message())
+ .route("", web::post().to(route_post::<CreateComment>))
+ .route("", web::put().to(route_post::<EditComment>))
+ .route("/like", web::post().to(route_post::<CreateCommentLike>))
+ .route("/save", web::put().to(route_post::<SaveComment>)),
+ )
+ // User
+ .service(
+ // Account action, I don't like that it's in /user maybe /accounts
+ // Handle /user/register separately to add the register() rate limitter
+ web::resource("/user/register")
+ .guard(guard::Post())
+ .wrap(rate_limit.register())
+ .route(web::post().to(route_post::<Register>)),
+ )
+ // User actions
+ .service(
+ web::scope("/user")
+ .wrap(rate_limit.message())
+ .route("", web::get().to(route_get::<GetUserDetails>))
+ .route("/mention", web::get().to(route_get::<GetUserMentions>))
+ .route("/mention", web::put().to(route_post::<EditUserMention>))
+ .route("/replies", web::get().to(route_get::<GetReplies>))
+ .route("/followed_communities", web::get().to(route_get::<GetFollowedCommunities>))
+ // Admin action. I don't like that it's in /user
+ .route("/ban", web::post().to(route_post::<BanUser>))
+ // Account actions. I don't like that they're in /user maybe /accounts
+ .route("/login", web::post().to(route_post::<Login>))
+ .route("/delete_account", web::post().to(route_post::<DeleteAccount>))
+ .route("/password_reset", web::post().to(route_post::<PasswordReset>))
+ .route("/password_change", web::post().to(route_post::<PasswordChange>))
+ // mark_all_as_read feels off being in this section as well
+ .route("/mark_all_as_read", web::post().to(route_post::<MarkAllAsRead>))
+ .route("/save_user_settings", web::put().to(route_post::<SaveUserSettings>)),
+ )
+ // Admin Actions
+ .service(
+ web::resource("/admin/add")
+ .wrap(rate_limit.message())
+ .route(web::post().to(route_post::<AddAdmin>)),
+ ),
+ );
}
-fn perform<Request, Response>(
+fn perform<Request>(
data: Request,
db: DbPoolParam,
- rate_limit_param: RateLimitParam,
chat_server: ChatServerParam,
- req: HttpRequest,
) -> Result<HttpResponse, Error>
where
- Response: Serialize,
- Oper<Request>: Perform<Response>,
+ Oper<Request>: Perform,
{
let ws_info = WebsocketInfo {
chatserver: chat_server.get_ref().to_owned(),
id: None,
};
- let rate_limit_info = RateLimitInfo {
- rate_limiter: rate_limit_param.get_ref().to_owned(),
- ip: get_ip(&req),
- };
-
let oper: Oper<Request> = Oper::new(data);
- let res = oper.perform(
- db.get_ref().to_owned(),
- Some(ws_info),
- Some(rate_limit_info),
- );
+ let res = oper.perform(db.get_ref().to_owned(), Some(ws_info));
Ok(HttpResponse::Ok().json(res?))
}
-async fn route_get<Data, Response>(
+async fn route_get<Data>(
data: web::Query<Data>,
db: DbPoolParam,
- rate_limit_param: RateLimitParam,
chat_server: ChatServerParam,
- req: HttpRequest,
) -> Result<HttpResponse, Error>
where
Data: Serialize,
- Response: Serialize,
- Oper<Data>: Perform<Response>,
+ Oper<Data>: Perform,
{
- perform::<Data, Response>(data.0, db, rate_limit_param, chat_server, req)
+ perform::<Data>(data.0, db, chat_server)
}
-async fn route_post<Data, Response>(
+async fn route_post<Data>(
data: web::Json<Data>,
db: DbPoolParam,
- rate_limit_param: RateLimitParam,
chat_server: ChatServerParam,
- req: HttpRequest,
) -> Result<HttpResponse, Error>
where
Data: Serialize,
- Response: Serialize,
- Oper<Data>: Perform<Response>,
+ Oper<Data>: Perform,
{
- perform::<Data, Response>(data.0, db, rate_limit_param, chat_server, req)
+ perform::<Data>(data.0, db, chat_server)
}
use crate::api::{Oper, Perform};
use crate::db::site_view::SiteView;
-use crate::rate_limit::{rate_limiter::RateLimiter, RateLimitInfo};
+use crate::rate_limit::rate_limiter::RateLimiter;
use crate::websocket::{server::ChatServer, WebsocketInfo};
use crate::{get_ip, markdown_to_html, version, Settings};
use actix::prelude::*;
.into_actor(self)
.then(|res, _, ctx| {
match res {
- Ok(res) => ctx.text(res),
- Err(e) => {
- error!("{}", &e);
- }
+ Ok(Ok(res)) => ctx.text(res),
+ Ok(Err(e)) => error!("{}", e),
+ Err(e) => error!("{}", &e),
}
actix::fut::ready(())
})
use server::ChatServer;
use std::collections::{HashMap, HashSet};
use std::str::FromStr;
-use std::sync::Arc;
-use std::sync::Mutex;
#[derive(EnumString, ToString, Debug, Clone)]
pub enum UserOperation {
use crate::api::site::*;
use crate::api::user::*;
use crate::api::*;
-use crate::rate_limit::{rate_limiter::RateLimiter, RateLimitInfo};
+use crate::rate_limit::RateLimit;
use crate::websocket::UserOperation;
use crate::{CommunityId, ConnectionId, IPAddr, PostId, UserId};
/// The messages sent to websocket clients
#[derive(Serialize, Deserialize, Message)]
-#[rtype(String)]
+#[rtype(result = "Result<String, failure::Error>")]
pub struct StandardMessage {
/// Id of the client session
pub id: ConnectionId,
pool: Pool<ConnectionManager<PgConnection>>,
/// Rate limiting based on rate type and IP addr
- rate_limiter: Arc<Mutex<RateLimiter>>,
+ rate_limiter: RateLimit,
}
impl ChatServer {
pub fn startup(
pool: Pool<ConnectionManager<PgConnection>>,
- rate_limiter: Arc<Mutex<RateLimiter>>,
+ rate_limiter: RateLimit,
) -> ChatServer {
ChatServer {
sessions: HashMap::new(),
}
}
- fn do_user_operation<'a, Data, Response>(
- &self,
- id: ConnectionId,
- ip: IPAddr,
- op: UserOperation,
- data: &str,
- ctx: &mut Context<Self>,
- ) -> Result<String, Error>
- where
- for<'de> Data: Deserialize<'de> + 'a,
- Response: Serialize,
- Oper<Data>: Perform<Response>,
- {
- let parsed_data: Data = serde_json::from_str(data)?;
-
- let ws_info = WebsocketInfo {
- chatserver: ctx.address(),
- id: Some(id),
- };
-
- let rate_limit_info = RateLimitInfo {
- rate_limiter: self.rate_limiter.clone(),
- ip,
- };
-
- let new_pool = self.pool.clone();
- let res = Oper::new(parsed_data).perform(new_pool, Some(ws_info), Some(rate_limit_info))?;
- to_json_string(&op, &res)
- }
-
fn parse_json_message(
&mut self,
msg: StandardMessage,
ctx: &mut Context<Self>,
- ) -> Result<String, Error> {
- let json: Value = serde_json::from_str(&msg.msg)?;
- let data = &json["data"].to_string();
- let op = &json["op"].as_str().ok_or(APIError {
- message: "Unknown op type".to_string(),
- })?;
-
- let user_operation: UserOperation = UserOperation::from_str(&op)?;
+ ) -> impl Future<Output = Result<String, Error>> {
+ let addr = ctx.address();
+ let pool = self.pool.clone();
+ let rate_limiter = self.rate_limiter.clone();
let ip: IPAddr = match self.sessions.get(&msg.id) {
Some(info) => info.ip.to_owned(),
None => "blank_ip".to_string(),
};
- match user_operation {
- // User ops
- UserOperation::Login => {
- self.do_user_operation::<Login, LoginResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::Register => {
- self.do_user_operation::<Register, LoginResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::GetUserDetails => self
- .do_user_operation::<GetUserDetails, GetUserDetailsResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::GetReplies => self.do_user_operation::<GetReplies, GetRepliesResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::AddAdmin => {
- self.do_user_operation::<AddAdmin, AddAdminResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::BanUser => {
- self.do_user_operation::<BanUser, BanUserResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::GetUserMentions => self
- .do_user_operation::<GetUserMentions, GetUserMentionsResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::EditUserMention => self
- .do_user_operation::<EditUserMention, UserMentionResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::MarkAllAsRead => self.do_user_operation::<MarkAllAsRead, GetRepliesResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::DeleteAccount => self.do_user_operation::<DeleteAccount, LoginResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::PasswordReset => self
- .do_user_operation::<PasswordReset, PasswordResetResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::PasswordChange => self.do_user_operation::<PasswordChange, LoginResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::CreatePrivateMessage => self
- .do_user_operation::<CreatePrivateMessage, PrivateMessageResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::EditPrivateMessage => self
- .do_user_operation::<EditPrivateMessage, PrivateMessageResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::GetPrivateMessages => self
- .do_user_operation::<GetPrivateMessages, PrivateMessagesResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::UserJoin => {
- self.do_user_operation::<UserJoin, UserJoinResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::SaveUserSettings => self.do_user_operation::<SaveUserSettings, LoginResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
-
- // Site ops
- UserOperation::GetModlog => self.do_user_operation::<GetModlog, GetModlogResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::CreateSite => {
- self.do_user_operation::<CreateSite, SiteResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::EditSite => {
- self.do_user_operation::<EditSite, SiteResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::GetSite => {
- self.do_user_operation::<GetSite, GetSiteResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::GetSiteConfig => self
- .do_user_operation::<GetSiteConfig, GetSiteConfigResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::SaveSiteConfig => self
- .do_user_operation::<SaveSiteConfig, GetSiteConfigResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::Search => {
- self.do_user_operation::<Search, SearchResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::TransferCommunity => self
- .do_user_operation::<TransferCommunity, GetCommunityResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::TransferSite => self.do_user_operation::<TransferSite, GetSiteResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::ListCategories => self
- .do_user_operation::<ListCategories, ListCategoriesResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
-
- // Community ops
- UserOperation::GetCommunity => self.do_user_operation::<GetCommunity, GetCommunityResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::ListCommunities => self
- .do_user_operation::<ListCommunities, ListCommunitiesResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::CreateCommunity => self
- .do_user_operation::<CreateCommunity, CommunityResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::EditCommunity => self.do_user_operation::<EditCommunity, CommunityResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::FollowCommunity => self
- .do_user_operation::<FollowCommunity, CommunityResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::GetFollowedCommunities => self
- .do_user_operation::<GetFollowedCommunities, GetFollowedCommunitiesResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::BanFromCommunity => self
- .do_user_operation::<BanFromCommunity, BanFromCommunityResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::AddModToCommunity => self
- .do_user_operation::<AddModToCommunity, AddModToCommunityResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
-
- // Post ops
- UserOperation::CreatePost => {
- self.do_user_operation::<CreatePost, PostResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::GetPost => {
- self.do_user_operation::<GetPost, GetPostResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::GetPosts => {
- self.do_user_operation::<GetPosts, GetPostsResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::EditPost => {
- self.do_user_operation::<EditPost, PostResponse>(msg.id, ip, user_operation, data, ctx)
- }
- UserOperation::CreatePostLike => self.do_user_operation::<CreatePostLike, PostResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::SavePost => {
- self.do_user_operation::<SavePost, PostResponse>(msg.id, ip, user_operation, data, ctx)
- }
+ async move {
+ let msg = msg;
+ let json: Value = serde_json::from_str(&msg.msg)?;
+ let data = &json["data"].to_string();
+ let op = &json["op"].as_str().ok_or(APIError {
+ message: "Unknown op type".to_string(),
+ })?;
+
+ let user_operation: UserOperation = UserOperation::from_str(&op)?;
+
+ match user_operation {
+ // User ops
+ UserOperation::Login => {
+ do_user_operation::<Login>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::Register => {
+ do_user_operation::<Register>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::GetUserDetails => {
+ do_user_operation::<GetUserDetails>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::GetReplies => {
+ do_user_operation::<GetReplies>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::AddAdmin => {
+ do_user_operation::<AddAdmin>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::BanUser => {
+ do_user_operation::<BanUser>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::GetUserMentions => {
+ do_user_operation::<GetUserMentions>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::EditUserMention => {
+ do_user_operation::<EditUserMention>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::MarkAllAsRead => {
+ do_user_operation::<MarkAllAsRead>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::DeleteAccount => {
+ do_user_operation::<DeleteAccount>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::PasswordReset => {
+ do_user_operation::<PasswordReset>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::PasswordChange => {
+ do_user_operation::<PasswordChange>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::CreatePrivateMessage => {
+ do_user_operation::<CreatePrivateMessage>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::EditPrivateMessage => {
+ do_user_operation::<EditPrivateMessage>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::GetPrivateMessages => {
+ do_user_operation::<GetPrivateMessages>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::UserJoin => {
+ do_user_operation::<UserJoin>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::SaveUserSettings => {
+ do_user_operation::<SaveUserSettings>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
- // Comment ops
- UserOperation::CreateComment => self.do_user_operation::<CreateComment, CommentResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::EditComment => self.do_user_operation::<EditComment, CommentResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::SaveComment => self.do_user_operation::<SaveComment, CommentResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::GetComments => self.do_user_operation::<GetComments, GetCommentsResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
- UserOperation::CreateCommentLike => self
- .do_user_operation::<CreateCommentLike, CommentResponse>(
- msg.id,
- ip,
- user_operation,
- data,
- ctx,
- ),
+ // Site ops
+ UserOperation::GetModlog => {
+ do_user_operation::<GetModlog>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::CreateSite => {
+ do_user_operation::<CreateSite>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::EditSite => {
+ do_user_operation::<EditSite>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::GetSite => {
+ do_user_operation::<GetSite>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::GetSiteConfig => {
+ do_user_operation::<GetSiteConfig>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::SaveSiteConfig => {
+ do_user_operation::<SaveSiteConfig>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::Search => {
+ do_user_operation::<Search>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::TransferCommunity => {
+ do_user_operation::<TransferCommunity>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::TransferSite => {
+ do_user_operation::<TransferSite>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::ListCategories => {
+ do_user_operation::<ListCategories>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+
+ // Community ops
+ UserOperation::GetCommunity => {
+ do_user_operation::<GetCommunity>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::ListCommunities => {
+ do_user_operation::<ListCommunities>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::CreateCommunity => {
+ do_user_operation::<CreateCommunity>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::EditCommunity => {
+ do_user_operation::<EditCommunity>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::FollowCommunity => {
+ do_user_operation::<FollowCommunity>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::GetFollowedCommunities => {
+ do_user_operation::<GetFollowedCommunities>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::BanFromCommunity => {
+ do_user_operation::<BanFromCommunity>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::AddModToCommunity => {
+ do_user_operation::<AddModToCommunity>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+
+ // Post ops
+ UserOperation::CreatePost => {
+ do_user_operation::<CreatePost>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::GetPost => {
+ do_user_operation::<GetPost>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::GetPosts => {
+ do_user_operation::<GetPosts>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::EditPost => {
+ do_user_operation::<EditPost>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+ UserOperation::CreatePostLike => {
+ do_user_operation::<CreatePostLike>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::SavePost => {
+ do_user_operation::<SavePost>(pool, rate_limiter, addr, msg.id, ip, user_operation, data)
+ .await
+ }
+
+ // Comment ops
+ UserOperation::CreateComment => {
+ do_user_operation::<CreateComment>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::EditComment => {
+ do_user_operation::<EditComment>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::SaveComment => {
+ do_user_operation::<SaveComment>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::GetComments => {
+ do_user_operation::<GetComments>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ UserOperation::CreateCommentLike => {
+ do_user_operation::<CreateCommentLike>(
+ pool,
+ rate_limiter,
+ addr,
+ msg.id,
+ ip,
+ user_operation,
+ data,
+ )
+ .await
+ }
+ }
}
}
}
+async fn do_user_operation<'a, Data>(
+ pool: Pool<ConnectionManager<PgConnection>>,
+ rate_limiter: RateLimit,
+ chatserver: Addr<ChatServer>,
+ id: ConnectionId,
+ ip: IPAddr,
+ op: UserOperation,
+ data: &str,
+) -> Result<String, Error>
+where
+ for<'de> Data: Deserialize<'de> + 'a,
+ Oper<Data>: Perform,
+{
+ let ws_info = WebsocketInfo {
+ chatserver,
+ id: Some(id),
+ };
+
+ let data = data.to_string();
+ let op2 = op.clone();
+ let fut = async move {
+ let parsed_data: Data = serde_json::from_str(&data)?;
+ let res = Oper::new(parsed_data).perform(pool, Some(ws_info))?;
+ to_json_string(&op, &res)
+ };
+
+ match op2 {
+ UserOperation::Register => rate_limiter.register().wrap(ip, fut).await,
+ UserOperation::CreatePost => rate_limiter.post().wrap(ip, fut).await,
+ _ => rate_limiter.message().wrap(ip, fut).await,
+ }
+}
+
/// Make actor from `ChatServer`
impl Actor for ChatServer {
/// We are going to use simple Context, we just need ability to communicate
/// Handler for Message message.
impl Handler<StandardMessage> for ChatServer {
- type Result = MessageResult<StandardMessage>;
+ type Result = ResponseFuture<Result<String, failure::Error>>;
fn handle(&mut self, msg: StandardMessage, ctx: &mut Context<Self>) -> Self::Result {
- match self.parse_json_message(msg, ctx) {
- Ok(m) => {
- info!("Message Sent: {}", m);
- MessageResult(m)
- }
- Err(e) => {
- error!("Error during message handling {}", e);
- MessageResult(e.to_string())
+ let fut = self.parse_json_message(msg, ctx);
+ Box::pin(async move {
+ match fut.await {
+ Ok(m) => {
+ info!("Message Sent: {}", m);
+ Ok(m)
+ }
+ Err(e) => {
+ error!("Error during message handling {}", e);
+ Ok(e.to_string())
+ }
}
- }
+ })
}
}