Compare commits

...

1 commit

Author SHA1 Message Date
ef25414aff First sync try, not working. 2020-04-16 13:11:33 -04:00
19 changed files with 602 additions and 348 deletions

View file

@ -65,17 +65,21 @@ pub struct GetCommentsResponse {
}
impl Perform<CommentResponse> for Oper<CreateComment> {
fn perform(&self, conn: &PgConnection) -> Result<CommentResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<CommentResponse, Error> {
let data: &CreateComment = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
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.hostname);
// Check for a community ban
let post = Post::read(&conn, data.post_id)?;
@ -137,14 +141,14 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
if let Some(mention_email) = mention_user.email {
let subject = &format!(
"{} - Mentioned by {}",
Settings::get().hostname,
settings.hostname,
claims.username
);
let html = &format!(
"<h1>User Mention</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
claims.username, comment_form.content, hostname
);
match send_email(subject, &mention_email, &mention_user.name, html) {
match send_email(subject, &mention_email, &mention_user.name, html, settings) {
Ok(_o) => _o,
Err(e) => error!("{}", e),
};
@ -166,14 +170,14 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
if let Some(comment_reply_email) = parent_user.email {
let subject = &format!(
"{} - Reply from {}",
Settings::get().hostname,
settings.hostname,
claims.username
);
let html = &format!(
"<h1>Comment Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
claims.username, comment_form.content, hostname
);
match send_email(subject, &comment_reply_email, &parent_user.name, html) {
match send_email(subject, &comment_reply_email, &parent_user.name, html, settings) {
Ok(_o) => _o,
Err(e) => error!("{}", e),
};
@ -191,14 +195,14 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
if let Some(post_reply_email) = parent_user.email {
let subject = &format!(
"{} - Reply from {}",
Settings::get().hostname,
settings.hostname,
claims.username
);
let html = &format!(
"<h1>Post Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
claims.username, comment_form.content, hostname
);
match send_email(subject, &post_reply_email, &parent_user.name, html) {
match send_email(subject, &post_reply_email, &parent_user.name, html, settings) {
Ok(_o) => _o,
Err(e) => error!("{}", e),
};
@ -231,10 +235,14 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
}
impl Perform<CommentResponse> for Oper<EditComment> {
fn perform(&self, conn: &PgConnection) -> Result<CommentResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<CommentResponse, Error> {
let data: &EditComment = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -361,10 +369,14 @@ impl Perform<CommentResponse> for Oper<EditComment> {
}
impl Perform<CommentResponse> for Oper<SaveComment> {
fn perform(&self, conn: &PgConnection) -> Result<CommentResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<CommentResponse, Error> {
let data: &SaveComment = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -398,10 +410,14 @@ impl Perform<CommentResponse> for Oper<SaveComment> {
}
impl Perform<CommentResponse> for Oper<CreateCommentLike> {
fn perform(&self, conn: &PgConnection) -> Result<CommentResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<CommentResponse, Error> {
let data: &CreateCommentLike = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -475,11 +491,15 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
}
impl Perform<GetCommentsResponse> for Oper<GetComments> {
fn perform(&self, conn: &PgConnection) -> Result<GetCommentsResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetCommentsResponse, Error> {
let data: &GetComments = &self.data;
let user_claims: Option<Claims> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Some(auth) => match Claims::decode(&auth, &settings.jwt_secret) {
Ok(claims) => Some(claims.claims),
Err(_e) => None,
},

View file

@ -114,11 +114,15 @@ pub struct TransferCommunity {
}
impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
fn perform(&self, conn: &PgConnection) -> Result<GetCommunityResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetCommunityResponse, Error> {
let data: &GetCommunity = &self.data;
let user_id: Option<i32> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Some(auth) => match Claims::decode(&auth, &settings.jwt_secret) {
Ok(claims) => {
let user_id = claims.claims.id;
Some(user_id)
@ -168,10 +172,14 @@ impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
}
impl Perform<CommunityResponse> for Oper<CreateCommunity> {
fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<CommunityResponse, Error> {
let data: &CreateCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -246,7 +254,11 @@ impl Perform<CommunityResponse> for Oper<CreateCommunity> {
}
impl Perform<CommunityResponse> for Oper<EditCommunity> {
fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<CommunityResponse, Error> {
let data: &EditCommunity = &self.data;
if let Err(slurs) = slur_check(&data.name) {
@ -263,7 +275,7 @@ impl Perform<CommunityResponse> for Oper<EditCommunity> {
}
}
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -330,11 +342,15 @@ impl Perform<CommunityResponse> for Oper<EditCommunity> {
}
impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
fn perform(&self, conn: &PgConnection) -> Result<ListCommunitiesResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<ListCommunitiesResponse, Error> {
let data: &ListCommunities = &self.data;
let user_claims: Option<Claims> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Some(auth) => match Claims::decode(&auth, &settings.jwt_secret) {
Ok(claims) => Some(claims.claims),
Err(_e) => None,
},
@ -367,10 +383,14 @@ impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
}
impl Perform<CommunityResponse> for Oper<FollowCommunity> {
fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<CommunityResponse, Error> {
let data: &FollowCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -403,10 +423,14 @@ impl Perform<CommunityResponse> for Oper<FollowCommunity> {
}
impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
fn perform(&self, conn: &PgConnection) -> Result<GetFollowedCommunitiesResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetFollowedCommunitiesResponse, Error> {
let data: &GetFollowedCommunities = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -425,10 +449,14 @@ impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
}
impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
fn perform(&self, conn: &PgConnection) -> Result<BanFromCommunityResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<BanFromCommunityResponse, Error> {
let data: &BanFromCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -478,10 +506,14 @@ impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
}
impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
fn perform(&self, conn: &PgConnection) -> Result<AddModToCommunityResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<AddModToCommunityResponse, Error> {
let data: &AddModToCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -521,10 +553,14 @@ impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
}
impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
fn perform(&self, conn: &PgConnection) -> Result<GetCommunityResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetCommunityResponse, Error> {
let data: &TransferCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};

View file

@ -17,6 +17,7 @@ use crate::db::user_mention::*;
use crate::db::user_mention_view::*;
use crate::db::user_view::*;
use crate::db::*;
use crate::Settings;
use crate::{
extract_usernames, fetch_iframely_and_pictshare_data, naive_from_unix, naive_now, remove_slurs,
slur_check, slurs_vec_to_str,
@ -56,7 +57,7 @@ impl<T> Oper<T> {
}
pub trait Perform<T> {
fn perform(&self, conn: &PgConnection) -> Result<T, Error>
fn perform(&self, conn: &PgConnection, settings: &Settings) -> Result<T, Error>
where
T: Sized;
}

View file

@ -80,10 +80,14 @@ pub struct SavePost {
}
impl Perform<PostResponse> for Oper<CreatePost> {
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<PostResponse, Error> {
let data: &CreatePost = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -169,11 +173,15 @@ impl Perform<PostResponse> for Oper<CreatePost> {
}
impl Perform<GetPostResponse> for Oper<GetPost> {
fn perform(&self, conn: &PgConnection) -> Result<GetPostResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetPostResponse, Error> {
let data: &GetPost = &self.data;
let user_id: Option<i32> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Some(auth) => match Claims::decode(&auth, &settings.jwt_secret) {
Ok(claims) => {
let user_id = claims.claims.id;
Some(user_id)
@ -217,11 +225,15 @@ impl Perform<GetPostResponse> for Oper<GetPost> {
}
impl Perform<GetPostsResponse> for Oper<GetPosts> {
fn perform(&self, conn: &PgConnection) -> Result<GetPostsResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetPostsResponse, Error> {
let data: &GetPosts = &self.data;
let user_claims: Option<Claims> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Some(auth) => match Claims::decode(&auth, &settings.jwt_secret) {
Ok(claims) => Some(claims.claims),
Err(_e) => None,
},
@ -260,10 +272,14 @@ impl Perform<GetPostsResponse> for Oper<GetPosts> {
}
impl Perform<PostResponse> for Oper<CreatePostLike> {
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<PostResponse, Error> {
let data: &CreatePostLike = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -318,7 +334,11 @@ impl Perform<PostResponse> for Oper<CreatePostLike> {
}
impl Perform<PostResponse> for Oper<EditPost> {
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<PostResponse, Error> {
let data: &EditPost = &self.data;
if let Err(slurs) = slur_check(&data.name) {
@ -331,7 +351,7 @@ impl Perform<PostResponse> for Oper<EditPost> {
}
}
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -432,10 +452,14 @@ impl Perform<PostResponse> for Oper<EditPost> {
}
impl Perform<PostResponse> for Oper<SavePost> {
fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<PostResponse, Error> {
let data: &SavePost = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};

View file

@ -114,7 +114,11 @@ pub struct SaveSiteConfig {
}
impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
fn perform(&self, conn: &PgConnection) -> Result<ListCategoriesResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<ListCategoriesResponse, Error> {
let _data: &ListCategories = &self.data;
let categories: Vec<Category> = Category::list_all(&conn)?;
@ -125,7 +129,11 @@ impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
}
impl Perform<GetModlogResponse> for Oper<GetModlog> {
fn perform(&self, conn: &PgConnection) -> Result<GetModlogResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetModlogResponse, Error> {
let data: &GetModlog = &self.data;
let removed_posts = ModRemovePostView::list(
@ -198,10 +206,14 @@ impl Perform<GetModlogResponse> for Oper<GetModlog> {
}
impl Perform<SiteResponse> for Oper<CreateSite> {
fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<SiteResponse, Error> {
let data: &CreateSite = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -245,10 +257,14 @@ impl Perform<SiteResponse> for Oper<CreateSite> {
}
impl Perform<SiteResponse> for Oper<EditSite> {
fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<SiteResponse, Error> {
let data: &EditSite = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -294,13 +310,17 @@ impl Perform<SiteResponse> for Oper<EditSite> {
}
impl Perform<GetSiteResponse> for Oper<GetSite> {
fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetSiteResponse, Error> {
let _data: &GetSite = &self.data;
let site = Site::read(&conn, 1);
let site_view = if site.is_ok() {
Some(SiteView::read(&conn)?)
} else if let Some(setup) = Settings::get().setup.as_ref() {
} else if let Some(setup) = &settings.setup {
let register = Register {
username: setup.admin_username.to_owned(),
email: setup.admin_email.to_owned(),
@ -309,7 +329,7 @@ impl Perform<GetSiteResponse> for Oper<GetSite> {
admin: true,
show_nsfw: true,
};
let login_response = Oper::new(register).perform(&conn)?;
let login_response = Oper::new(register).perform(&conn, &settings)?;
info!("Admin {} created", setup.admin_username);
let create_site = CreateSite {
@ -320,7 +340,7 @@ impl Perform<GetSiteResponse> for Oper<GetSite> {
enable_nsfw: false,
auth: login_response.jwt,
};
Oper::new(create_site).perform(&conn)?;
Oper::new(create_site).perform(&conn, &settings)?;
info!("Site {} created", setup.site_name);
Some(SiteView::read(&conn)?)
} else {
@ -347,11 +367,15 @@ impl Perform<GetSiteResponse> for Oper<GetSite> {
}
impl Perform<SearchResponse> for Oper<Search> {
fn perform(&self, conn: &PgConnection) -> Result<SearchResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<SearchResponse, Error> {
let data: &Search = &self.data;
let user_id: Option<i32> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Some(auth) => match Claims::decode(&auth, &settings.jwt_secret) {
Ok(claims) => {
let user_id = claims.claims.id;
Some(user_id)
@ -465,10 +489,14 @@ impl Perform<SearchResponse> for Oper<Search> {
}
impl Perform<GetSiteResponse> for Oper<TransferSite> {
fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetSiteResponse, Error> {
let data: &TransferSite = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -528,10 +556,14 @@ impl Perform<GetSiteResponse> for Oper<TransferSite> {
}
impl Perform<GetSiteConfigResponse> for Oper<GetSiteConfig> {
fn perform(&self, conn: &PgConnection) -> Result<GetSiteConfigResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetSiteConfigResponse, Error> {
let data: &GetSiteConfig = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -553,10 +585,14 @@ impl Perform<GetSiteConfigResponse> for Oper<GetSiteConfig> {
}
impl Perform<GetSiteConfigResponse> for Oper<SaveSiteConfig> {
fn perform(&self, conn: &PgConnection) -> Result<GetSiteConfigResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetSiteConfigResponse, Error> {
let data: &SaveSiteConfig = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};

View file

@ -205,7 +205,11 @@ pub struct UserJoinResponse {
}
impl Perform<LoginResponse> for Oper<Login> {
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<LoginResponse, Error> {
let data: &Login = &self.data;
// Fetch that username / email
@ -221,12 +225,16 @@ impl Perform<LoginResponse> for Oper<Login> {
}
// Return the jwt
Ok(LoginResponse { jwt: user.jwt() })
Ok(LoginResponse { jwt: user.jwt(&settings.jwt_secret) })
}
}
impl Perform<LoginResponse> for Oper<Register> {
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<LoginResponse, Error> {
let data: &Register = &self.data;
// Make sure site has open registration
@ -253,7 +261,7 @@ impl Perform<LoginResponse> for Oper<Register> {
// Register the new user
let user_form = UserForm {
name: data.username.to_owned(),
fedi_name: Settings::get().hostname,
fedi_name: settings.hostname.to_owned(),
email: data.email.to_owned(),
matrix_user_id: None,
avatar: None,
@ -334,16 +342,20 @@ impl Perform<LoginResponse> for Oper<Register> {
// Return the jwt
Ok(LoginResponse {
jwt: inserted_user.jwt(),
jwt: inserted_user.jwt(&settings.jwt_secret),
})
}
}
impl Perform<LoginResponse> for Oper<SaveUserSettings> {
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<LoginResponse, Error> {
let data: &SaveUserSettings = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -422,17 +434,21 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
// Return the jwt
Ok(LoginResponse {
jwt: updated_user.jwt(),
jwt: updated_user.jwt(&settings.jwt_secret),
})
}
}
impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
fn perform(&self, conn: &PgConnection) -> Result<GetUserDetailsResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetUserDetailsResponse, Error> {
let data: &GetUserDetails = &self.data;
let user_claims: Option<Claims> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Some(auth) => match Claims::decode(&auth, &settings.jwt_secret) {
Ok(claims) => Some(claims.claims),
Err(_e) => None,
},
@ -525,10 +541,14 @@ impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
}
impl Perform<AddAdminResponse> for Oper<AddAdmin> {
fn perform(&self, conn: &PgConnection) -> Result<AddAdminResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<AddAdminResponse, Error> {
let data: &AddAdmin = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -588,10 +608,14 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
}
impl Perform<BanUserResponse> for Oper<BanUser> {
fn perform(&self, conn: &PgConnection) -> Result<BanUserResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<BanUserResponse, Error> {
let data: &BanUser = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -657,10 +681,14 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
}
impl Perform<GetRepliesResponse> for Oper<GetReplies> {
fn perform(&self, conn: &PgConnection) -> Result<GetRepliesResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetRepliesResponse, Error> {
let data: &GetReplies = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -681,10 +709,14 @@ impl Perform<GetRepliesResponse> for Oper<GetReplies> {
}
impl Perform<GetUserMentionsResponse> for Oper<GetUserMentions> {
fn perform(&self, conn: &PgConnection) -> Result<GetUserMentionsResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetUserMentionsResponse, Error> {
let data: &GetUserMentions = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -705,10 +737,14 @@ impl Perform<GetUserMentionsResponse> for Oper<GetUserMentions> {
}
impl Perform<UserMentionResponse> for Oper<EditUserMention> {
fn perform(&self, conn: &PgConnection) -> Result<UserMentionResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<UserMentionResponse, Error> {
let data: &EditUserMention = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -738,10 +774,14 @@ impl Perform<UserMentionResponse> for Oper<EditUserMention> {
}
impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
fn perform(&self, conn: &PgConnection) -> Result<GetRepliesResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<GetRepliesResponse, Error> {
let data: &MarkAllAsRead = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -822,10 +862,14 @@ impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
}
impl Perform<LoginResponse> for Oper<DeleteAccount> {
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<LoginResponse, Error> {
let data: &DeleteAccount = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -903,7 +947,11 @@ impl Perform<LoginResponse> for Oper<DeleteAccount> {
}
impl Perform<PasswordResetResponse> for Oper<PasswordReset> {
fn perform(&self, conn: &PgConnection) -> Result<PasswordResetResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<PasswordResetResponse, Error> {
let data: &PasswordReset = &self.data;
// Fetch that email
@ -922,9 +970,9 @@ impl Perform<PasswordResetResponse> for Oper<PasswordReset> {
// TODO no i18n support here.
let user_email = &user.email.expect("email");
let subject = &format!("Password reset for {}", user.name);
let hostname = &format!("https://{}", Settings::get().hostname); //TODO add https for now.
let hostname = &format!("https://{}", &settings.hostname); //TODO add https for now.
let html = &format!("<h1>Password Reset Request for {}</h1><br><a href={}/password_change/{}>Click here to reset your password</a>", user.name, hostname, &token);
match send_email(subject, user_email, &user.name, html) {
match send_email(subject, user_email, &user.name, html, &settings) {
Ok(_o) => _o,
Err(_e) => return Err(APIError::err(&_e).into()),
};
@ -934,7 +982,11 @@ impl Perform<PasswordResetResponse> for Oper<PasswordReset> {
}
impl Perform<LoginResponse> for Oper<PasswordChange> {
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<LoginResponse, Error> {
let data: &PasswordChange = &self.data;
// Fetch the user_id from the token
@ -953,23 +1005,27 @@ impl Perform<LoginResponse> for Oper<PasswordChange> {
// Return the jwt
Ok(LoginResponse {
jwt: updated_user.jwt(),
jwt: updated_user.jwt(&settings.jwt_secret),
})
}
}
impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
fn perform(&self, conn: &PgConnection) -> Result<PrivateMessageResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<PrivateMessageResponse, Error> {
let data: &CreatePrivateMessage = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
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.hostname);
// Check for a site ban
if UserView::read(&conn, user_id)?.banned {
@ -1000,14 +1056,14 @@ impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
if let Some(email) = recipient_user.email {
let subject = &format!(
"{} - Private Message from {}",
Settings::get().hostname,
&settings.hostname,
claims.username
);
let html = &format!(
"<h1>Private Message</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
claims.username, &content_slurs_removed, hostname
);
match send_email(subject, &email, &recipient_user.name, html) {
match send_email(subject, &email, &recipient_user.name, html, &settings) {
Ok(_o) => _o,
Err(e) => error!("{}", e),
};
@ -1021,10 +1077,14 @@ impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
}
impl Perform<PrivateMessageResponse> for Oper<EditPrivateMessage> {
fn perform(&self, conn: &PgConnection) -> Result<PrivateMessageResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<PrivateMessageResponse, Error> {
let data: &EditPrivateMessage = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -1076,10 +1136,14 @@ impl Perform<PrivateMessageResponse> for Oper<EditPrivateMessage> {
}
impl Perform<PrivateMessagesResponse> for Oper<GetPrivateMessages> {
fn perform(&self, conn: &PgConnection) -> Result<PrivateMessagesResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<PrivateMessagesResponse, Error> {
let data: &GetPrivateMessages = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
@ -1097,10 +1161,14 @@ impl Perform<PrivateMessagesResponse> for Oper<GetPrivateMessages> {
}
impl Perform<UserJoinResponse> for Oper<UserJoin> {
fn perform(&self, _conn: &PgConnection) -> Result<UserJoinResponse, Error> {
fn perform(
&self,
conn: &PgConnection,
settings: &Settings,
) -> Result<UserJoinResponse, Error> {
let data: &UserJoin = &self.data;
let claims = match Claims::decode(&data.auth) {
let claims = match Claims::decode(&data.auth, &settings.jwt_secret) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};

View file

@ -91,7 +91,7 @@ mod tests {
let page = post.as_page();
assert_eq!(
format!("https://{}/federation/post/62", Settings::get().hostname),
format!("https://{}/federation/post/62", "fake_host"),
page.object_props.id_string().unwrap()
);
}
@ -100,7 +100,7 @@ mod tests {
pub fn make_apub_endpoint<S: Display, T: Display>(point: S, value: T) -> String {
format!(
"https://{}/federation/{}/{}",
Settings::get().hostname,
"fake_host",
point,
value
)

View file

@ -69,8 +69,8 @@ impl Community {
.first::<Self>(conn)
}
pub fn get_url(&self) -> String {
format!("https://{}/c/{}", Settings::get().hostname, self.name)
pub fn get_url(&self, hostname: &str) -> String {
format!("https://{}/c/{}", hostname, self.name)
}
}

View file

@ -112,7 +112,7 @@ impl<T> MaybeOptional<T> for Option<T> {
}
pub fn establish_unpooled_connection() -> PgConnection {
let db_url = Settings::get().get_database_url();
let db_url = Settings::init().unwrap().get_database_url();
PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url))
}

View file

@ -110,14 +110,14 @@ pub struct Claims {
}
impl Claims {
pub fn decode(jwt: &str) -> Result<TokenData<Claims>, jsonwebtoken::errors::Error> {
pub fn decode(jwt: &str, jwt_secret: &str) -> Result<TokenData<Claims>, jsonwebtoken::errors::Error> {
let v = Validation {
validate_exp: false,
..Validation::default()
};
decode::<Claims>(
&jwt,
&DecodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
&DecodingKey::from_secret(jwt_secret.as_bytes()),
&v,
)
}
@ -125,7 +125,7 @@ impl Claims {
type Jwt = String;
impl User_ {
pub fn jwt(&self) -> Jwt {
pub fn jwt(&self, jwt_secret: &str) -> Jwt {
let my_claims = Claims {
id: self.id,
username: self.name.to_owned(),
@ -141,7 +141,7 @@ impl User_ {
encode(
&Header::default(),
&my_claims,
&EncodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
&EncodingKey::from_secret(jwt_secret.as_bytes()),
)
.unwrap()
}
@ -165,12 +165,12 @@ impl User_ {
}
}
pub fn get_profile_url(&self) -> String {
format!("https://{}/u/{}", Settings::get().hostname, self.name)
pub fn get_profile_url(&self, hostname: &str) -> String {
format!("https://{}/u/{}", hostname, self.name)
}
pub fn find_by_jwt(conn: &PgConnection, jwt: &str) -> Result<Self, Error> {
let claims: Claims = Claims::decode(&jwt).expect("Invalid token").claims;
pub fn find_by_jwt(conn: &PgConnection, jwt: &str, jwt_secret: &str) -> Result<Self, Error> {
let claims: Claims = Claims::decode(jwt, jwt_secret).expect("Invalid token").claims;
Self::read(&conn, claims.id)
}
}

View file

@ -111,8 +111,9 @@ pub fn send_email(
to_email: &str,
to_username: &str,
html: &str,
settings: &Settings,
) -> Result<(), String> {
let email_config = Settings::get().email.ok_or("no_email_setup")?;
let email_config = settings.email.to_owned().ok_or("no_email_setup")?;
let email = Email::builder()
.to((to_email, to_username))
@ -127,7 +128,7 @@ pub fn send_email(
} else {
SmtpClient::new(&email_config.smtp_server, ClientSecurity::None).unwrap()
}
.hello_name(ClientId::Domain(Settings::get().hostname))
.hello_name(ClientId::Domain(settings.hostname.to_owned()))
.smtp_utf8(true)
.authentication_mechanism(Mechanism::Plain)
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited);

View file

@ -2,10 +2,8 @@ extern crate lemmy_server;
#[macro_use]
extern crate diesel_migrations;
use actix::prelude::*;
use actix_web::*;
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
use actix::prelude::*;
use lemmy_server::routes::{api, federation, feeds, index, nodeinfo, webfinger, websocket};
use lemmy_server::settings::Settings;
use lemmy_server::websocket::server::*;
@ -16,21 +14,24 @@ embed_migrations!();
#[actix_rt::main]
async fn main() -> io::Result<()> {
env_logger::init();
let settings = Settings::get();
// Set up the r2d2 connection pool
let manager = ConnectionManager::<PgConnection>::new(&settings.get_database_url());
let pool = Pool::builder()
.max_size(settings.database.pool_size)
.build(manager)
.unwrap_or_else(|_| panic!("Error connecting to {}", settings.get_database_url()));
// Set up the Chatserver's shared state
let cs_state_web = web::Data::new(ChatSharedState::init());
let cs_state_web_two = cs_state_web.clone();
let server = SyncArbiter::start(2, move || ChatServer::init(cs_state_web.clone()));
let settings = cs_state_web_two.settings.lock().unwrap().to_owned();
// Run the migrations from code
let conn = pool.get().unwrap();
// let conn = pool.get().unwrap();
let conn = cs_state_web_two.pool.lock().unwrap().get().unwrap();
embedded_migrations::run(&conn).unwrap();
// Set up websocket server
let server = ChatServer::startup(pool.clone()).start();
// let server = ChatServer::init(cs_state).start();
// let cs_state_web_two = cs_state_web.clone();
println!(
"Starting http server at {}:{}",
@ -39,10 +40,11 @@ async fn main() -> io::Result<()> {
// Create Http server with websocket support
HttpServer::new(move || {
let settings = Settings::get();
let settings = cs_state_web_two.settings.lock().unwrap().to_owned();
App::new()
.wrap(middleware::Logger::default())
.data(pool.clone())
// .app_data(cs_state_web_two.clone())
.app_data(cs_state_web_two.clone())
.data(server.clone())
// The routes
.configure(api::config)
@ -59,7 +61,7 @@ async fn main() -> io::Result<()> {
))
.service(actix_files::Files::new(
"/docs",
settings.front_end_dir + "/documentation",
settings.front_end_dir.to_owned() + "/documentation",
))
})
.bind((settings.bind, settings.port))?

View file

@ -4,14 +4,13 @@ use crate::api::post::*;
use crate::api::site::*;
use crate::api::user::*;
use crate::api::{Oper, Perform};
use crate::websocket::server::ChatSharedState;
use actix_web::{web, HttpResponse};
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
use failure::Error;
use serde::Serialize;
type DbParam = web::Data<Pool<ConnectionManager<PgConnection>>>;
#[rustfmt::skip]
pub fn config(cfg: &mut web::ServiceConfig) {
cfg
@ -66,40 +65,46 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("/api/v1/user/save_user_settings", web::put().to(route_post::<SaveUserSettings, LoginResponse>));
}
fn perform<Request, Response>(data: Request, db: DbParam) -> Result<HttpResponse, Error>
fn perform<Request, Response>(
data: Request,
state: web::Data<ChatSharedState>,
) -> Result<HttpResponse, Error>
where
Response: Serialize,
Oper<Request>: Perform<Response>,
{
let conn = match db.get() {
Ok(c) => c,
Err(e) => return Err(format_err!("{}", e)),
};
// let conn = match db.get() {
// Ok(c) => c,
// Err(e) => return Err(format_err!("{}", e)),
// };
let conn = state.pool.lock().unwrap().get()?;
let settings = state.settings.lock().unwrap();
let oper: Oper<Request> = Oper::new(data);
let response = oper.perform(&conn);
let response = oper.perform(&conn, &settings);
Ok(HttpResponse::Ok().json(response?))
}
async fn route_get<Data, Response>(
data: web::Query<Data>,
db: DbParam,
state: web::Data<ChatSharedState>,
) -> Result<HttpResponse, Error>
where
Data: Serialize,
Response: Serialize,
Oper<Data>: Perform<Response>,
{
perform::<Data, Response>(data.0, db)
perform::<Data, Response>(data.0, state)
}
async fn route_post<Data, Response>(
data: web::Json<Data>,
db: DbParam,
state: web::Data<ChatSharedState>,
) -> Result<HttpResponse, Error>
where
Data: Serialize,
Response: Serialize,
Oper<Data>: Perform<Response>,
{
perform::<Data, Response>(data.0, db)
perform::<Data, Response>(data.0, state)
}

View file

@ -6,10 +6,10 @@ use crate::db::site_view::SiteView;
use crate::db::user::{Claims, User_};
use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView};
use crate::db::{ListingType, SortType};
use crate::{markdown_to_html, Settings};
use crate::websocket::server::ChatSharedState;
use crate::markdown_to_html;
use actix_web::{web, HttpResponse, Result};
use chrono::{DateTime, NaiveDateTime, Utc};
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
use failure::Error;
use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder};
@ -37,11 +37,12 @@ pub fn config(cfg: &mut web::ServiceConfig) {
async fn get_all_feed(
info: web::Query<Params>,
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
state: web::Data<ChatSharedState>,
) -> Result<HttpResponse, actix_web::Error> {
let res = web::block(move || {
let conn = db.get()?;
get_feed_all_data(&conn, &get_sort_type(info)?)
let conn = state.pool.lock().unwrap().get()?;
let hostname = &state.settings.lock().unwrap().hostname;
get_feed_all_data(&conn, &hostname, &get_sort_type(info)?)
})
.await
.map(|rss| {
@ -53,7 +54,7 @@ async fn get_all_feed(
Ok(res)
}
fn get_feed_all_data(conn: &PgConnection, sort_type: &SortType) -> Result<String, failure::Error> {
fn get_feed_all_data(conn: &PgConnection, hostname: &str, sort_type: &SortType) -> Result<String, failure::Error> {
let site_view = SiteView::read(&conn)?;
let posts = PostQueryBuilder::create(&conn)
@ -61,12 +62,12 @@ fn get_feed_all_data(conn: &PgConnection, sort_type: &SortType) -> Result<String
.sort(sort_type)
.list()?;
let items = create_post_items(posts);
let items = create_post_items(posts, hostname);
let mut channel_builder = ChannelBuilder::default();
channel_builder
.title(&format!("{} - All", site_view.name))
.link(format!("https://{}", Settings::get().hostname))
.link(format!("https://{}", hostname))
.items(items);
if let Some(site_desc) = site_view.description {
@ -79,10 +80,12 @@ fn get_feed_all_data(conn: &PgConnection, sort_type: &SortType) -> Result<String
async fn get_feed(
path: web::Path<(String, String)>,
info: web::Query<Params>,
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
state: web::Data<ChatSharedState>,
) -> Result<HttpResponse, actix_web::Error> {
let res = web::block(move || {
let conn = db.get()?;
let conn = state.pool.lock().unwrap().get()?;
let hostname = &state.settings.lock().unwrap().hostname;
let jwt_secret = &state.settings.lock().unwrap().jwt_secret;
let sort_type = get_sort_type(info)?;
@ -97,10 +100,10 @@ async fn get_feed(
let param = path.1.to_owned();
match request_type {
RequestType::User => get_feed_user(&conn, &sort_type, param),
RequestType::Community => get_feed_community(&conn, &sort_type, param),
RequestType::Front => get_feed_front(&conn, &sort_type, param),
RequestType::Inbox => get_feed_inbox(&conn, param),
RequestType::User => get_feed_user(&conn, &hostname, &sort_type, param),
RequestType::Community => get_feed_community(&conn, &hostname, &sort_type, param),
RequestType::Front => get_feed_front(&conn, &hostname, &jwt_secret, &sort_type, param),
RequestType::Inbox => get_feed_inbox(&conn, &hostname, &jwt_secret, param),
}
})
.await
@ -124,12 +127,13 @@ fn get_sort_type(info: web::Query<Params>) -> Result<SortType, ParseError> {
fn get_feed_user(
conn: &PgConnection,
hostname: &str,
sort_type: &SortType,
user_name: String,
) -> Result<ChannelBuilder, Error> {
let site_view = SiteView::read(&conn)?;
let user = User_::find_by_username(&conn, &user_name)?;
let user_url = user.get_profile_url();
let user_url = user.get_profile_url(hostname);
let posts = PostQueryBuilder::create(&conn)
.listing_type(ListingType::All)
@ -137,7 +141,7 @@ fn get_feed_user(
.for_creator_id(user.id)
.list()?;
let items = create_post_items(posts);
let items = create_post_items(posts, hostname);
let mut channel_builder = ChannelBuilder::default();
channel_builder
@ -150,12 +154,13 @@ fn get_feed_user(
fn get_feed_community(
conn: &PgConnection,
hostname: &str,
sort_type: &SortType,
community_name: String,
) -> Result<ChannelBuilder, Error> {
let site_view = SiteView::read(&conn)?;
let community = Community::read_from_name(&conn, community_name)?;
let community_url = community.get_url();
let community_url = community.get_url(hostname);
let posts = PostQueryBuilder::create(&conn)
.listing_type(ListingType::All)
@ -163,7 +168,7 @@ fn get_feed_community(
.for_community_id(community.id)
.list()?;
let items = create_post_items(posts);
let items = create_post_items(posts, hostname);
let mut channel_builder = ChannelBuilder::default();
channel_builder
@ -180,11 +185,13 @@ fn get_feed_community(
fn get_feed_front(
conn: &PgConnection,
hostname: &str,
jwt_secret: &str,
sort_type: &SortType,
jwt: String,
) -> Result<ChannelBuilder, Error> {
let site_view = SiteView::read(&conn)?;
let user_id = Claims::decode(&jwt)?.claims.id;
let user_id = Claims::decode(&jwt, jwt_secret)?.claims.id;
let posts = PostQueryBuilder::create(&conn)
.listing_type(ListingType::Subscribed)
@ -192,12 +199,12 @@ fn get_feed_front(
.my_user_id(user_id)
.list()?;
let items = create_post_items(posts);
let items = create_post_items(posts, hostname);
let mut channel_builder = ChannelBuilder::default();
channel_builder
.title(&format!("{} - Subscribed", site_view.name))
.link(format!("https://{}", Settings::get().hostname))
.link(format!("https://{}", hostname))
.items(items);
if let Some(site_desc) = site_view.description {
@ -207,9 +214,13 @@ fn get_feed_front(
Ok(channel_builder)
}
fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, Error> {
fn get_feed_inbox(
conn: &PgConnection,
hostname: &str,
jwt_secret: &str,
jwt: String) -> Result<ChannelBuilder, Error> {
let site_view = SiteView::read(&conn)?;
let user_id = Claims::decode(&jwt)?.claims.id;
let user_id = Claims::decode(&jwt, jwt_secret)?.claims.id;
let sort = SortType::New;
@ -221,12 +232,12 @@ fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, Er
.sort(&sort)
.list()?;
let items = create_reply_and_mention_items(replies, mentions);
let items = create_reply_and_mention_items(replies, mentions, hostname);
let mut channel_builder = ChannelBuilder::default();
channel_builder
.title(&format!("{} - Inbox", site_view.name))
.link(format!("https://{}/inbox", Settings::get().hostname))
.link(format!("https://{}/inbox", hostname))
.items(items);
if let Some(site_desc) = site_view.description {
@ -239,17 +250,18 @@ fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, Er
fn create_reply_and_mention_items(
replies: Vec<ReplyView>,
mentions: Vec<UserMentionView>,
hostname: &str,
) -> Vec<Item> {
let mut reply_items: Vec<Item> = replies
.iter()
.map(|r| {
let reply_url = format!(
"https://{}/post/{}/comment/{}",
Settings::get().hostname,
hostname,
r.post_id,
r.id
);
build_item(&r.creator_name, &r.published, &reply_url, &r.content)
build_item(&r.creator_name, &r.published, &reply_url, &r.content, hostname)
})
.collect();
@ -258,11 +270,11 @@ fn create_reply_and_mention_items(
.map(|m| {
let mention_url = format!(
"https://{}/post/{}/comment/{}",
Settings::get().hostname,
hostname,
m.post_id,
m.id
);
build_item(&m.creator_name, &m.published, &mention_url, &m.content)
build_item(&m.creator_name, &m.published, &mention_url, &m.content, hostname)
})
.collect();
@ -270,10 +282,10 @@ fn create_reply_and_mention_items(
reply_items
}
fn build_item(creator_name: &str, published: &NaiveDateTime, url: &str, content: &str) -> Item {
fn build_item(creator_name: &str, published: &NaiveDateTime, url: &str, content: &str, hostname: &str) -> Item {
let mut i = ItemBuilder::default();
i.title(format!("Reply from {}", creator_name));
let author_url = format!("https://{}/u/{}", Settings::get().hostname, creator_name);
let author_url = format!("https://{}/u/{}", hostname, creator_name);
i.author(format!(
"/u/{} <a href=\"{}\">(link)</a>",
creator_name, author_url
@ -290,7 +302,7 @@ fn build_item(creator_name: &str, published: &NaiveDateTime, url: &str, content:
i.build().unwrap()
}
fn create_post_items(posts: Vec<PostView>) -> Vec<Item> {
fn create_post_items(posts: Vec<PostView>, hostname: &str) -> Vec<Item> {
let mut items: Vec<Item> = Vec::new();
for p in posts {
@ -298,7 +310,7 @@ fn create_post_items(posts: Vec<PostView>) -> Vec<Item> {
i.title(p.name);
let author_url = format!("https://{}/u/{}", Settings::get().hostname, p.creator_name);
let author_url = format!("https://{}/u/{}", hostname, p.creator_name);
i.author(format!(
"/u/{} <a href=\"{}\">(link)</a>",
p.creator_name, author_url
@ -307,7 +319,7 @@ fn create_post_items(posts: Vec<PostView>) -> Vec<Item> {
let dt = DateTime::<Utc>::from_utc(p.published, Utc);
i.pub_date(dt.to_rfc2822());
let post_url = format!("https://{}/post/{}", Settings::get().hostname, p.id);
let post_url = format!("https://{}/post/{}", hostname, p.id);
i.comments(post_url.to_owned());
let guid = GuidBuilder::default()
.permalink(true)
@ -317,7 +329,7 @@ fn create_post_items(posts: Vec<PostView>) -> Vec<Item> {
let community_url = format!(
"https://{}/c/{}",
Settings::get().hostname,
hostname,
p.community_name
);
@ -326,7 +338,7 @@ fn create_post_items(posts: Vec<PostView>) -> Vec<Item> {
"/c/{} <a href=\"{}\">(link)</a>",
p.community_name, community_url
))
.domain(Settings::get().hostname.to_owned())
.domain(hostname.to_string())
.build();
i.categories(vec![category.unwrap()]);

View file

@ -1,4 +1,4 @@
use crate::settings::Settings;
use crate::websocket::server::ChatSharedState;
use actix_files::NamedFile;
use actix_web::web;
@ -43,8 +43,10 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("/password_change/{token}", web::get().to(index));
}
async fn index() -> Result<NamedFile, actix_web::error::Error> {
async fn index(
state: web::Data<ChatSharedState>,
) -> Result<NamedFile, actix_web::error::Error> {
Ok(NamedFile::open(
Settings::get().front_end_dir + "/index.html",
state.settings.lock().unwrap().front_end_dir.to_owned() + "/index.html",
)?)
}

View file

@ -1,11 +1,9 @@
use crate::db::site_view::SiteView;
use crate::version;
use crate::Settings;
use crate::websocket::server::ChatSharedState;
use actix_web::body::Body;
use actix_web::web;
use actix_web::HttpResponse;
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
use serde::Serialize;
pub fn config(cfg: &mut web::ServiceConfig) {
@ -14,26 +12,29 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("/.well-known/nodeinfo", web::get().to(node_info_well_known));
}
async fn node_info_well_known() -> HttpResponse<Body> {
async fn node_info_well_known(
state: web::Data<ChatSharedState>,
) -> HttpResponse<Body> {
let node_info = NodeInfoWellKnown {
links: NodeInfoWellKnownLinks {
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0".to_string(),
href: format!("https://{}/nodeinfo/2.0.json", Settings::get().hostname),
href: format!("https://{}/nodeinfo/2.0.json", state.settings.lock().unwrap().hostname),
},
};
HttpResponse::Ok().json(node_info)
}
async fn node_info(
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
state: web::Data<ChatSharedState>,
) -> Result<HttpResponse, actix_web::Error> {
let res = web::block(move || {
let conn = db.get()?;
let conn = state.pool.lock().unwrap().get()?;
let site_view = match SiteView::read(&conn) {
Ok(site_view) => site_view,
Err(_) => return Err(format_err!("not_found")),
};
let protocols = if Settings::get().federation_enabled {
let protocols = if state.settings.lock().unwrap().federation_enabled {
vec!["activitypub".to_string()]
} else {
vec![]

View file

@ -1,4 +1,5 @@
use crate::db::community::Community;
use crate::websocket::server::ChatSharedState;
use crate::Settings;
use actix_web::web;
use actix_web::web::Query;
@ -14,21 +15,17 @@ pub struct Params {
resource: String,
}
pub fn config(cfg: &mut web::ServiceConfig) {
if Settings::get().federation_enabled {
pub fn config(
cfg: &mut web::ServiceConfig,
) {
// TODO couldn't get this to pass the app state
// if state.settings.lock().unwrap().federation_enabled {
cfg.route(
".well-known/webfinger",
web::get().to(get_webfinger_response),
);
}
}
lazy_static! {
static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
"^group:([a-z0-9_]{{3, 20}})@{}$",
Settings::get().hostname
))
.unwrap();
// }
}
/// Responds to webfinger requests of the following format. There isn't any real documentation for
@ -39,10 +36,16 @@ lazy_static! {
/// https://radical.town/.well-known/webfinger?resource=acct:felix@radical.town
async fn get_webfinger_response(
info: Query<Params>,
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
state: web::Data<ChatSharedState>,
) -> Result<HttpResponse, actix_web::Error> {
let res = web::block(move || {
let conn = db.get()?;
let conn = state.pool.lock().unwrap().get()?;
let hostname = &state.settings.lock().unwrap().hostname;
let WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
"^group:([a-z0-9_]{{3, 20}})@{}$",
hostname
))
.unwrap();
let regex_parsed = WEBFINGER_COMMUNITY_REGEX
.captures(&info.resource)
@ -63,7 +66,7 @@ async fn get_webfinger_response(
Err(_) => return Err(format_err!("not_found")),
};
let community_url = community.get_url();
let community_url = community.get_url(&hostname);
Ok(json!({
"subject": info.resource,

View file

@ -4,7 +4,8 @@ use serde::Deserialize;
use std::env;
use std::fs;
use std::net::IpAddr;
use std::sync::RwLock;
use std::sync::Mutex;
use std::sync::Arc;
static CONFIG_FILE_DEFAULTS: &str = "config/defaults.hjson";
static CONFIG_FILE: &str = "config/config.hjson";
@ -60,19 +61,12 @@ pub struct Database {
pub pool_size: u32,
}
lazy_static! {
static ref SETTINGS: RwLock<Settings> = RwLock::new(match Settings::init() {
Ok(c) => c,
Err(e) => panic!("{}", e),
});
}
impl Settings {
/// Reads config from the files and environment.
/// First, defaults are loaded from CONFIG_FILE_DEFAULTS, then these values can be overwritten
/// from CONFIG_FILE (optional). Finally, values from the environment (with prefix LEMMY) are
/// added to the config.
fn init() -> Result<Self, ConfigError> {
pub fn init() -> Result<Self, ConfigError> {
let mut s = Config::new();
s.merge(File::with_name(CONFIG_FILE_DEFAULTS))?;
@ -90,9 +84,9 @@ impl Settings {
}
/// Returns the config as a struct.
pub fn get() -> Self {
SETTINGS.read().unwrap().to_owned()
}
// pub fn get() -> Self {
// SETTINGS.read().unwrap().to_owned()
// }
/// Returns the postgres connection url. If LEMMY_DATABASE_URL is set, that is used,
/// otherwise the connection url is generated from the config.
@ -120,15 +114,18 @@ impl Settings {
pub fn save_config_file(data: &str) -> Result<String, Error> {
fs::write(CONFIG_FILE, data)?;
// Reload the new settings
// From https://stackoverflow.com/questions/29654927/how-do-i-assign-a-string-to-a-mutable-static-variable/47181804#47181804
let mut new_settings = SETTINGS.write().unwrap();
*new_settings = match Settings::init() {
Ok(c) => c,
Err(e) => panic!("{}", e),
};
Self::read_config_file()
}
// pub fn live_reload_config(new_settings: Self) -> Result<(), Error> {
// // TODO test that this works now
// // Reload the new settings
// // From https://stackoverflow.com/questions/29654927/how-do-i-assign-a-string-to-a-mutable-static-variable/47181804#47181804
// new_settings = match Settings::init() {
// Ok(c) => c,
// Err(e) => panic!("{}", e),
// };
// Ok(())
// }
}

View file

@ -7,13 +7,16 @@ use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
use diesel::PgConnection;
use failure::Error;
use log::{error, info, warn};
use rand::{rngs::ThreadRng, Rng};
use rand::{Rng};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::{HashMap, HashSet};
use std::str::FromStr;
use std::time::SystemTime;
use std::sync::Mutex;
use std::sync::Arc;
use strum::IntoEnumIterator;
use actix_web::web;
use crate::api::comment::*;
use crate::api::community::*;
@ -80,105 +83,138 @@ pub enum RateLimitType {
Post,
}
/// `ChatServer` manages chat rooms and responsible for coordinating chat
/// session.
pub struct ChatServer {
pub struct ChatSharedState {
/// The Settings
pub settings: Mutex<Settings>,
/// Connection pool
pub pool: Mutex<Pool<ConnectionManager<PgConnection>>>,
/// A map from generated random ID to session addr
sessions: HashMap<ConnectionId, SessionInfo>,
sessions: Mutex<HashMap<ConnectionId, SessionInfo>>,
/// A map from post_id to set of connectionIDs
post_rooms: HashMap<PostId, HashSet<ConnectionId>>,
post_rooms: Mutex<HashMap<PostId, HashSet<ConnectionId>>>,
/// A map from community to set of connectionIDs
community_rooms: HashMap<CommunityId, HashSet<ConnectionId>>,
community_rooms: Mutex<HashMap<CommunityId, HashSet<ConnectionId>>>,
/// A map from user id to its connection ID for joined users. Remember a user can have multiple
/// sessions (IE clients)
user_rooms: HashMap<UserId, HashSet<ConnectionId>>,
user_rooms: Mutex<HashMap<UserId, HashSet<ConnectionId>>>,
/// Rate limiting based on rate type and IP addr
rate_limit_buckets: HashMap<RateLimitType, HashMap<IPAddr, RateLimitBucket>>,
rate_limit_buckets: Mutex<HashMap<RateLimitType, HashMap<IPAddr, RateLimitBucket>>>,
}
rng: ThreadRng,
db: Pool<ConnectionManager<PgConnection>>,
impl ChatSharedState {
pub fn init() -> Self {
// Setup Settings
let settings = match Settings::init() {
Ok(c) => c,
Err(e) => panic!("{}", e),
};
// Set up the r2d2 connection pool
let manager = ConnectionManager::<PgConnection>::new(&settings.get_database_url());
let pool = Pool::builder()
.max_size(settings.database.pool_size)
.build(manager)
.unwrap_or_else(|_| panic!("Error connecting to {}", settings.get_database_url()));
ChatSharedState {
settings: Mutex::new(settings),
pool: Mutex::new(pool),
sessions: Mutex::new(HashMap::new()),
rate_limit_buckets: Mutex::new(HashMap::new()),
post_rooms: Mutex::new(HashMap::new()),
community_rooms: Mutex::new(HashMap::new()),
user_rooms: Mutex::new(HashMap::new()),
}
}
}
/// `ChatServer` manages chat rooms and responsible for coordinating chat
/// session.
pub struct ChatServer {
state: web::Data<ChatSharedState>,
}
impl ChatServer {
pub fn startup(db: Pool<ConnectionManager<PgConnection>>) -> ChatServer {
pub fn init(state: web::Data<ChatSharedState>) -> Self {
ChatServer {
sessions: HashMap::new(),
rate_limit_buckets: HashMap::new(),
post_rooms: HashMap::new(),
community_rooms: HashMap::new(),
user_rooms: HashMap::new(),
rng: rand::thread_rng(),
db,
state,
}
}
fn join_community_room(&mut self, community_id: CommunityId, id: ConnectionId) {
fn join_community_room(&self, community_id: CommunityId, id: ConnectionId) {
// remove session from all rooms
for sessions in self.community_rooms.values_mut() {
for sessions in self.state.community_rooms.lock().unwrap().values_mut() {
sessions.remove(&id);
}
// Also leave all post rooms
// This avoids double messages
for sessions in self.post_rooms.values_mut() {
for sessions in self.state.post_rooms.lock().unwrap().values_mut() {
sessions.remove(&id);
}
// If the room doesn't exist yet
if self.community_rooms.get_mut(&community_id).is_none() {
self.community_rooms.insert(community_id, HashSet::new());
if self.state.community_rooms.lock().unwrap().get_mut(&community_id).is_none() {
self.state.community_rooms.lock().unwrap().insert(community_id, HashSet::new());
}
self
.state
.community_rooms
.lock()
.unwrap()
.get_mut(&community_id)
.unwrap()
.insert(id);
}
fn join_post_room(&mut self, post_id: PostId, id: ConnectionId) {
fn join_post_room(&self, post_id: PostId, id: ConnectionId) {
// remove session from all rooms
for sessions in self.post_rooms.values_mut() {
for sessions in self.state.post_rooms.lock().unwrap().values_mut() {
sessions.remove(&id);
}
// Also leave all communities
// This avoids double messages
for sessions in self.community_rooms.values_mut() {
for sessions in self.state.community_rooms.lock().unwrap().values_mut() {
sessions.remove(&id);
}
// If the room doesn't exist yet
if self.post_rooms.get_mut(&post_id).is_none() {
self.post_rooms.insert(post_id, HashSet::new());
if self.state.post_rooms.lock().unwrap().get_mut(&post_id).is_none() {
self.state.post_rooms.lock().unwrap().insert(post_id, HashSet::new());
}
self.post_rooms.get_mut(&post_id).unwrap().insert(id);
self.state.post_rooms.lock().unwrap().get_mut(&post_id).unwrap().insert(id);
}
fn join_user_room(&mut self, user_id: UserId, id: ConnectionId) {
fn join_user_room(&self, user_id: UserId, id: ConnectionId) {
// remove session from all rooms
for sessions in self.user_rooms.values_mut() {
for sessions in self.state.user_rooms.lock().unwrap().values_mut() {
sessions.remove(&id);
}
// If the room doesn't exist yet
if self.user_rooms.get_mut(&user_id).is_none() {
self.user_rooms.insert(user_id, HashSet::new());
if self.state.user_rooms.lock().unwrap().get_mut(&user_id).is_none() {
self.state.user_rooms.lock().unwrap().insert(user_id, HashSet::new());
}
self.user_rooms.get_mut(&user_id).unwrap().insert(id);
self.state.user_rooms.lock().unwrap().get_mut(&user_id).unwrap().insert(id);
}
fn send_post_room_message(&self, post_id: PostId, message: &str, skip_id: ConnectionId) {
if let Some(sessions) = self.post_rooms.get(&post_id) {
if let Some(sessions) = self.state.post_rooms.lock().unwrap().get(&post_id) {
for id in sessions {
if *id != skip_id {
if let Some(info) = self.sessions.get(id) {
if let Some(info) = self.state.sessions.lock().unwrap().get(id) {
let _ = info.addr.do_send(WSMessage(message.to_owned()));
}
}
@ -192,10 +228,10 @@ impl ChatServer {
message: &str,
skip_id: ConnectionId,
) {
if let Some(sessions) = self.community_rooms.get(&community_id) {
if let Some(sessions) = self.state.community_rooms.lock().unwrap().get(&community_id) {
for id in sessions {
if *id != skip_id {
if let Some(info) = self.sessions.get(id) {
if let Some(info) = self.state.sessions.lock().unwrap().get(id) {
let _ = info.addr.do_send(WSMessage(message.to_owned()));
}
}
@ -204,10 +240,10 @@ impl ChatServer {
}
fn send_user_room_message(&self, user_id: UserId, message: &str, skip_id: ConnectionId) {
if let Some(sessions) = self.user_rooms.get(&user_id) {
if let Some(sessions) = self.state.user_rooms.lock().unwrap().get(&user_id) {
for id in sessions {
if *id != skip_id {
if let Some(info) = self.sessions.get(id) {
if let Some(info) = self.state.sessions.lock().unwrap().get(id) {
let _ = info.addr.do_send(WSMessage(message.to_owned()));
}
}
@ -216,9 +252,9 @@ impl ChatServer {
}
fn send_all_message(&self, message: &str, skip_id: ConnectionId) {
for id in self.sessions.keys() {
for id in self.state.sessions.lock().unwrap().keys() {
if *id != skip_id {
if let Some(info) = self.sessions.get(id) {
if let Some(info) = self.state.sessions.lock().unwrap().get(id) {
let _ = info.addr.do_send(WSMessage(message.to_owned()));
}
}
@ -287,47 +323,47 @@ impl ChatServer {
to_json_string(&user_operation, post)
}
fn check_rate_limit_register(&mut self, id: usize, check_only: bool) -> Result<(), Error> {
fn check_rate_limit_register(&self, id: usize, check_only: bool) -> Result<(), Error> {
self.check_rate_limit_full(
RateLimitType::Register,
id,
Settings::get().rate_limit.register,
Settings::get().rate_limit.register_per_second,
self.state.settings.lock().unwrap().rate_limit.register,
self.state.settings.lock().unwrap().rate_limit.register_per_second,
check_only,
)
}
fn check_rate_limit_post(&mut self, id: usize, check_only: bool) -> Result<(), Error> {
fn check_rate_limit_post(&self, id: usize, check_only: bool) -> Result<(), Error> {
self.check_rate_limit_full(
RateLimitType::Post,
id,
Settings::get().rate_limit.post,
Settings::get().rate_limit.post_per_second,
self.state.settings.lock().unwrap().rate_limit.post,
self.state.settings.lock().unwrap().rate_limit.post_per_second,
check_only,
)
}
fn check_rate_limit_message(&mut self, id: usize, check_only: bool) -> Result<(), Error> {
fn check_rate_limit_message(&self, id: usize, check_only: bool) -> Result<(), Error> {
self.check_rate_limit_full(
RateLimitType::Message,
id,
Settings::get().rate_limit.message,
Settings::get().rate_limit.message_per_second,
self.state.settings.lock().unwrap().rate_limit.message,
self.state.settings.lock().unwrap().rate_limit.message_per_second,
check_only,
)
}
#[allow(clippy::float_cmp)]
fn check_rate_limit_full(
&mut self,
&self,
type_: RateLimitType,
id: usize,
rate: i32,
per: i32,
check_only: bool,
) -> Result<(), Error> {
if let Some(info) = self.sessions.get(&id) {
if let Some(bucket) = self.rate_limit_buckets.get_mut(&type_) {
if let Some(info) = self.state.sessions.lock().unwrap().get(&id) {
if let Some(bucket) = self.state.rate_limit_buckets.lock().unwrap().get_mut(&type_) {
if let Some(rate_limit) = bucket.get_mut(&info.ip) {
let current = SystemTime::now();
let time_passed = current.duration_since(rate_limit.last_checked)?.as_secs() as f64;
@ -376,7 +412,7 @@ impl ChatServer {
impl Actor for ChatServer {
/// We are going to use simple Context, we just need ability to communicate
/// with other actors.
type Context = Context<Self>;
type Context = SyncContext<Self>;
}
/// Handler for Connect message.
@ -385,12 +421,12 @@ impl Actor for ChatServer {
impl Handler<Connect> for ChatServer {
type Result = usize;
fn handle(&mut self, msg: Connect, _ctx: &mut Context<Self>) -> Self::Result {
fn handle(&mut self, msg: Connect, _ctx: &mut SyncContext<Self>) -> Self::Result {
// register session with random id
let id = self.rng.gen::<usize>();
let id = rand::thread_rng().gen::<usize>();
info!("{} joined", &msg.ip);
self.sessions.insert(
self.state.sessions.lock().unwrap().insert(
id,
SessionInfo {
addr: msg.addr,
@ -399,13 +435,16 @@ impl Handler<Connect> for ChatServer {
);
for rate_limit_type in RateLimitType::iter() {
if self.rate_limit_buckets.get(&rate_limit_type).is_none() {
if self.state.rate_limit_buckets.lock().unwrap().get(&rate_limit_type).is_none() {
self
.state
.rate_limit_buckets
.lock()
.unwrap()
.insert(rate_limit_type, HashMap::new());
}
if let Some(bucket) = self.rate_limit_buckets.get_mut(&rate_limit_type) {
if let Some(bucket) = self.state.rate_limit_buckets.lock().unwrap().get_mut(&rate_limit_type) {
if bucket.get(&msg.ip).is_none() {
bucket.insert(
msg.ip.to_owned(),
@ -426,18 +465,18 @@ impl Handler<Connect> for ChatServer {
impl Handler<Disconnect> for ChatServer {
type Result = ();
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
fn handle(&mut self, msg: Disconnect, _: &mut SyncContext<Self>) {
// Remove connections from sessions and all 3 scopes
if self.sessions.remove(&msg.id).is_some() {
for sessions in self.user_rooms.values_mut() {
if self.state.sessions.lock().unwrap().remove(&msg.id).is_some() {
for sessions in self.state.user_rooms.lock().unwrap().values_mut() {
sessions.remove(&msg.id);
}
for sessions in self.post_rooms.values_mut() {
for sessions in self.state.post_rooms.lock().unwrap().values_mut() {
sessions.remove(&msg.id);
}
for sessions in self.community_rooms.values_mut() {
for sessions in self.state.community_rooms.lock().unwrap().values_mut() {
sessions.remove(&msg.id);
}
}
@ -448,7 +487,7 @@ impl Handler<Disconnect> for ChatServer {
impl Handler<StandardMessage> for ChatServer {
type Result = MessageResult<StandardMessage>;
fn handle(&mut self, msg: StandardMessage, _: &mut Context<Self>) -> Self::Result {
fn handle(&mut self, msg: StandardMessage, _: &mut SyncContext<Self>) -> Self::Result {
match parse_json_message(self, msg) {
Ok(m) => {
info!("Message Sent: {}", m);
@ -483,6 +522,7 @@ fn do_user_operation<'a, Data, Response>(
op: UserOperation,
data: &str,
conn: &PooledConnection<ConnectionManager<PgConnection>>,
settings: &Settings,
) -> Result<String, Error>
where
for<'de> Data: Deserialize<'de> + 'a,
@ -490,7 +530,7 @@ where
Oper<Data>: Perform<Response>,
{
let parsed_data: Data = serde_json::from_str(data)?;
let res = Oper::new(parsed_data).perform(&conn)?;
let res = Oper::new(parsed_data).perform(&conn, &settings)?;
to_json_string(&op, &res)
}
@ -501,7 +541,8 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
message: "Unknown op type".to_string(),
})?;
let conn = chat.db.get()?;
let conn = chat.state.pool.lock().unwrap().get()?;
let settings = chat.state.settings.lock().unwrap();
let user_operation: UserOperation = UserOperation::from_str(&op)?;
@ -512,54 +553,54 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
chat.check_rate_limit_message(msg.id, false)?;
match user_operation {
UserOperation::Login => do_user_operation::<Login, LoginResponse>(user_operation, data, &conn),
UserOperation::Login => do_user_operation::<Login, LoginResponse>(user_operation, data, &conn, &settings),
UserOperation::Register => {
chat.check_rate_limit_register(msg.id, true)?;
let register: Register = serde_json::from_str(data)?;
let res = Oper::new(register).perform(&conn)?;
let res = Oper::new(register).perform(&conn, &settings)?;
chat.check_rate_limit_register(msg.id, false)?;
to_json_string(&user_operation, &res)
}
UserOperation::GetUserDetails => {
do_user_operation::<GetUserDetails, GetUserDetailsResponse>(user_operation, data, &conn)
do_user_operation::<GetUserDetails, GetUserDetailsResponse>(user_operation, data, &conn, &settings)
}
UserOperation::SaveUserSettings => {
do_user_operation::<SaveUserSettings, LoginResponse>(user_operation, data, &conn)
do_user_operation::<SaveUserSettings, LoginResponse>(user_operation, data, &conn, &settings)
}
UserOperation::AddAdmin => {
let add_admin: AddAdmin = serde_json::from_str(data)?;
let res = Oper::new(add_admin).perform(&conn)?;
let res = Oper::new(add_admin).perform(&conn, &settings)?;
let res_str = to_json_string(&user_operation, &res)?;
chat.send_all_message(&res_str, msg.id);
Ok(res_str)
}
UserOperation::BanUser => {
let ban_user: BanUser = serde_json::from_str(data)?;
let res = Oper::new(ban_user).perform(&conn)?;
let res = Oper::new(ban_user).perform(&conn, &settings)?;
let res_str = to_json_string(&user_operation, &res)?;
chat.send_all_message(&res_str, msg.id);
Ok(res_str)
}
UserOperation::GetReplies => {
do_user_operation::<GetReplies, GetRepliesResponse>(user_operation, data, &conn)
do_user_operation::<GetReplies, GetRepliesResponse>(user_operation, data, &conn, &settings)
}
UserOperation::GetUserMentions => {
do_user_operation::<GetUserMentions, GetUserMentionsResponse>(user_operation, data, &conn)
do_user_operation::<GetUserMentions, GetUserMentionsResponse>(user_operation, data, &conn, &settings)
}
UserOperation::EditUserMention => {
do_user_operation::<EditUserMention, UserMentionResponse>(user_operation, data, &conn)
do_user_operation::<EditUserMention, UserMentionResponse>(user_operation, data, &conn, &settings)
}
UserOperation::MarkAllAsRead => {
do_user_operation::<MarkAllAsRead, GetRepliesResponse>(user_operation, data, &conn)
do_user_operation::<MarkAllAsRead, GetRepliesResponse>(user_operation, data, &conn, &settings)
}
UserOperation::GetCommunity => {
let get_community: GetCommunity = serde_json::from_str(data)?;
let mut res = Oper::new(get_community).perform(&conn)?;
let mut res = Oper::new(get_community).perform(&conn, &settings)?;
let community_id = res.community.id;
chat.join_community_room(community_id, msg.id);
res.online = if let Some(community_users) = chat.community_rooms.get(&community_id) {
res.online = if let Some(community_users) = chat.state.community_rooms.lock().unwrap().get(&community_id) {
community_users.len()
} else {
0
@ -568,18 +609,18 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
to_json_string(&user_operation, &res)
}
UserOperation::ListCommunities => {
do_user_operation::<ListCommunities, ListCommunitiesResponse>(user_operation, data, &conn)
do_user_operation::<ListCommunities, ListCommunitiesResponse>(user_operation, data, &conn, &settings)
}
UserOperation::CreateCommunity => {
chat.check_rate_limit_register(msg.id, true)?;
let create_community: CreateCommunity = serde_json::from_str(data)?;
let res = Oper::new(create_community).perform(&conn)?;
let res = Oper::new(create_community).perform(&conn, &settings)?;
chat.check_rate_limit_register(msg.id, false)?;
to_json_string(&user_operation, &res)
}
UserOperation::EditCommunity => {
let edit_community: EditCommunity = serde_json::from_str(data)?;
let res = Oper::new(edit_community).perform(&conn)?;
let res = Oper::new(edit_community).perform(&conn, &settings)?;
let mut community_sent: CommunityResponse = res.clone();
community_sent.community.user_id = None;
community_sent.community.subscribed = None;
@ -588,16 +629,16 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
to_json_string(&user_operation, &res)
}
UserOperation::FollowCommunity => {
do_user_operation::<FollowCommunity, CommunityResponse>(user_operation, data, &conn)
do_user_operation::<FollowCommunity, CommunityResponse>(user_operation, data, &conn, &settings)
}
UserOperation::GetFollowedCommunities => do_user_operation::<
GetFollowedCommunities,
GetFollowedCommunitiesResponse,
>(user_operation, data, &conn),
>(user_operation, data, &conn, &settings),
UserOperation::BanFromCommunity => {
let ban_from_community: BanFromCommunity = serde_json::from_str(data)?;
let community_id = ban_from_community.community_id;
let res = Oper::new(ban_from_community).perform(&conn)?;
let res = Oper::new(ban_from_community).perform(&conn, &settings)?;
let res_str = to_json_string(&user_operation, &res)?;
chat.send_community_room_message(community_id, &res_str, msg.id);
Ok(res_str)
@ -605,21 +646,21 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
UserOperation::AddModToCommunity => {
let mod_add_to_community: AddModToCommunity = serde_json::from_str(data)?;
let community_id = mod_add_to_community.community_id;
let res = Oper::new(mod_add_to_community).perform(&conn)?;
let res = Oper::new(mod_add_to_community).perform(&conn, &settings)?;
let res_str = to_json_string(&user_operation, &res)?;
chat.send_community_room_message(community_id, &res_str, msg.id);
Ok(res_str)
}
UserOperation::ListCategories => {
do_user_operation::<ListCategories, ListCategoriesResponse>(user_operation, data, &conn)
do_user_operation::<ListCategories, ListCategoriesResponse>(user_operation, data, &conn, &settings)
}
UserOperation::GetPost => {
let get_post: GetPost = serde_json::from_str(data)?;
let post_id = get_post.id;
chat.join_post_room(post_id, msg.id);
let mut res = Oper::new(get_post).perform(&conn)?;
let mut res = Oper::new(get_post).perform(&conn, &settings)?;
res.online = if let Some(post_users) = chat.post_rooms.get(&post_id) {
res.online = if let Some(post_users) = chat.state.post_rooms.lock().unwrap().get(&post_id) {
post_users.len()
} else {
0
@ -633,7 +674,7 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
// 0 is the "all" community
chat.join_community_room(0, msg.id);
}
let res = Oper::new(get_posts).perform(&conn)?;
let res = Oper::new(get_posts).perform(&conn, &settings)?;
to_json_string(&user_operation, &res)
}
UserOperation::GetComments => {
@ -642,118 +683,123 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
// 0 is the "all" community
chat.join_community_room(0, msg.id);
}
let res = Oper::new(get_comments).perform(&conn)?;
let res = Oper::new(get_comments).perform(&conn, &settings)?;
to_json_string(&user_operation, &res)
}
UserOperation::CreatePost => {
chat.check_rate_limit_post(msg.id, true)?;
let create_post: CreatePost = serde_json::from_str(data)?;
let res = Oper::new(create_post).perform(&conn)?;
let res = Oper::new(create_post).perform(&conn, &settings)?;
chat.check_rate_limit_post(msg.id, false)?;
chat.post_sends(UserOperation::CreatePost, res, msg.id)
}
UserOperation::CreatePostLike => {
let create_post_like: CreatePostLike = serde_json::from_str(data)?;
let res = Oper::new(create_post_like).perform(&conn)?;
let res = Oper::new(create_post_like).perform(&conn, &settings)?;
chat.post_sends(UserOperation::CreatePostLike, res, msg.id)
}
UserOperation::EditPost => {
let edit_post: EditPost = serde_json::from_str(data)?;
let res = Oper::new(edit_post).perform(&conn)?;
let res = Oper::new(edit_post).perform(&conn, &settings)?;
chat.post_sends(UserOperation::EditPost, res, msg.id)
}
UserOperation::SavePost => {
do_user_operation::<SavePost, PostResponse>(user_operation, data, &conn)
do_user_operation::<SavePost, PostResponse>(user_operation, data, &conn, &settings)
}
UserOperation::CreateComment => {
let create_comment: CreateComment = serde_json::from_str(data)?;
let res = Oper::new(create_comment).perform(&conn)?;
let res = Oper::new(create_comment).perform(&conn, &settings)?;
chat.comment_sends(UserOperation::CreateComment, res, msg.id)
}
UserOperation::EditComment => {
let edit_comment: EditComment = serde_json::from_str(data)?;
let res = Oper::new(edit_comment).perform(&conn)?;
let res = Oper::new(edit_comment).perform(&conn, &settings)?;
chat.comment_sends(UserOperation::EditComment, res, msg.id)
}
UserOperation::SaveComment => {
do_user_operation::<SaveComment, CommentResponse>(user_operation, data, &conn)
do_user_operation::<SaveComment, CommentResponse>(user_operation, data, &conn, &settings)
}
UserOperation::CreateCommentLike => {
let create_comment_like: CreateCommentLike = serde_json::from_str(data)?;
let res = Oper::new(create_comment_like).perform(&conn)?;
let res = Oper::new(create_comment_like).perform(&conn, &settings)?;
chat.comment_sends(UserOperation::CreateCommentLike, res, msg.id)
}
UserOperation::GetModlog => {
do_user_operation::<GetModlog, GetModlogResponse>(user_operation, data, &conn)
do_user_operation::<GetModlog, GetModlogResponse>(user_operation, data, &conn, &settings)
}
UserOperation::CreateSite => {
do_user_operation::<CreateSite, SiteResponse>(user_operation, data, &conn)
do_user_operation::<CreateSite, SiteResponse>(user_operation, data, &conn, &settings)
}
UserOperation::EditSite => {
let edit_site: EditSite = serde_json::from_str(data)?;
let res = Oper::new(edit_site).perform(&conn)?;
let res = Oper::new(edit_site).perform(&conn, &settings)?;
let res_str = to_json_string(&user_operation, &res)?;
chat.send_all_message(&res_str, msg.id);
Ok(res_str)
}
UserOperation::GetSite => {
let get_site: GetSite = serde_json::from_str(data)?;
let mut res = Oper::new(get_site).perform(&conn)?;
res.online = chat.sessions.len();
let mut res = Oper::new(get_site).perform(&conn, &settings)?;
res.online = chat.state.sessions.lock().unwrap().len();
to_json_string(&user_operation, &res)
}
UserOperation::GetSiteConfig => {
let get_site_config: GetSiteConfig = serde_json::from_str(data)?;
let res = Oper::new(get_site_config).perform(&conn)?;
let res = Oper::new(get_site_config).perform(&conn, &settings)?;
to_json_string(&user_operation, &res)
}
UserOperation::SaveSiteConfig => {
let save_site_config: SaveSiteConfig = serde_json::from_str(data)?;
let res = Oper::new(save_site_config).perform(&conn)?;
let res = Oper::new(save_site_config).perform(&conn, &settings)?;
let mut new_settings = chat.state.settings.lock().unwrap();
*new_settings = match Settings::init() {
Ok(c) => c,
Err(e) => panic!("{}", e),
};
to_json_string(&user_operation, &res)
}
UserOperation::Search => {
do_user_operation::<Search, SearchResponse>(user_operation, data, &conn)
do_user_operation::<Search, SearchResponse>(user_operation, data, &conn, &settings)
}
UserOperation::TransferCommunity => {
do_user_operation::<TransferCommunity, GetCommunityResponse>(user_operation, data, &conn)
do_user_operation::<TransferCommunity, GetCommunityResponse>(user_operation, data, &conn, &settings)
}
UserOperation::TransferSite => {
do_user_operation::<TransferSite, GetSiteResponse>(user_operation, data, &conn)
do_user_operation::<TransferSite, GetSiteResponse>(user_operation, data, &conn, &settings)
}
UserOperation::DeleteAccount => {
do_user_operation::<DeleteAccount, LoginResponse>(user_operation, data, &conn)
do_user_operation::<DeleteAccount, LoginResponse>(user_operation, data, &conn, &settings)
}
UserOperation::PasswordReset => {
do_user_operation::<PasswordReset, PasswordResetResponse>(user_operation, data, &conn)
do_user_operation::<PasswordReset, PasswordResetResponse>(user_operation, data, &conn, &settings)
}
UserOperation::PasswordChange => {
do_user_operation::<PasswordChange, LoginResponse>(user_operation, data, &conn)
do_user_operation::<PasswordChange, LoginResponse>(user_operation, data, &conn, &settings)
}
UserOperation::CreatePrivateMessage => {
let create_private_message: CreatePrivateMessage = serde_json::from_str(data)?;
let recipient_id = create_private_message.recipient_id;
let res = Oper::new(create_private_message).perform(&conn)?;
let res = Oper::new(create_private_message).perform(&conn, &settings)?;
let res_str = to_json_string(&user_operation, &res)?;
chat.send_user_room_message(recipient_id, &res_str, msg.id);
Ok(res_str)
}
UserOperation::EditPrivateMessage => {
do_user_operation::<EditPrivateMessage, PrivateMessageResponse>(user_operation, data, &conn)
do_user_operation::<EditPrivateMessage, PrivateMessageResponse>(user_operation, data, &conn, &settings)
}
UserOperation::GetPrivateMessages => {
do_user_operation::<GetPrivateMessages, PrivateMessagesResponse>(user_operation, data, &conn)
do_user_operation::<GetPrivateMessages, PrivateMessagesResponse>(user_operation, data, &conn, &settings)
}
UserOperation::UserJoin => {
let user_join: UserJoin = serde_json::from_str(data)?;
let res = Oper::new(user_join).perform(&conn)?;
let res = Oper::new(user_join).perform(&conn, &settings)?;
chat.join_user_room(res.user_id, msg.id);
to_json_string(&user_operation, &res)
}