mirror of
https://github.com/LemmyNet/lemmy
synced 2024-11-05 06:00:31 +00:00
Merge remote-tracking branch 'origin/email-verification' into invite_instances
This commit is contained in:
commit
9889d158f1
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -1722,7 +1722,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_api"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-rt",
|
||||
@ -1765,7 +1765,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_api_common"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"chrono",
|
||||
@ -1782,7 +1782,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_api_crud"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-rt",
|
||||
@ -1826,7 +1826,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_apub"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"activitystreams-kinds",
|
||||
"actix",
|
||||
@ -1872,7 +1872,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_apub_lib"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"activitystreams",
|
||||
"actix-web",
|
||||
@ -1898,7 +1898,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_apub_lib_derive"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.32",
|
||||
"quote 1.0.10",
|
||||
@ -1908,7 +1908,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_db_schema"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
@ -1930,7 +1930,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_db_views"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
"lemmy_db_schema",
|
||||
@ -1942,7 +1942,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_db_views_actor"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
"lemmy_db_schema",
|
||||
@ -1951,7 +1951,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_db_views_moderator"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
"lemmy_db_schema",
|
||||
@ -1960,7 +1960,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_routes"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-http",
|
||||
@ -1987,7 +1987,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_server"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"activitystreams",
|
||||
"actix",
|
||||
@ -2029,7 +2029,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_utils"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"actix-rt",
|
||||
"actix-web",
|
||||
@ -2066,7 +2066,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lemmy_websocket"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-web",
|
||||
|
26
Cargo.toml
26
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_server"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -31,18 +31,18 @@ members = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
lemmy_api = { version = "=0.14.1", path = "./crates/api" }
|
||||
lemmy_api_crud = { version = "=0.14.1", path = "./crates/api_crud" }
|
||||
lemmy_apub = { version = "=0.14.1", path = "./crates/apub" }
|
||||
lemmy_apub_lib = { version = "=0.14.1", path = "./crates/apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.1", path = "./crates/utils" }
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "./crates/db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.1", path = "./crates/db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.1", path = "./crates/db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.1", path = "./crates/db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.1", path = "crates/api_common" }
|
||||
lemmy_websocket = { version = "=0.14.1", path = "./crates/websocket" }
|
||||
lemmy_routes = { version = "=0.14.1", path = "./crates/routes" }
|
||||
lemmy_api = { version = "=0.14.3", path = "./crates/api" }
|
||||
lemmy_api_crud = { version = "=0.14.3", path = "./crates/api_crud" }
|
||||
lemmy_apub = { version = "=0.14.3", path = "./crates/apub" }
|
||||
lemmy_apub_lib = { version = "=0.14.3", path = "./crates/apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.3", path = "./crates/utils" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "./crates/db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.3", path = "./crates/db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.3", path = "./crates/db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.3", path = "./crates/db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.3", path = "crates/api_common" }
|
||||
lemmy_websocket = { version = "=0.14.3", path = "./crates/websocket" }
|
||||
lemmy_routes = { version = "=0.14.3", path = "./crates/routes" }
|
||||
diesel = "1.4.8"
|
||||
diesel_migrations = "1.4.0"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_api"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -13,15 +13,15 @@ path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_apub = { version = "=0.14.1", path = "../apub" }
|
||||
lemmy_apub_lib = { version = "=0.14.1", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.1", path = "../utils" }
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.1", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.1", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.1", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.1", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.14.1", path = "../websocket" }
|
||||
lemmy_apub = { version = "=0.14.3", path = "../apub" }
|
||||
lemmy_apub_lib = { version = "=0.14.3", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.3", path = "../utils" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.3", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.3", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.3", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.3", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.14.3", path = "../websocket" }
|
||||
diesel = "1.4.8"
|
||||
bcrypt = "0.10.1"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
|
@ -219,8 +219,8 @@ mod tests {
|
||||
let inserted_person = Person::create(&conn, &new_person).unwrap();
|
||||
|
||||
let local_user_form = LocalUserForm {
|
||||
person_id: inserted_person.id,
|
||||
password_encrypted: "123456".to_string(),
|
||||
person_id: Some(inserted_person.id),
|
||||
password_encrypted: Some("123456".to_string()),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,7 @@ use lemmy_db_schema::{
|
||||
source::{
|
||||
comment::Comment,
|
||||
community::Community,
|
||||
email_verification::EmailVerification,
|
||||
local_user::{LocalUser, LocalUserForm},
|
||||
moderator::*,
|
||||
password_reset_request::*,
|
||||
@ -54,6 +55,7 @@ use lemmy_utils::{
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::{
|
||||
email::send_verification_email,
|
||||
messages::{CaptchaItem, SendAllMessage},
|
||||
LemmyContext,
|
||||
UserOperation,
|
||||
@ -88,13 +90,18 @@ impl Perform for Login {
|
||||
return Err(ApiError::err_plain("password_incorrect").into());
|
||||
}
|
||||
|
||||
let site = blocking(context.pool(), Site::read_simple).await??;
|
||||
if site.require_email_verification && !local_user_view.local_user.email_verified {
|
||||
return Err(ApiError::err_plain("email_not_verified").into());
|
||||
}
|
||||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
jwt: Some(Claims::jwt(
|
||||
local_user_view.local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?,
|
||||
)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -159,12 +166,29 @@ impl Perform for SaveUserSettings {
|
||||
|
||||
let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
|
||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||
let email = diesel_option_overwrite(&data.email);
|
||||
let bio = diesel_option_overwrite(&data.bio);
|
||||
let display_name = diesel_option_overwrite(&data.display_name);
|
||||
let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
|
||||
let bot_account = data.bot_account;
|
||||
|
||||
let email = if let Some(email) = &data.email {
|
||||
let site = blocking(context.pool(), Site::read_simple).await??;
|
||||
if site.require_email_verification {
|
||||
send_verification_email(
|
||||
local_user_view.local_user.id,
|
||||
email,
|
||||
&local_user_view.person.name,
|
||||
context,
|
||||
)
|
||||
.await?;
|
||||
None
|
||||
} else {
|
||||
diesel_option_overwrite(&data.email)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(Some(bio)) = &bio {
|
||||
if bio.chars().count() > 300 {
|
||||
return Err(ApiError::err_plain("bio_length_overflow").into());
|
||||
@ -222,9 +246,9 @@ impl Perform for SaveUserSettings {
|
||||
.map_err(|e| ApiError::err("user_already_exists", e))?;
|
||||
|
||||
let local_user_form = LocalUserForm {
|
||||
person_id,
|
||||
person_id: Some(person_id),
|
||||
email,
|
||||
password_encrypted,
|
||||
password_encrypted: Some(password_encrypted),
|
||||
show_nsfw: data.show_nsfw,
|
||||
show_bot_accounts: data.show_bot_accounts,
|
||||
show_scores: data.show_scores,
|
||||
@ -236,6 +260,7 @@ impl Perform for SaveUserSettings {
|
||||
show_read_posts: data.show_read_posts,
|
||||
show_new_post_notifs: data.show_new_post_notifs,
|
||||
send_notifications_to_email: data.send_notifications_to_email,
|
||||
email_verified: None,
|
||||
};
|
||||
|
||||
let local_user_res = blocking(context.pool(), move |conn| {
|
||||
@ -259,11 +284,11 @@ impl Perform for SaveUserSettings {
|
||||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
jwt: Some(Claims::jwt(
|
||||
updated_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?,
|
||||
)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -307,11 +332,11 @@ impl Perform for ChangePassword {
|
||||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
jwt: Some(Claims::jwt(
|
||||
updated_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?,
|
||||
)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -775,11 +800,11 @@ impl Perform for PasswordChange {
|
||||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
jwt: Some(Claims::jwt(
|
||||
updated_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?,
|
||||
)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -860,3 +885,36 @@ impl Perform for GetUnreadCount {
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for VerifyEmail {
|
||||
type Response = ();
|
||||
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
_websocket_id: Option<usize>,
|
||||
) -> Result<Self::Response, LemmyError> {
|
||||
let token = self.token.clone();
|
||||
let verification = blocking(context.pool(), move |conn| {
|
||||
EmailVerification::read_for_token(conn, &token)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| ApiError::err("token_not_found", e))?;
|
||||
|
||||
let form = LocalUserForm {
|
||||
// necessary in case this is a new signup
|
||||
email_verified: Some(true),
|
||||
// necessary in case email of an existing user was changed
|
||||
email: Some(Some(verification.email)),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
let local_user_id = verification.local_user_id;
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalUser::update(conn, local_user_id, &form)
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_api_common"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -13,11 +13,11 @@ path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_db_views = { version = "=0.14.1", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.1", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.1", path = "../db_views_actor" }
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "../db_schema" }
|
||||
lemmy_utils = { version = "=0.14.1", path = "../utils" }
|
||||
lemmy_db_views = { version = "=0.14.3", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.3", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.3", path = "../db_views_actor" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "../db_schema" }
|
||||
lemmy_utils = { version = "=0.14.3", path = "../utils" }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
diesel = "1.4.8"
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["cookies"] }
|
||||
|
@ -11,6 +11,12 @@ pub struct CreateComment {
|
||||
pub auth: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GetComment {
|
||||
pub id: CommentId,
|
||||
pub auth: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct EditComment {
|
||||
pub content: String,
|
||||
|
@ -23,6 +23,7 @@ pub struct Register {
|
||||
pub password: String,
|
||||
pub password_verify: String,
|
||||
pub show_nsfw: bool,
|
||||
/// email is mandatory if email verification is enabled on the server
|
||||
pub email: Option<String>,
|
||||
pub captcha_uuid: Option<String>,
|
||||
pub captcha_answer: Option<String>,
|
||||
@ -77,7 +78,9 @@ pub struct ChangePassword {
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct LoginResponse {
|
||||
pub jwt: String,
|
||||
/// This is None in response to `Register` if email verification is enabled, and in response to
|
||||
/// `DeleteAccount`.
|
||||
pub jwt: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -278,3 +281,8 @@ pub struct GetUnreadCountResponse {
|
||||
pub mentions: i64,
|
||||
pub private_messages: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct VerifyEmail {
|
||||
pub token: String,
|
||||
}
|
||||
|
@ -111,6 +111,7 @@ pub struct EditSite {
|
||||
pub open_registration: Option<bool>,
|
||||
pub enable_nsfw: Option<bool>,
|
||||
pub community_creation_admin_only: Option<bool>,
|
||||
pub require_email_verification: Option<bool>,
|
||||
pub auth: String,
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_api_crud"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -8,15 +8,15 @@ homepage = "https://join-lemmy.org/"
|
||||
documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
|
||||
[dependencies]
|
||||
lemmy_apub = { version = "=0.14.1", path = "../apub" }
|
||||
lemmy_apub_lib = { version = "=0.14.1", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.1", path = "../utils" }
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.1", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.1", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.1", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.1", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.14.1", path = "../websocket" }
|
||||
lemmy_apub = { version = "=0.14.3", path = "../apub" }
|
||||
lemmy_apub_lib = { version = "=0.14.3", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.3", path = "../utils" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.3", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.3", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.3", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.3", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.14.3", path = "../websocket" }
|
||||
diesel = "1.4.8"
|
||||
bcrypt = "0.10.1"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
|
@ -12,10 +12,39 @@ use lemmy_db_schema::{
|
||||
ListingType,
|
||||
SortType,
|
||||
};
|
||||
use lemmy_db_views::comment_view::CommentQueryBuilder;
|
||||
use lemmy_db_views::comment_view::{CommentQueryBuilder, CommentView};
|
||||
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for GetComment {
|
||||
type Response = CommentResponse;
|
||||
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<Self::Response, LemmyError> {
|
||||
let data = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?;
|
||||
|
||||
let person_id = local_user_view.map(|u| u.person.id);
|
||||
let id = data.id;
|
||||
let comment_view = blocking(context.pool(), move |conn| {
|
||||
CommentView::read(conn, id, person_id)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| ApiError::err("couldnt_find_comment", e))?;
|
||||
|
||||
Ok(Self::Response {
|
||||
comment_view,
|
||||
form_id: None,
|
||||
recipient_ids: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for GetComments {
|
||||
type Response = GetCommentsResponse;
|
||||
|
@ -108,6 +108,9 @@ pub async fn match_websocket_operation_crud(
|
||||
UserOperationCrud::RemoveComment => {
|
||||
do_websocket_operation::<RemoveComment>(context, id, op, data).await
|
||||
}
|
||||
UserOperationCrud::GetComment => {
|
||||
do_websocket_operation::<GetComment>(context, id, op, data).await
|
||||
}
|
||||
UserOperationCrud::GetComments => {
|
||||
do_websocket_operation::<GetComments>(context, id, op, data).await
|
||||
}
|
||||
|
@ -66,8 +66,8 @@ impl PerformCrud for CreateSite {
|
||||
enable_downvotes: data.enable_downvotes,
|
||||
open_registration: data.open_registration,
|
||||
enable_nsfw: data.enable_nsfw,
|
||||
updated: None,
|
||||
community_creation_admin_only: data.community_creation_admin_only,
|
||||
..SiteForm::default()
|
||||
};
|
||||
|
||||
let create_site = move |conn: &'_ _| Site::create(conn, &site_form);
|
||||
|
@ -45,7 +45,11 @@ impl PerformCrud for GetSite {
|
||||
captcha_answer: None,
|
||||
honeypot: None,
|
||||
};
|
||||
let login_response = register.perform(context, websocket_id).await?;
|
||||
let admin_jwt = register
|
||||
.perform(context, websocket_id)
|
||||
.await?
|
||||
.jwt
|
||||
.expect("jwt is returned from registration on newly created site");
|
||||
info!("Admin {} created", setup.admin_username);
|
||||
|
||||
let create_site = CreateSite {
|
||||
@ -58,7 +62,7 @@ impl PerformCrud for GetSite {
|
||||
open_registration: setup.open_registration,
|
||||
enable_nsfw: setup.enable_nsfw,
|
||||
community_creation_admin_only: setup.community_creation_admin_only,
|
||||
auth: login_response.jwt,
|
||||
auth: admin_jwt,
|
||||
};
|
||||
create_site.perform(context, websocket_id).await?;
|
||||
info!("Site {} created", setup.site_name);
|
||||
|
@ -59,6 +59,7 @@ impl PerformCrud for EditSite {
|
||||
open_registration: data.open_registration,
|
||||
enable_nsfw: data.enable_nsfw,
|
||||
community_creation_admin_only: data.community_creation_admin_only,
|
||||
require_email_verification: data.require_email_verification,
|
||||
};
|
||||
|
||||
let update_site = move |conn: &'_ _| Site::update(conn, 1, &site_form);
|
||||
|
@ -24,8 +24,6 @@ use lemmy_db_schema::{
|
||||
site::Site,
|
||||
},
|
||||
traits::{Crud, Followable, Joinable},
|
||||
ListingType,
|
||||
SortType,
|
||||
};
|
||||
use lemmy_db_views_actor::person_view::PersonViewSafe;
|
||||
use lemmy_utils::{
|
||||
@ -36,7 +34,7 @@ use lemmy_utils::{
|
||||
ConnectionId,
|
||||
LemmyError,
|
||||
};
|
||||
use lemmy_websocket::{messages::CheckCaptcha, LemmyContext};
|
||||
use lemmy_websocket::{email::send_verification_email, messages::CheckCaptcha, LemmyContext};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for Register {
|
||||
@ -49,16 +47,24 @@ impl PerformCrud for Register {
|
||||
) -> Result<LoginResponse, LemmyError> {
|
||||
let data: &Register = self;
|
||||
|
||||
// no email verification if the site is not setup yet
|
||||
let mut email_verification = false;
|
||||
|
||||
// Make sure site has open registration
|
||||
if let Ok(site) = blocking(context.pool(), Site::read_simple).await? {
|
||||
if !site.open_registration {
|
||||
return Err(ApiError::err_plain("registration_closed").into());
|
||||
}
|
||||
email_verification = site.require_email_verification;
|
||||
}
|
||||
|
||||
password_length_check(&data.password)?;
|
||||
honeypot_check(&data.honeypot)?;
|
||||
|
||||
if email_verification && data.email.is_none() {
|
||||
return Err(ApiError::err_plain("email_required").into());
|
||||
}
|
||||
|
||||
// Make sure passwords match
|
||||
if data.password != data.password_verify {
|
||||
return Err(ApiError::err_plain("passwords_dont_match").into());
|
||||
@ -124,22 +130,13 @@ impl PerformCrud for Register {
|
||||
.map_err(|e| ApiError::err("user_already_exists", e))?;
|
||||
|
||||
// Create the local user
|
||||
// TODO some of these could probably use the DB defaults
|
||||
let local_user_form = LocalUserForm {
|
||||
person_id: inserted_person.id,
|
||||
person_id: Some(inserted_person.id),
|
||||
email: Some(data.email.to_owned()),
|
||||
password_encrypted: data.password.to_owned(),
|
||||
password_encrypted: Some(data.password.to_owned()),
|
||||
show_nsfw: Some(data.show_nsfw),
|
||||
show_bot_accounts: Some(true),
|
||||
theme: Some("browser".into()),
|
||||
default_sort_type: Some(SortType::Active as i16),
|
||||
default_listing_type: Some(ListingType::Subscribed as i16),
|
||||
lang: Some("browser".into()),
|
||||
show_avatars: Some(true),
|
||||
show_scores: Some(true),
|
||||
show_read_posts: Some(true),
|
||||
show_new_post_notifs: Some(false),
|
||||
send_notifications_to_email: Some(false),
|
||||
email_verified: Some(!email_verification),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
|
||||
let inserted_local_user = match blocking(context.pool(), move |conn| {
|
||||
@ -228,13 +225,24 @@ impl PerformCrud for Register {
|
||||
.map_err(|e| ApiError::err("community_moderator_already_exists", e))?;
|
||||
}
|
||||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
// Log the user in directly if email verification is not required
|
||||
let jwt = if email_verification {
|
||||
send_verification_email(
|
||||
inserted_local_user.id,
|
||||
// we check at the beginning of this method that email is set
|
||||
&inserted_local_user.email.expect("email was provided"),
|
||||
&inserted_person.name,
|
||||
context,
|
||||
)
|
||||
.await?;
|
||||
None
|
||||
} else {
|
||||
Some(Claims::jwt(
|
||||
inserted_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?,
|
||||
})
|
||||
)?)
|
||||
};
|
||||
Ok(LoginResponse { jwt })
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,6 @@ impl PerformCrud for DeleteAccount {
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(LoginResponse {
|
||||
jwt: data.auth.to_owned(),
|
||||
})
|
||||
Ok(LoginResponse { jwt: None })
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_apub"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -13,13 +13,13 @@ path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.1", path = "../utils" }
|
||||
lemmy_apub_lib = { version = "=0.14.1", path = "../apub_lib" }
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.1", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.14.1", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.1", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.14.1", path = "../websocket" }
|
||||
lemmy_utils = { version = "=0.14.3", path = "../utils" }
|
||||
lemmy_apub_lib = { version = "=0.14.3", path = "../apub_lib" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.3", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.14.3", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.3", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.14.3", path = "../websocket" }
|
||||
diesel = "1.4.8"
|
||||
activitystreams-kinds = "0.1.2"
|
||||
bcrypt = "0.10.1"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_apub_lib"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -8,8 +8,8 @@ homepage = "https://join-lemmy.org/"
|
||||
documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.1", path = "../utils" }
|
||||
lemmy_apub_lib_derive = { version = "=0.14.1", path = "../apub_lib_derive" }
|
||||
lemmy_utils = { version = "=0.14.3", path = "../utils" }
|
||||
lemmy_apub_lib_derive = { version = "=0.14.3", path = "../apub_lib_derive" }
|
||||
activitystreams = "0.7.0-alpha.11"
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
async-trait = "0.1.51"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_apub_lib_derive"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_db_schema"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -11,8 +11,8 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.1", path = "../utils" }
|
||||
lemmy_apub_lib = { version = "=0.14.1", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.3", path = "../utils" }
|
||||
lemmy_apub_lib = { version = "=0.14.3", path = "../apub_lib" }
|
||||
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
|
||||
diesel_migrations = "1.4.0"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
|
@ -65,6 +65,7 @@ mod tests {
|
||||
enable_nsfw: None,
|
||||
updated: None,
|
||||
community_creation_admin_only: Some(false),
|
||||
require_email_verification: None,
|
||||
};
|
||||
|
||||
Site::create(&conn, &site_form).unwrap();
|
||||
|
39
crates/db_schema/src/impls/email_verification.rs
Normal file
39
crates/db_schema/src/impls/email_verification.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use crate::{source::email_verification::*, traits::Crud};
|
||||
use diesel::{insert_into, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
|
||||
|
||||
impl Crud for EmailVerification {
|
||||
type Form = EmailVerificationForm;
|
||||
type IdType = i32;
|
||||
fn create(conn: &PgConnection, form: &EmailVerificationForm) -> Result<Self, Error> {
|
||||
use crate::schema::email_verification::dsl::*;
|
||||
insert_into(email_verification)
|
||||
.values(form)
|
||||
.get_result::<Self>(conn)
|
||||
}
|
||||
|
||||
fn read(conn: &PgConnection, id_: i32) -> Result<Self, Error> {
|
||||
use crate::schema::email_verification::dsl::*;
|
||||
email_verification.find(id_).first::<Self>(conn)
|
||||
}
|
||||
|
||||
fn update(conn: &PgConnection, id_: i32, form: &EmailVerificationForm) -> Result<Self, Error> {
|
||||
use crate::schema::email_verification::dsl::*;
|
||||
diesel::update(email_verification.find(id_))
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
}
|
||||
|
||||
fn delete(conn: &PgConnection, id_: i32) -> Result<usize, Error> {
|
||||
use crate::schema::email_verification::dsl::*;
|
||||
diesel::delete(email_verification.find(id_)).execute(conn)
|
||||
}
|
||||
}
|
||||
|
||||
impl EmailVerification {
|
||||
pub fn read_for_token(conn: &PgConnection, token: &str) -> Result<Self, Error> {
|
||||
use crate::schema::email_verification::dsl::*;
|
||||
email_verification
|
||||
.filter(verification_token.eq(token))
|
||||
.first::<Self>(conn)
|
||||
}
|
||||
}
|
@ -62,8 +62,10 @@ mod safe_settings_type {
|
||||
impl LocalUser {
|
||||
pub fn register(conn: &PgConnection, form: &LocalUserForm) -> Result<Self, Error> {
|
||||
let mut edited_user = form.clone();
|
||||
let password_hash =
|
||||
hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
|
||||
let password_hash = form
|
||||
.password_encrypted
|
||||
.as_ref()
|
||||
.map(|p| hash(p, DEFAULT_COST).expect("Couldn't hash password"));
|
||||
edited_user.password_encrypted = password_hash;
|
||||
|
||||
Self::create(conn, &edited_user)
|
||||
|
@ -3,6 +3,7 @@ pub mod comment;
|
||||
pub mod comment_report;
|
||||
pub mod community;
|
||||
pub mod community_block;
|
||||
pub mod email_verification;
|
||||
pub mod local_user;
|
||||
pub mod moderator;
|
||||
pub mod password_reset_request;
|
||||
|
@ -93,8 +93,8 @@ mod tests {
|
||||
let inserted_person = Person::create(&conn, &new_person).unwrap();
|
||||
|
||||
let new_local_user = LocalUserForm {
|
||||
person_id: inserted_person.id,
|
||||
password_encrypted: "pass".to_string(),
|
||||
person_id: Some(inserted_person.id),
|
||||
password_encrypted: Some("pass".to_string()),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
|
||||
|
@ -157,6 +157,7 @@ table! {
|
||||
show_scores -> Bool,
|
||||
show_read_posts -> Bool,
|
||||
show_new_post_notifs -> Bool,
|
||||
email_verified -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
@ -447,6 +448,7 @@ table! {
|
||||
banner -> Nullable<Varchar>,
|
||||
description -> Nullable<Text>,
|
||||
community_creation_admin_only -> Bool,
|
||||
require_email_verification -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
@ -558,6 +560,15 @@ table! {
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
email_verification (id) {
|
||||
id -> Int4,
|
||||
local_user_id -> Int4,
|
||||
email -> Text,
|
||||
verification_token -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
joinable!(comment_alias_1 -> person_alias_1 (creator_id));
|
||||
joinable!(comment -> comment_alias_1 (parent_id));
|
||||
joinable!(person_mention -> person_alias_1 (recipient_id));
|
||||
@ -619,6 +630,7 @@ joinable!(post_saved -> person (person_id));
|
||||
joinable!(post_saved -> post (post_id));
|
||||
joinable!(site -> person (creator_id));
|
||||
joinable!(site_aggregates -> site (site_id));
|
||||
joinable!(email_verification -> local_user (local_user_id));
|
||||
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
activity,
|
||||
@ -662,4 +674,5 @@ allow_tables_to_appear_in_same_query!(
|
||||
comment_alias_1,
|
||||
person_alias_1,
|
||||
person_alias_2,
|
||||
email_verification
|
||||
);
|
||||
|
18
crates/db_schema/src/source/email_verification.rs
Normal file
18
crates/db_schema/src/source/email_verification.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use crate::{newtypes::LocalUserId, schema::email_verification};
|
||||
|
||||
#[derive(Queryable, Identifiable, Clone)]
|
||||
#[table_name = "email_verification"]
|
||||
pub struct EmailVerification {
|
||||
pub id: i32,
|
||||
pub local_user_id: LocalUserId,
|
||||
pub email: String,
|
||||
pub verification_code: String,
|
||||
}
|
||||
|
||||
#[derive(Insertable, AsChangeset)]
|
||||
#[table_name = "email_verification"]
|
||||
pub struct EmailVerificationForm {
|
||||
pub local_user_id: LocalUserId,
|
||||
pub email: String,
|
||||
pub verification_token: String,
|
||||
}
|
@ -23,14 +23,15 @@ pub struct LocalUser {
|
||||
pub show_scores: bool,
|
||||
pub show_read_posts: bool,
|
||||
pub show_new_post_notifs: bool,
|
||||
pub email_verified: bool,
|
||||
}
|
||||
|
||||
// TODO redo these, check table defaults
|
||||
#[derive(Insertable, AsChangeset, Clone, Default)]
|
||||
#[table_name = "local_user"]
|
||||
pub struct LocalUserForm {
|
||||
pub person_id: PersonId,
|
||||
pub password_encrypted: String,
|
||||
pub person_id: Option<PersonId>,
|
||||
pub password_encrypted: Option<String>,
|
||||
pub email: Option<Option<String>>,
|
||||
pub show_nsfw: Option<bool>,
|
||||
pub theme: Option<String>,
|
||||
@ -43,6 +44,7 @@ pub struct LocalUserForm {
|
||||
pub show_scores: Option<bool>,
|
||||
pub show_read_posts: Option<bool>,
|
||||
pub show_new_post_notifs: Option<bool>,
|
||||
pub email_verified: Option<bool>,
|
||||
}
|
||||
|
||||
/// A local user view that removes password encrypted
|
||||
|
@ -3,6 +3,7 @@ pub mod comment;
|
||||
pub mod comment_report;
|
||||
pub mod community;
|
||||
pub mod community_block;
|
||||
pub mod email_verification;
|
||||
pub mod local_user;
|
||||
pub mod moderator;
|
||||
pub mod password_reset_request;
|
||||
|
@ -20,9 +20,10 @@ pub struct Site {
|
||||
pub banner: Option<DbUrl>,
|
||||
pub description: Option<String>,
|
||||
pub community_creation_admin_only: bool,
|
||||
pub require_email_verification: bool,
|
||||
}
|
||||
|
||||
#[derive(Insertable, AsChangeset)]
|
||||
#[derive(Insertable, AsChangeset, Default)]
|
||||
#[table_name = "site"]
|
||||
pub struct SiteForm {
|
||||
pub name: String,
|
||||
@ -37,4 +38,5 @@ pub struct SiteForm {
|
||||
pub banner: Option<Option<DbUrl>>,
|
||||
pub description: Option<Option<String>>,
|
||||
pub community_creation_admin_only: Option<bool>,
|
||||
pub require_email_verification: Option<bool>,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_db_views"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -11,7 +11,7 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "../db_schema" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "../db_schema" }
|
||||
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
tracing = "0.1.29"
|
||||
|
@ -147,22 +147,28 @@ impl CommentReportView {
|
||||
let mut query = comment_report::table
|
||||
.inner_join(comment::table)
|
||||
.inner_join(post::table.on(comment::post_id.eq(post::id)))
|
||||
.inner_join(
|
||||
community_moderator::table.on(community_moderator::community_id.eq(post::community_id)),
|
||||
)
|
||||
.filter(comment_report::resolved.eq(false))
|
||||
.into_boxed();
|
||||
|
||||
// If its not an admin, get only the ones you mod
|
||||
if !admin {
|
||||
query = query.filter(community_moderator::person_id.eq(my_person_id));
|
||||
}
|
||||
|
||||
if let Some(community_id) = community_id {
|
||||
query = query.filter(post::community_id.eq(community_id))
|
||||
}
|
||||
|
||||
query.select(count(comment_report::id)).first::<i64>(conn)
|
||||
// If its not an admin, get only the ones you mod
|
||||
if !admin {
|
||||
query
|
||||
.inner_join(
|
||||
community_moderator::table.on(
|
||||
community_moderator::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_moderator::person_id.eq(my_person_id)),
|
||||
),
|
||||
)
|
||||
.select(count(comment_report::id))
|
||||
.first::<i64>(conn)
|
||||
} else {
|
||||
query.select(count(comment_report::id)).first::<i64>(conn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,10 +222,6 @@ impl<'a> CommentReportQueryBuilder<'a> {
|
||||
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||
.inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
|
||||
.inner_join(person_alias_1::table.on(comment::creator_id.eq(person_alias_1::id)))
|
||||
// Test this join
|
||||
.inner_join(
|
||||
community_moderator::table.on(community_moderator::community_id.eq(post::community_id)),
|
||||
)
|
||||
.inner_join(
|
||||
comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)),
|
||||
)
|
||||
@ -254,11 +256,6 @@ impl<'a> CommentReportQueryBuilder<'a> {
|
||||
))
|
||||
.into_boxed();
|
||||
|
||||
// If its not an admin, get only the ones you mod
|
||||
if !self.admin {
|
||||
query = query.filter(community_moderator::person_id.eq(self.my_person_id));
|
||||
}
|
||||
|
||||
if let Some(community_id) = self.community_id {
|
||||
query = query.filter(post::community_id.eq(community_id));
|
||||
}
|
||||
@ -269,11 +266,25 @@ impl<'a> CommentReportQueryBuilder<'a> {
|
||||
|
||||
let (limit, offset) = limit_and_offset(self.page, self.limit);
|
||||
|
||||
let res = query
|
||||
.order_by(comment_report::published.asc())
|
||||
query = query
|
||||
.order_by(comment_report::published.desc())
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.load::<CommentReportViewTuple>(self.conn)?;
|
||||
.offset(offset);
|
||||
|
||||
// If its not an admin, get only the ones you mod
|
||||
let res = if !self.admin {
|
||||
query
|
||||
.inner_join(
|
||||
community_moderator::table.on(
|
||||
community_moderator::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_moderator::person_id.eq(self.my_person_id)),
|
||||
),
|
||||
)
|
||||
.load::<CommentReportViewTuple>(self.conn)?
|
||||
} else {
|
||||
query.load::<CommentReportViewTuple>(self.conn)?
|
||||
};
|
||||
|
||||
Ok(CommentReportView::from_tuple_to_vec(res))
|
||||
}
|
||||
@ -498,8 +509,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
reports,
|
||||
[
|
||||
expected_sara_report_view.to_owned(),
|
||||
expected_jessica_report_view.to_owned()
|
||||
expected_jessica_report_view.to_owned(),
|
||||
expected_sara_report_view.to_owned()
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -131,23 +131,28 @@ impl PostReportView {
|
||||
use diesel::dsl::*;
|
||||
let mut query = post_report::table
|
||||
.inner_join(post::table)
|
||||
// Test this join
|
||||
.inner_join(
|
||||
community_moderator::table.on(community_moderator::community_id.eq(post::community_id)),
|
||||
)
|
||||
.filter(post_report::resolved.eq(false))
|
||||
.into_boxed();
|
||||
|
||||
// If its not an admin, get only the ones you mod
|
||||
if !admin {
|
||||
query = query.filter(community_moderator::person_id.eq(my_person_id));
|
||||
}
|
||||
|
||||
if let Some(community_id) = community_id {
|
||||
query = query.filter(post::community_id.eq(community_id))
|
||||
}
|
||||
|
||||
query.select(count(post_report::id)).first::<i64>(conn)
|
||||
// If its not an admin, get only the ones you mod
|
||||
if !admin {
|
||||
query
|
||||
.inner_join(
|
||||
community_moderator::table.on(
|
||||
community_moderator::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_moderator::person_id.eq(my_person_id)),
|
||||
),
|
||||
)
|
||||
.select(count(post_report::id))
|
||||
.first::<i64>(conn)
|
||||
} else {
|
||||
query.select(count(post_report::id)).first::<i64>(conn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,9 +205,6 @@ impl<'a> PostReportQueryBuilder<'a> {
|
||||
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||
.inner_join(person::table.on(post_report::creator_id.eq(person::id)))
|
||||
.inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id)))
|
||||
.inner_join(
|
||||
community_moderator::table.on(community_moderator::community_id.eq(post::community_id)),
|
||||
)
|
||||
.left_join(
|
||||
community_person_ban::table.on(
|
||||
post::community_id
|
||||
@ -234,11 +236,6 @@ impl<'a> PostReportQueryBuilder<'a> {
|
||||
))
|
||||
.into_boxed();
|
||||
|
||||
// If its not an admin, get only the ones you mod
|
||||
if !self.admin {
|
||||
query = query.filter(community_moderator::person_id.eq(self.my_person_id));
|
||||
}
|
||||
|
||||
if let Some(community_id) = self.community_id {
|
||||
query = query.filter(post::community_id.eq(community_id));
|
||||
}
|
||||
@ -249,11 +246,25 @@ impl<'a> PostReportQueryBuilder<'a> {
|
||||
|
||||
let (limit, offset) = limit_and_offset(self.page, self.limit);
|
||||
|
||||
let res = query
|
||||
.order_by(post_report::published.asc())
|
||||
query = query
|
||||
.order_by(post_report::published.desc())
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.load::<PostReportViewTuple>(self.conn)?;
|
||||
.offset(offset);
|
||||
|
||||
// If its not an admin, get only the ones you mod
|
||||
let res = if !self.admin {
|
||||
query
|
||||
.inner_join(
|
||||
community_moderator::table.on(
|
||||
community_moderator::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_moderator::person_id.eq(self.my_person_id)),
|
||||
),
|
||||
)
|
||||
.load::<PostReportViewTuple>(self.conn)?
|
||||
} else {
|
||||
query.load::<PostReportViewTuple>(self.conn)?
|
||||
};
|
||||
|
||||
Ok(PostReportView::from_tuple_to_vec(res))
|
||||
}
|
||||
@ -481,8 +492,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
reports,
|
||||
[
|
||||
expected_sara_report_view.to_owned(),
|
||||
expected_jessica_report_view.to_owned()
|
||||
expected_jessica_report_view.to_owned(),
|
||||
expected_sara_report_view.to_owned()
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_db_views_actor"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -11,6 +11,6 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "../db_schema" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "../db_schema" }
|
||||
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_db_views_moderator"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -11,6 +11,6 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "../db_schema" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "../db_schema" }
|
||||
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_routes"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -11,13 +11,13 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.1", path = "../utils" }
|
||||
lemmy_websocket = { version = "=0.14.1", path = "../websocket" }
|
||||
lemmy_db_views = { version = "=0.14.1", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.14.1", path = "../db_views_actor" }
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "../db_schema" }
|
||||
lemmy_api_common = { version = "=0.14.1", path = "../api_common" }
|
||||
lemmy_apub = { version = "=0.14.1", path = "../apub" }
|
||||
lemmy_utils = { version = "=0.14.3", path = "../utils" }
|
||||
lemmy_websocket = { version = "=0.14.3", path = "../websocket" }
|
||||
lemmy_db_views = { version = "=0.14.3", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.14.3", path = "../db_views_actor" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "../db_schema" }
|
||||
lemmy_api_common = { version = "=0.14.3", path = "../api_common" }
|
||||
lemmy_apub = { version = "=0.14.3", path = "../apub" }
|
||||
diesel = "1.4.8"
|
||||
actix = "0.12.0"
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["rustls"] }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_utils"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -13,7 +13,7 @@ static MENTIONS_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
static VALID_ACTOR_NAME_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^[a-zA-Z0-9_]{3,}$").expect("compile regex"));
|
||||
static VALID_POST_TITLE_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r".*\S.*").expect("compile regex"));
|
||||
Lazy::new(|| Regex::new(r".*\S{3,}.*").expect("compile regex"));
|
||||
static VALID_MATRIX_ID_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(r"^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").expect("compile regex")
|
||||
});
|
||||
@ -120,8 +120,14 @@ pub fn scrape_text_for_mentions(text: &str) -> Vec<MentionData> {
|
||||
out.into_iter().unique().collect()
|
||||
}
|
||||
|
||||
fn has_newline(name: &str) -> bool {
|
||||
name.contains('\n')
|
||||
}
|
||||
|
||||
pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> bool {
|
||||
name.chars().count() <= actor_name_max_length && VALID_ACTOR_NAME_REGEX.is_match(name)
|
||||
name.chars().count() <= actor_name_max_length
|
||||
&& VALID_ACTOR_NAME_REGEX.is_match(name)
|
||||
&& !has_newline(name)
|
||||
}
|
||||
|
||||
// Can't do a regex here, reverse lookarounds not supported
|
||||
@ -130,14 +136,15 @@ pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> bool {
|
||||
&& !name.starts_with('\u{200b}')
|
||||
&& name.chars().count() >= 3
|
||||
&& name.chars().count() <= actor_name_max_length
|
||||
&& !has_newline(name)
|
||||
}
|
||||
|
||||
pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
|
||||
VALID_MATRIX_ID_REGEX.is_match(matrix_id)
|
||||
VALID_MATRIX_ID_REGEX.is_match(matrix_id) && !has_newline(matrix_id)
|
||||
}
|
||||
|
||||
pub fn is_valid_post_title(title: &str) -> bool {
|
||||
VALID_POST_TITLE_REGEX.is_match(title)
|
||||
VALID_POST_TITLE_REGEX.is_match(title) && !has_newline(title)
|
||||
}
|
||||
|
||||
pub fn get_ip(conn_info: &ConnectionInfo) -> IpAddr {
|
||||
@ -166,7 +173,7 @@ pub fn clean_url_params(mut url: Url) -> Url {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::utils::clean_url_params;
|
||||
use crate::utils::{clean_url_params, is_valid_post_title};
|
||||
use url::Url;
|
||||
|
||||
#[test]
|
||||
@ -180,4 +187,13 @@ mod tests {
|
||||
let cleaned = clean_url_params(url.clone());
|
||||
assert_eq!(url.to_string(), cleaned.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regex_checks() {
|
||||
assert!(!is_valid_post_title("hi"));
|
||||
assert!(is_valid_post_title("him"));
|
||||
assert!(!is_valid_post_title("n\n\n\n\nanother"));
|
||||
assert!(!is_valid_post_title("hello there!\n this is a test."));
|
||||
assert!(is_valid_post_title("hello there! this is a test."));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lemmy_websocket"
|
||||
version = "0.14.1"
|
||||
version = "0.14.3"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
@ -13,11 +13,11 @@ path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.1", path = "../utils" }
|
||||
lemmy_api_common = { version = "=0.14.1", path = "../api_common" }
|
||||
lemmy_db_schema = { version = "=0.14.1", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.1", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.14.1", path = "../db_views_actor" }
|
||||
lemmy_utils = { version = "=0.14.3", path = "../utils" }
|
||||
lemmy_api_common = { version = "=0.14.3", path = "../api_common" }
|
||||
lemmy_db_schema = { version = "=0.14.3", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.3", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.14.3", path = "../db_views_actor" }
|
||||
reqwest = { version = "0.11.4", features = ["json"] }
|
||||
tracing = "0.1.29"
|
||||
rand = "0.8.4"
|
||||
|
46
crates/websocket/src/email.rs
Normal file
46
crates/websocket/src/email.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use crate::LemmyContext;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::LocalUserId,
|
||||
source::email_verification::{EmailVerification, EmailVerificationForm},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_utils::{email::send_email, utils::generate_random_string, ApiError, LemmyError};
|
||||
|
||||
pub async fn send_verification_email(
|
||||
local_user_id: LocalUserId,
|
||||
new_email: &str,
|
||||
username: &str,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError> {
|
||||
let settings = context.settings();
|
||||
let form = EmailVerificationForm {
|
||||
local_user_id,
|
||||
email: new_email.to_string(),
|
||||
verification_token: generate_random_string(),
|
||||
};
|
||||
// TODO: link should be replaced with a frontend route once that exists
|
||||
let verify_link = format!(
|
||||
"{}/api/v3/user/verify_email?token={}",
|
||||
settings.get_protocol_and_hostname(),
|
||||
&form.verification_token
|
||||
);
|
||||
blocking(context.pool(), move |conn| {
|
||||
EmailVerification::create(conn, &form)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let subject = format!("Verify your email address for {}", settings.hostname);
|
||||
let body = format!(
|
||||
concat!(
|
||||
"Please click the link below to verify your email address ",
|
||||
"for the account @{}@{}. Ignore this email if the account isn't yours.\n\n",
|
||||
"<a href=\"{}\"></a>"
|
||||
),
|
||||
username, settings.hostname, verify_link
|
||||
);
|
||||
send_email(&subject, new_email, username, &body, &context.settings())
|
||||
.map_err(|e| ApiError::err("email_send_failed", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -10,6 +10,7 @@ use reqwest::Client;
|
||||
use serde::Serialize;
|
||||
|
||||
pub mod chat_server;
|
||||
pub mod email;
|
||||
pub mod handlers;
|
||||
pub mod messages;
|
||||
pub mod routes;
|
||||
@ -169,6 +170,7 @@ pub enum UserOperationCrud {
|
||||
RemovePost,
|
||||
// Comment
|
||||
CreateComment,
|
||||
GetComment,
|
||||
GetComments,
|
||||
EditComment,
|
||||
DeleteComment,
|
||||
|
@ -26,7 +26,7 @@ services:
|
||||
- postgres
|
||||
|
||||
lemmy-ui:
|
||||
image: dessalines/lemmy-ui:0.14.1
|
||||
image: dessalines/lemmy-ui:0.14.3
|
||||
restart: always
|
||||
environment:
|
||||
- LEMMY_INTERNAL_HOST=lemmy:8536
|
||||
|
@ -28,7 +28,7 @@ services:
|
||||
- ./volumes/pictrs_alpha:/mnt
|
||||
|
||||
lemmy-alpha-ui:
|
||||
image: dessalines/lemmy-ui:0.14.1
|
||||
image: dessalines/lemmy-ui:0.14.3
|
||||
environment:
|
||||
- LEMMY_INTERNAL_HOST=lemmy-alpha:8541
|
||||
- LEMMY_EXTERNAL_HOST=localhost:8541
|
||||
@ -57,7 +57,7 @@ services:
|
||||
- ./volumes/postgres_alpha:/var/lib/postgresql/data
|
||||
|
||||
lemmy-beta-ui:
|
||||
image: dessalines/lemmy-ui:0.14.1
|
||||
image: dessalines/lemmy-ui:0.14.3
|
||||
environment:
|
||||
- LEMMY_INTERNAL_HOST=lemmy-beta:8551
|
||||
- LEMMY_EXTERNAL_HOST=localhost:8551
|
||||
@ -86,7 +86,7 @@ services:
|
||||
- ./volumes/postgres_beta:/var/lib/postgresql/data
|
||||
|
||||
lemmy-gamma-ui:
|
||||
image: dessalines/lemmy-ui:0.14.1
|
||||
image: dessalines/lemmy-ui:0.14.3
|
||||
environment:
|
||||
- LEMMY_INTERNAL_HOST=lemmy-gamma:8561
|
||||
- LEMMY_EXTERNAL_HOST=localhost:8561
|
||||
@ -116,7 +116,7 @@ services:
|
||||
|
||||
# An instance with only an allowlist for beta
|
||||
lemmy-delta-ui:
|
||||
image: dessalines/lemmy-ui:0.14.1
|
||||
image: dessalines/lemmy-ui:0.14.3
|
||||
environment:
|
||||
- LEMMY_INTERNAL_HOST=lemmy-delta:8571
|
||||
- LEMMY_EXTERNAL_HOST=localhost:8571
|
||||
@ -146,7 +146,7 @@ services:
|
||||
|
||||
# An instance who has a blocklist, with lemmy-alpha blocked
|
||||
lemmy-epsilon-ui:
|
||||
image: dessalines/lemmy-ui:0.14.1
|
||||
image: dessalines/lemmy-ui:0.14.3
|
||||
environment:
|
||||
- LEMMY_INTERNAL_HOST=lemmy-epsilon:8581
|
||||
- LEMMY_EXTERNAL_HOST=localhost:8581
|
||||
|
@ -12,7 +12,7 @@ services:
|
||||
restart: always
|
||||
|
||||
lemmy:
|
||||
image: dessalines/lemmy:0.14.1
|
||||
image: dessalines/lemmy:0.14.3
|
||||
ports:
|
||||
- "127.0.0.1:8536:8536"
|
||||
restart: always
|
||||
@ -25,7 +25,7 @@ services:
|
||||
- pictrs
|
||||
|
||||
lemmy-ui:
|
||||
image: dessalines/lemmy-ui:0.14.1
|
||||
image: dessalines/lemmy-ui:0.14.3
|
||||
ports:
|
||||
- "127.0.0.1:1235:1234"
|
||||
restart: always
|
||||
|
@ -6,13 +6,13 @@ delete from activity where ap_id is null;
|
||||
alter table activity alter column ap_id set not null;
|
||||
|
||||
-- Delete dupes, keeping the first one
|
||||
delete
|
||||
from activity
|
||||
where id not in (
|
||||
select min(id)
|
||||
delete from activity a using (
|
||||
select min(id) as id, ap_id
|
||||
from activity
|
||||
group by ap_id
|
||||
);
|
||||
group by ap_id having count(*) > 1
|
||||
) b
|
||||
where a.ap_id = b.ap_id
|
||||
and a.id <> b.id;
|
||||
|
||||
-- The index
|
||||
create unique index idx_activity_ap_id on activity(ap_id);
|
||||
|
@ -0,0 +1,2 @@
|
||||
drop index idx_comment_report_published;
|
||||
drop index idx_post_report_published;
|
@ -0,0 +1,2 @@
|
||||
create index idx_comment_report_published on comment_report (published desc);
|
||||
create index idx_post_report_published on post_report (published desc);
|
8
migrations/2021-11-23-132840_email_verification/down.sql
Normal file
8
migrations/2021-11-23-132840_email_verification/down.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- revert defaults from db for local user init
|
||||
alter table local_user alter column theme set default 'darkly';
|
||||
alter table local_user alter column default_listing_type set default 1;
|
||||
|
||||
-- remove tables and columns for optional email verification
|
||||
alter table site drop column require_email_verification;
|
||||
alter table local_user drop column email_verified;
|
||||
drop table email_verification;
|
15
migrations/2021-11-23-132840_email_verification/up.sql
Normal file
15
migrations/2021-11-23-132840_email_verification/up.sql
Normal file
@ -0,0 +1,15 @@
|
||||
-- use defaults from db for local user init
|
||||
alter table local_user alter column theme set default 'browser';
|
||||
alter table local_user alter column default_listing_type set default 2;
|
||||
|
||||
-- add tables and columns for optional email verification
|
||||
alter table site add column require_email_verification boolean not null default false;
|
||||
alter table local_user add column email_verified boolean not null default false;
|
||||
|
||||
create table email_verification (
|
||||
id serial primary key,
|
||||
local_user_id int references local_user(id) on update cascade on delete cascade not null,
|
||||
email text not null,
|
||||
verification_token text not null
|
||||
|
||||
);
|
@ -115,6 +115,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
|
||||
.service(
|
||||
web::scope("/comment")
|
||||
.wrap(rate_limit.message())
|
||||
.route("", web::get().to(route_get_crud::<GetComment>))
|
||||
.route("", web::put().to(route_post_crud::<EditComment>))
|
||||
.route("/delete", web::post().to(route_post_crud::<DeleteComment>))
|
||||
.route("/remove", web::post().to(route_post_crud::<RemoveComment>))
|
||||
@ -204,7 +205,9 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
|
||||
web::put().to(route_post::<ChangePassword>),
|
||||
)
|
||||
.route("/report_count", web::get().to(route_get::<GetReportCount>))
|
||||
.route("/unread_count", web::get().to(route_get::<GetUnreadCount>)),
|
||||
.route("/unread_count", web::get().to(route_get::<GetUnreadCount>))
|
||||
// TODO: currently GET for easier testing, but should probably be POST
|
||||
.route("/verify_email", web::get().to(route_get::<VerifyEmail>)),
|
||||
)
|
||||
// Admin Actions
|
||||
.service(
|
||||
|
Loading…
Reference in New Issue
Block a user