mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-02-27 08:06:42 +00:00
Merge from main.
This commit is contained in:
parent
442c69b270
commit
c294992699
83 changed files with 1919 additions and 884 deletions
552
Cargo.lock
generated
552
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
58
Cargo.toml
58
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_server"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -31,43 +31,43 @@ members = [
|
|||
]
|
||||
|
||||
[dependencies]
|
||||
lemmy_api = { version = "=0.14.4-rc.4", path = "./crates/api" }
|
||||
lemmy_api_crud = { version = "=0.14.4-rc.4", path = "./crates/api_crud" }
|
||||
lemmy_apub = { version = "=0.14.4-rc.4", path = "./crates/apub" }
|
||||
lemmy_apub_lib = { version = "=0.14.4-rc.4", path = "./crates/apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.4-rc.4", path = "./crates/utils" }
|
||||
lemmy_db_schema = { version = "=0.14.4-rc.4", path = "./crates/db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.4-rc.4", path = "./crates/db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.4-rc.4", path = "./crates/db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.4-rc.4", path = "./crates/db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.4-rc.4", path = "crates/api_common" }
|
||||
lemmy_websocket = { version = "=0.14.4-rc.4", path = "./crates/websocket" }
|
||||
lemmy_routes = { version = "=0.14.4-rc.4", path = "./crates/routes" }
|
||||
lemmy_api = { version = "=0.15.0-rc.6", path = "./crates/api" }
|
||||
lemmy_api_crud = { version = "=0.15.0-rc.6", path = "./crates/api_crud" }
|
||||
lemmy_apub = { version = "=0.15.0-rc.6", path = "./crates/apub" }
|
||||
lemmy_apub_lib = { version = "=0.15.0-rc.6", path = "./crates/apub_lib" }
|
||||
lemmy_utils = { version = "=0.15.0-rc.6", path = "./crates/utils" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "./crates/db_schema" }
|
||||
lemmy_db_views = { version = "=0.15.0-rc.6", path = "./crates/db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.15.0-rc.6", path = "./crates/db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.15.0-rc.6", path = "./crates/db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.15.0-rc.6", path = "crates/api_common" }
|
||||
lemmy_websocket = { version = "=0.15.0-rc.6", path = "./crates/websocket" }
|
||||
lemmy_routes = { version = "=0.15.0-rc.6", path = "./crates/routes" }
|
||||
diesel = "1.4.8"
|
||||
diesel_migrations = "1.4.0"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
actix = "0.12.0"
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["rustls"] }
|
||||
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["rustls"] }
|
||||
tracing = "0.1.29"
|
||||
tracing-actix-web = { version = "0.5.0-beta.3", default-features = false }
|
||||
tracing-actix-web = { version = "0.5.0-beta.5", default-features = false }
|
||||
tracing-error = "0.2.0"
|
||||
tracing-log = "0.1.2"
|
||||
tracing-subscriber = { version = "0.3.2", features = ["env-filter"] }
|
||||
strum = "0.21.0"
|
||||
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }
|
||||
strum = "0.23.0"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
openssl = "0.10.36"
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.10", default-features = false, features = ["sha-2"] }
|
||||
tokio = { version = "1.12.0", features = ["sync"] }
|
||||
anyhow = "1.0.44"
|
||||
reqwest = { version = "0.11.4", features = ["json"] }
|
||||
reqwest-middleware = "0.1.2"
|
||||
reqwest-tracing = { version = "0.2.0", features = ["opentelemetry_0_16"] }
|
||||
activitystreams = "0.7.0-alpha.11"
|
||||
actix-rt = { version = "2.2.0", default-features = false }
|
||||
serde_json = { version = "1.0.68", features = ["preserve_order"] }
|
||||
openssl = "0.10.38"
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.14", default-features = false, features = ["sha-2"] }
|
||||
tokio = { version = "1.14.0", features = ["sync"] }
|
||||
anyhow = "1.0.51"
|
||||
reqwest = { version = "0.11.7", features = ["json"] }
|
||||
reqwest-middleware = "0.1.3"
|
||||
reqwest-tracing = "0.2.0"
|
||||
activitystreams = "0.7.0-alpha.14"
|
||||
actix-rt = { version = "2.5.0", default-features = false }
|
||||
serde_json = { version = "1.0.72", features = ["preserve_order"] }
|
||||
clokwerk = "0.3.5"
|
||||
doku = "0.10.1"
|
||||
doku = "0.10.2"
|
||||
opentelemetry = { version = "0.16", features = ["rt-tokio"] }
|
||||
opentelemetry-otlp = "0.9"
|
||||
tracing-opentelemetry = "0.16"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
0.13.3
|
|
@ -1,53 +0,0 @@
|
|||
version: '2.2'
|
||||
|
||||
services:
|
||||
lemmy:
|
||||
image: {{ lemmy_docker_image }}
|
||||
ports:
|
||||
- "127.0.0.1:8536:8536"
|
||||
restart: always
|
||||
environment:
|
||||
- RUST_LOG="warn,lemmy_server=info,lemmy_api=info,lemmy_api_common=info,lemmy_api_crud=info,lemmy_apub=info,lemmy_db_schema=info,lemmy_db_views=info,lemmy_db_views_actor=info,lemmy_db_views_moderator=info,lemmy_routes=info,lemmy_utils=info,lemmy_websocket=info"
|
||||
volumes:
|
||||
- ./lemmy.hjson:/config/config.hjson:ro
|
||||
depends_on:
|
||||
- postgres
|
||||
- pictrs
|
||||
|
||||
lemmy-ui:
|
||||
image: {{ lemmy_docker_ui_image }}
|
||||
ports:
|
||||
- "127.0.0.1:1235:1234"
|
||||
restart: always
|
||||
environment:
|
||||
- LEMMY_INTERNAL_HOST=lemmy:8536
|
||||
- LEMMY_EXTERNAL_HOST={{ domain }}
|
||||
- LEMMY_HTTPS=true
|
||||
depends_on:
|
||||
- lemmy
|
||||
|
||||
postgres:
|
||||
image: postgres:12-alpine
|
||||
environment:
|
||||
- POSTGRES_USER=lemmy
|
||||
- POSTGRES_PASSWORD={{ postgres_password }}
|
||||
- POSTGRES_DB=lemmy
|
||||
volumes:
|
||||
- ./volumes/postgres:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
pictrs:
|
||||
image: asonix/pictrs:v0.2.6-r2
|
||||
user: 991:991
|
||||
ports:
|
||||
- "127.0.0.1:8537:8080"
|
||||
volumes:
|
||||
- ./volumes/pictrs:/mnt
|
||||
restart: always
|
||||
mem_limit: 200m
|
||||
|
||||
postfix:
|
||||
image: mwader/postfix-relay
|
||||
environment:
|
||||
- POSTFIX_myhostname={{ domain }}
|
||||
restart: "always"
|
|
@ -649,7 +649,7 @@ export function wrapper(form: any): string {
|
|||
|
||||
export function randomString(length: number): string {
|
||||
var result = '';
|
||||
var characters = 'abcdefghijklmnopqrstuvwxyz0123456789_';
|
||||
var characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_';
|
||||
var charactersLength = characters.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
|
|
|
@ -83,8 +83,8 @@
|
|||
setup: {
|
||||
# Username for the admin user
|
||||
admin_username: "admin"
|
||||
# Password for the admin user
|
||||
admin_password: "my_passwd"
|
||||
# Password for the admin user. It must be at least 10 characters.
|
||||
admin_password: "my_passwd_longer_than_ten_characters"
|
||||
# Name of the site (can be changed later)
|
||||
site_name: "My Lemmy Instance"
|
||||
# Email for the admin user (optional, can be omitted and set later through the website)
|
||||
|
@ -97,6 +97,10 @@
|
|||
open_registration: true
|
||||
enable_nsfw: true
|
||||
community_creation_admin_only: true
|
||||
require_email_verification: true
|
||||
require_application: true
|
||||
application_question: "string"
|
||||
private_instance: true
|
||||
}
|
||||
# the domain name of your instance (mandatory)
|
||||
hostname: "unset"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_api"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -13,40 +13,40 @@ path = "src/lib.rs"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_apub = { version = "=0.14.4-rc.4", path = "../apub" }
|
||||
lemmy_apub_lib = { version = "=0.14.4-rc.4", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.4-rc.4", path = "../utils" }
|
||||
lemmy_db_schema = { version = "=0.14.4-rc.4", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.4-rc.4", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.4-rc.4", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.4-rc.4", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.4-rc.4", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.14.4-rc.4", path = "../websocket" }
|
||||
lemmy_apub = { version = "=0.15.0-rc.6", path = "../apub" }
|
||||
lemmy_apub_lib = { version = "=0.15.0-rc.6", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.15.0-rc.6", path = "../utils" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.15.0-rc.6", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.15.0-rc.6", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.15.0-rc.6", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.15.0-rc.6", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.15.0-rc.6", path = "../websocket" }
|
||||
diesel = "1.4.8"
|
||||
bcrypt = "0.10.1"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
serde_json = { version = "1.0.68", features = ["preserve_order"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde_json = { version = "1.0.72", features = ["preserve_order"] }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
actix = "0.12.0"
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false }
|
||||
actix-rt = { version = "2.2.0", default-features = false }
|
||||
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||
actix-rt = { version = "2.5.0", default-features = false }
|
||||
rand = "0.8.4"
|
||||
strum = "0.21.0"
|
||||
strum_macros = "0.21.1"
|
||||
strum = "0.23.0"
|
||||
strum_macros = "0.23.1"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
openssl = "0.10.36"
|
||||
openssl = "0.10.38"
|
||||
http = "0.2.5"
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.10", default-features = false, features = ["sha-2"] }
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.14", default-features = false, features = ["sha-2"] }
|
||||
base64 = "0.13.0"
|
||||
tokio = "1.12.0"
|
||||
futures = "0.3.17"
|
||||
itertools = "0.10.1"
|
||||
tokio = "1.14.0"
|
||||
futures = "0.3.18"
|
||||
itertools = "0.10.3"
|
||||
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||
sha2 = "0.9.8"
|
||||
async-trait = "0.1.51"
|
||||
sha2 = "0.10.0"
|
||||
async-trait = "0.1.52"
|
||||
captcha = "0.0.8"
|
||||
anyhow = "1.0.44"
|
||||
thiserror = "1.0.29"
|
||||
anyhow = "1.0.51"
|
||||
thiserror = "1.0.30"
|
||||
tracing = "0.1.29"
|
||||
background-jobs = "0.11.0"
|
||||
reqwest = { version = "0.11.4", features = ["json"] }
|
||||
reqwest = { version = "0.11.7", features = ["json"] }
|
||||
|
|
|
@ -38,6 +38,15 @@ pub async fn match_websocket_operation(
|
|||
UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
|
||||
UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
|
||||
UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
|
||||
UserOperation::GetUnreadRegistrationApplicationCount => {
|
||||
do_websocket_operation::<GetUnreadRegistrationApplicationCount>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::ListRegistrationApplications => {
|
||||
do_websocket_operation::<ListRegistrationApplications>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::ApproveRegistrationApplication => {
|
||||
do_websocket_operation::<ApproveRegistrationApplication>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
|
||||
UserOperation::BlockPerson => {
|
||||
do_websocket_operation::<BlockPerson>(context, id, op, data).await
|
||||
|
@ -75,6 +84,9 @@ pub async fn match_websocket_operation(
|
|||
UserOperation::GetUnreadCount => {
|
||||
do_websocket_operation::<GetUnreadCount>(context, id, op, data).await
|
||||
}
|
||||
UserOperation::VerifyEmail => {
|
||||
do_websocket_operation::<VerifyEmail>(context, id, op, data).await
|
||||
}
|
||||
|
||||
// Private Message ops
|
||||
UserOperation::MarkPrivateMessageAsRead => {
|
||||
|
@ -219,8 +231,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()
|
||||
};
|
||||
|
||||
|
|
|
@ -6,10 +6,14 @@ use captcha::{gen, Difficulty};
|
|||
use chrono::Duration;
|
||||
use lemmy_api_common::{
|
||||
blocking,
|
||||
check_registration_application,
|
||||
get_local_user_view_from_jwt,
|
||||
is_admin,
|
||||
password_length_check,
|
||||
person::*,
|
||||
send_email_verification_success,
|
||||
send_password_reset_email,
|
||||
send_verification_email,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
diesel_option_overwrite,
|
||||
|
@ -19,6 +23,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
comment::Comment,
|
||||
community::Community,
|
||||
email_verification::EmailVerification,
|
||||
local_user::{LocalUser, LocalUserForm},
|
||||
moderator::*,
|
||||
password_reset_request::*,
|
||||
|
@ -46,12 +51,10 @@ use lemmy_db_views_actor::{
|
|||
};
|
||||
use lemmy_utils::{
|
||||
claims::Claims,
|
||||
email::send_email,
|
||||
location_info,
|
||||
utils::{generate_random_string, is_valid_display_name, is_valid_matrix_id, naive_from_unix},
|
||||
utils::{is_valid_display_name, is_valid_matrix_id, naive_from_unix},
|
||||
ConnectionId,
|
||||
LemmyError,
|
||||
Sensitive,
|
||||
};
|
||||
use lemmy_websocket::{
|
||||
messages::{CaptchaItem, SendAllMessage},
|
||||
|
@ -90,14 +93,25 @@ impl Perform for Login {
|
|||
return Err(LemmyError::from_message("password_incorrect"));
|
||||
}
|
||||
|
||||
let site = blocking(context.pool(), Site::read_simple).await??;
|
||||
if site.require_email_verification && !local_user_view.local_user.email_verified {
|
||||
return Err(LemmyError::from_message("email_not_verified"));
|
||||
}
|
||||
|
||||
check_registration_application(&site, &local_user_view, context.pool()).await?;
|
||||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
local_user_view.local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
jwt: Some(
|
||||
Claims::jwt(
|
||||
local_user_view.local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
),
|
||||
verify_email_sent: false,
|
||||
registration_created: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -164,11 +178,35 @@ 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.clone().map(Sensitive::into_inner));
|
||||
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_deref = data.email.as_deref().map(|e| e.to_owned());
|
||||
let email = diesel_option_overwrite(&email_deref);
|
||||
|
||||
if let Some(Some(email)) = &email {
|
||||
let previous_email = local_user_view.local_user.email.unwrap_or_default();
|
||||
// Only send the verification email if there was an email change
|
||||
if previous_email.ne(email) {
|
||||
send_verification_email(
|
||||
local_user_view.local_user.id,
|
||||
email,
|
||||
&local_user_view.person.name,
|
||||
context.pool(),
|
||||
&context.settings(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
// When the site requires email, make sure email is not Some(None). IE, an overwrite to a None value
|
||||
if let Some(email) = &email {
|
||||
let site_fut = blocking(context.pool(), Site::read_simple);
|
||||
if email.is_none() && site_fut.await??.require_email_verification {
|
||||
return Err(LemmyError::from_message("email_required"));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(Some(bio)) = &bio {
|
||||
if bio.chars().count() > 300 {
|
||||
|
@ -228,9 +266,9 @@ impl Perform for SaveUserSettings {
|
|||
.map_err(|e| e.with_message("user_already_exists"))?;
|
||||
|
||||
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,
|
||||
|
@ -242,6 +280,8 @@ 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,
|
||||
accepted_application: None,
|
||||
};
|
||||
|
||||
let local_user_res = blocking(context.pool(), move |conn| {
|
||||
|
@ -265,12 +305,16 @@ impl Perform for SaveUserSettings {
|
|||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
updated_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
jwt: Some(
|
||||
Claims::jwt(
|
||||
updated_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
),
|
||||
verify_email_sent: false,
|
||||
registration_created: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -315,12 +359,16 @@ impl Perform for ChangePassword {
|
|||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
updated_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
jwt: Some(
|
||||
Claims::jwt(
|
||||
updated_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
),
|
||||
verify_email_sent: false,
|
||||
registration_created: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -736,34 +784,8 @@ impl Perform for PasswordReset {
|
|||
.map_err(LemmyError::from)
|
||||
.map_err(|e| e.with_message("couldnt_find_that_username_or_email"))?;
|
||||
|
||||
// Generate a random token
|
||||
let token = generate_random_string();
|
||||
|
||||
// Insert the row
|
||||
let token2 = token.clone();
|
||||
let local_user_id = local_user_view.local_user.id;
|
||||
blocking(context.pool(), move |conn| {
|
||||
PasswordResetRequest::create_token(conn, local_user_id, &token2)
|
||||
})
|
||||
.await??;
|
||||
|
||||
// Email the pure token to the user.
|
||||
// TODO no i18n support here.
|
||||
let email = &local_user_view.local_user.email.expect("email");
|
||||
let subject = &format!("Password reset for {}", local_user_view.person.name);
|
||||
let protocol_and_hostname = &context.settings().get_protocol_and_hostname();
|
||||
let html = &format!("<h1>Password Reset Request for {}</h1><br><a href={}/password_change/{}>Click here to reset your password</a>", local_user_view.person.name, protocol_and_hostname, &token);
|
||||
send_email(
|
||||
subject,
|
||||
email,
|
||||
&local_user_view.person.name,
|
||||
html,
|
||||
&context.settings(),
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))
|
||||
.map_err(LemmyError::from)
|
||||
.map_err(|e| e.with_message("email_send_failed"))?;
|
||||
|
||||
send_password_reset_email(&local_user_view, context.pool(), &context.settings()).await?;
|
||||
Ok(PasswordResetResponse {})
|
||||
}
|
||||
}
|
||||
|
@ -805,12 +827,16 @@ impl Perform for PasswordChange {
|
|||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
updated_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
jwt: Some(
|
||||
Claims::jwt(
|
||||
updated_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
),
|
||||
verify_email_sent: false,
|
||||
registration_created: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -893,3 +919,49 @@ impl Perform for GetUnreadCount {
|
|||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for VerifyEmail {
|
||||
type Response = VerifyEmailResponse;
|
||||
|
||||
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(LemmyError::from)
|
||||
.map_err(|e| e.with_message("token_not_found"))?;
|
||||
|
||||
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??;
|
||||
|
||||
let local_user_view = blocking(context.pool(), move |conn| {
|
||||
LocalUserView::read(conn, local_user_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
send_email_verification_success(&local_user_view, &context.settings())?;
|
||||
|
||||
blocking(context.pool(), move |conn| {
|
||||
EmailVerification::delete_old_tokens_for_local_user(conn, local_user_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(VerifyEmailResponse {})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@ use diesel::NotFound;
|
|||
use lemmy_api_common::{
|
||||
blocking,
|
||||
build_federated_instances,
|
||||
check_private_instance,
|
||||
get_local_user_view_from_jwt,
|
||||
get_local_user_view_from_jwt_opt,
|
||||
is_admin,
|
||||
send_application_approved_email,
|
||||
site::*,
|
||||
};
|
||||
use lemmy_apub::{
|
||||
|
@ -19,9 +21,15 @@ use lemmy_apub::{
|
|||
EndpointType,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
diesel_option_overwrite,
|
||||
from_opt_str_to_opt_enum,
|
||||
newtypes::PersonId,
|
||||
source::{moderator::*, site::Site},
|
||||
source::{
|
||||
local_user::{LocalUser, LocalUserForm},
|
||||
moderator::*,
|
||||
registration_application::{RegistrationApplication, RegistrationApplicationForm},
|
||||
site::Site,
|
||||
},
|
||||
traits::{Crud, DeleteableOrRemoveable},
|
||||
DbPool,
|
||||
ListingType,
|
||||
|
@ -30,7 +38,12 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use lemmy_db_views::{
|
||||
comment_view::{CommentQueryBuilder, CommentView},
|
||||
local_user_view::LocalUserView,
|
||||
post_view::{PostQueryBuilder, PostView},
|
||||
registration_application_view::{
|
||||
RegistrationApplicationQueryBuilder,
|
||||
RegistrationApplicationView,
|
||||
},
|
||||
site_view::SiteView,
|
||||
};
|
||||
use lemmy_db_views_actor::{
|
||||
|
@ -64,6 +77,12 @@ impl Perform for GetModlog {
|
|||
) -> Result<GetModlogResponse, LemmyError> {
|
||||
let data: &GetModlog = self;
|
||||
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let community_id = data.community_id;
|
||||
let mod_person_id = data.mod_person_id;
|
||||
let page = data.page;
|
||||
|
@ -149,6 +168,8 @@ impl Perform for Search {
|
|||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
|
||||
let show_bot_accounts = local_user_view
|
||||
.as_ref()
|
||||
|
@ -388,6 +409,8 @@ impl Perform for ResolveObject {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let res = search_by_apub_id(&self.q, context)
|
||||
.await
|
||||
.map_err(LemmyError::from)
|
||||
|
@ -555,3 +578,142 @@ impl Perform for SaveSiteConfig {
|
|||
Ok(GetSiteConfigResponse { config_hjson })
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists registration applications, filterable by undenied only.
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for ListRegistrationApplications {
|
||||
type Response = ListRegistrationApplicationsResponse;
|
||||
|
||||
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(&data.auth, context.pool(), context.secret()).await?;
|
||||
|
||||
// Make sure user is an admin
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let unread_only = data.unread_only;
|
||||
let verified_email_only = blocking(context.pool(), Site::read_simple)
|
||||
.await??
|
||||
.require_email_verification;
|
||||
|
||||
let page = data.page;
|
||||
let limit = data.limit;
|
||||
let registration_applications = blocking(context.pool(), move |conn| {
|
||||
RegistrationApplicationQueryBuilder::create(conn)
|
||||
.unread_only(unread_only)
|
||||
.verified_email_only(verified_email_only)
|
||||
.page(page)
|
||||
.limit(limit)
|
||||
.list()
|
||||
})
|
||||
.await??;
|
||||
|
||||
let res = Self::Response {
|
||||
registration_applications,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for ApproveRegistrationApplication {
|
||||
type Response = RegistrationApplicationResponse;
|
||||
|
||||
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(&data.auth, context.pool(), context.secret()).await?;
|
||||
|
||||
let app_id = data.id;
|
||||
|
||||
// Only let admins do this
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
// Update the registration with reason, admin_id
|
||||
let deny_reason = diesel_option_overwrite(&data.deny_reason);
|
||||
let app_form = RegistrationApplicationForm {
|
||||
admin_id: Some(local_user_view.person.id),
|
||||
deny_reason,
|
||||
..RegistrationApplicationForm::default()
|
||||
};
|
||||
|
||||
let registration_application = blocking(context.pool(), move |conn| {
|
||||
RegistrationApplication::update(conn, app_id, &app_form)
|
||||
})
|
||||
.await??;
|
||||
|
||||
// Update the local_user row
|
||||
let local_user_form = LocalUserForm {
|
||||
accepted_application: Some(data.approve),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
|
||||
let approved_user_id = registration_application.local_user_id;
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalUser::update(conn, approved_user_id, &local_user_form)
|
||||
})
|
||||
.await??;
|
||||
|
||||
if data.approve {
|
||||
let approved_local_user_view = blocking(context.pool(), move |conn| {
|
||||
LocalUserView::read(conn, approved_user_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
if approved_local_user_view.local_user.email.is_some() {
|
||||
send_application_approved_email(&approved_local_user_view, &context.settings())?;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the view
|
||||
let registration_application = blocking(context.pool(), move |conn| {
|
||||
RegistrationApplicationView::read(conn, app_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(Self::Response {
|
||||
registration_application,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Perform for GetUnreadRegistrationApplicationCount {
|
||||
type Response = GetUnreadRegistrationApplicationCountResponse;
|
||||
|
||||
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(&data.auth, context.pool(), context.secret()).await?;
|
||||
|
||||
// Only let admins do this
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let verified_email_only = blocking(context.pool(), Site::read_simple)
|
||||
.await??
|
||||
.require_email_verification;
|
||||
|
||||
let registration_applications = blocking(context.pool(), move |conn| {
|
||||
RegistrationApplicationView::get_unread_count(conn, verified_email_only)
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(Self::Response {
|
||||
registration_applications,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_api_common"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
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_db_views = { version = "=0.14.4-rc.4", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.4-rc.4", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.4-rc.4", path = "../db_views_actor" }
|
||||
lemmy_db_schema = { version = "=0.14.4-rc.4", path = "../db_schema" }
|
||||
lemmy_utils = { version = "=0.14.4-rc.4", path = "../utils" }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
lemmy_db_views = { version = "=0.15.0-rc.6", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.15.0-rc.6", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.15.0-rc.6", path = "../db_views_actor" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "../db_schema" }
|
||||
lemmy_utils = { version = "=0.15.0-rc.6", path = "../utils" }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
diesel = "1.4.8"
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["cookies"] }
|
||||
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] }
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
serde_json = { version = "1.0.68", features = ["preserve_order"] }
|
||||
serde_json = { version = "1.0.72", features = ["preserve_order"] }
|
||||
tracing = "0.1.29"
|
||||
url = "2.2.2"
|
||||
|
|
|
@ -10,8 +10,11 @@ use lemmy_db_schema::{
|
|||
newtypes::{CommunityId, LocalUserId, PersonId, PostId},
|
||||
source::{
|
||||
community::Community,
|
||||
email_verification::{EmailVerification, EmailVerificationForm},
|
||||
password_reset_request::PasswordResetRequest,
|
||||
person_block::PersonBlock,
|
||||
post::{Post, PostRead, PostReadForm},
|
||||
registration_application::RegistrationApplication,
|
||||
secret::Secret,
|
||||
site::Site,
|
||||
},
|
||||
|
@ -23,7 +26,14 @@ use lemmy_db_views_actor::{
|
|||
community_person_ban_view::CommunityPersonBanView,
|
||||
community_view::CommunityView,
|
||||
};
|
||||
use lemmy_utils::{claims::Claims, settings::structs::FederationConfig, LemmyError, Sensitive};
|
||||
use lemmy_utils::{
|
||||
claims::Claims,
|
||||
email::send_email,
|
||||
settings::structs::{FederationConfig, Settings},
|
||||
utils::generate_random_string,
|
||||
LemmyError,
|
||||
Sensitive,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
|
||||
|
@ -264,6 +274,20 @@ pub async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), Le
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn check_private_instance(
|
||||
local_user_view: &Option<LocalUserView>,
|
||||
pool: &DbPool,
|
||||
) -> Result<(), LemmyError> {
|
||||
if local_user_view.is_none() {
|
||||
let site = blocking(pool, Site::read_simple).await??;
|
||||
if site.private_instance {
|
||||
return Err(LemmyError::from_message("instance_is_private"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn build_federated_instances(
|
||||
pool: &DbPool,
|
||||
|
@ -333,3 +357,163 @@ pub fn honeypot_check(honeypot: &Option<String>) -> Result<(), LemmyError> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_email_to_user(
|
||||
local_user_view: &LocalUserView,
|
||||
subject_text: &str,
|
||||
body_text: &str,
|
||||
comment_content: &str,
|
||||
settings: &Settings,
|
||||
) {
|
||||
if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(user_email) = &local_user_view.local_user.email {
|
||||
let subject = &format!(
|
||||
"{} - {} {}",
|
||||
subject_text, settings.hostname, local_user_view.person.name,
|
||||
);
|
||||
let html = &format!(
|
||||
"<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
|
||||
body_text,
|
||||
local_user_view.person.name,
|
||||
comment_content,
|
||||
settings.get_protocol_and_hostname()
|
||||
);
|
||||
match send_email(
|
||||
subject,
|
||||
user_email,
|
||||
&local_user_view.person.name,
|
||||
html,
|
||||
settings,
|
||||
) {
|
||||
Ok(_o) => _o,
|
||||
Err(e) => tracing::error!("{}", e),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_password_reset_email(
|
||||
local_user_view: &LocalUserView,
|
||||
pool: &DbPool,
|
||||
settings: &Settings,
|
||||
) -> Result<(), LemmyError> {
|
||||
// Generate a random token
|
||||
let token = generate_random_string();
|
||||
|
||||
// Insert the row
|
||||
let token2 = token.clone();
|
||||
let local_user_id = local_user_view.local_user.id;
|
||||
blocking(pool, move |conn| {
|
||||
PasswordResetRequest::create_token(conn, local_user_id, &token2)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let email = &local_user_view.local_user.email.to_owned().expect("email");
|
||||
let subject = &format!("Password reset for {}", local_user_view.person.name);
|
||||
let protocol_and_hostname = settings.get_protocol_and_hostname();
|
||||
let html = &format!("<h1>Password Reset Request for {}</h1><br><a href={}/password_change/{}>Click here to reset your password</a>", local_user_view.person.name, protocol_and_hostname, &token);
|
||||
send_email(subject, email, &local_user_view.person.name, html, settings)
|
||||
}
|
||||
|
||||
/// Send a verification email
|
||||
pub async fn send_verification_email(
|
||||
local_user_id: LocalUserId,
|
||||
new_email: &str,
|
||||
username: &str,
|
||||
pool: &DbPool,
|
||||
settings: &Settings,
|
||||
) -> Result<(), LemmyError> {
|
||||
let form = EmailVerificationForm {
|
||||
local_user_id,
|
||||
email: new_email.to_string(),
|
||||
verification_token: generate_random_string(),
|
||||
};
|
||||
let verify_link = format!(
|
||||
"{}/verify_email/{}",
|
||||
settings.get_protocol_and_hostname(),
|
||||
&form.verification_token
|
||||
);
|
||||
blocking(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.<br><br>",
|
||||
"<a href=\"{}\">Verify your email</a>"
|
||||
),
|
||||
username, settings.hostname, verify_link
|
||||
);
|
||||
send_email(&subject, new_email, username, &body, settings)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_email_verification_success(
|
||||
local_user_view: &LocalUserView,
|
||||
settings: &Settings,
|
||||
) -> Result<(), LemmyError> {
|
||||
let email = &local_user_view.local_user.email.to_owned().expect("email");
|
||||
let subject = &format!("Email verified for {}", local_user_view.person.actor_id);
|
||||
let html = "Your email has been verified.";
|
||||
send_email(subject, email, &local_user_view.person.name, html, settings)
|
||||
}
|
||||
|
||||
pub fn send_application_approved_email(
|
||||
local_user_view: &LocalUserView,
|
||||
settings: &Settings,
|
||||
) -> Result<(), LemmyError> {
|
||||
let email = &local_user_view.local_user.email.to_owned().expect("email");
|
||||
let subject = &format!(
|
||||
"Registration approved for {}",
|
||||
local_user_view.person.actor_id
|
||||
);
|
||||
let html = &format!(
|
||||
"Your registration application has been approved. Welcome to {}!",
|
||||
settings.hostname
|
||||
);
|
||||
send_email(subject, email, &local_user_view.person.name, html, settings)
|
||||
}
|
||||
|
||||
pub async fn check_registration_application(
|
||||
site: &Site,
|
||||
local_user_view: &LocalUserView,
|
||||
pool: &DbPool,
|
||||
) -> Result<(), LemmyError> {
|
||||
if site.require_application
|
||||
&& !local_user_view.local_user.accepted_application
|
||||
&& !local_user_view.person.admin
|
||||
{
|
||||
// Fetch the registration, see if its denied
|
||||
let local_user_id = local_user_view.local_user.id;
|
||||
let registration = blocking(pool, move |conn| {
|
||||
RegistrationApplication::find_by_local_user_id(conn, local_user_id)
|
||||
})
|
||||
.await??;
|
||||
if registration.deny_reason.is_some() {
|
||||
return Err(LemmyError::from_message("registration_denied"));
|
||||
} else {
|
||||
return Err(LemmyError::from_message("registration_application_pending"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// TODO this check should be removed after https://github.com/LemmyNet/lemmy/issues/868 is done.
|
||||
pub async fn check_private_instance_and_federation_enabled(
|
||||
pool: &DbPool,
|
||||
settings: &Settings,
|
||||
) -> Result<(), LemmyError> {
|
||||
let site_opt = blocking(pool, Site::read_simple).await?;
|
||||
|
||||
if let Ok(site) = site_opt {
|
||||
if site.private_instance && settings.federation.enabled {
|
||||
return Err(LemmyError::from_message(
|
||||
"Cannot have both private instance and federation enabled.",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -24,10 +24,13 @@ pub struct Register {
|
|||
pub password: Sensitive<String>,
|
||||
pub password_verify: Sensitive<String>,
|
||||
pub show_nsfw: bool,
|
||||
/// email is mandatory if email verification is enabled on the server
|
||||
pub email: Option<Sensitive<String>>,
|
||||
pub captcha_uuid: Option<String>,
|
||||
pub captcha_answer: Option<String>,
|
||||
pub honeypot: Option<String>,
|
||||
/// An answer is mandatory if require application is enabled on the server
|
||||
pub answer: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -78,7 +81,10 @@ pub struct ChangePassword {
|
|||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct LoginResponse {
|
||||
pub jwt: Sensitive<String>,
|
||||
/// This is None in response to `Register` if email verification is enabled, or the server requires registration applications.
|
||||
pub jwt: Option<Sensitive<String>>,
|
||||
pub registration_created: bool,
|
||||
pub verify_email_sent: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -194,6 +200,9 @@ pub struct DeleteAccount {
|
|||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct DeleteAccountResponse {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct PasswordReset {
|
||||
pub email: Sensitive<String>,
|
||||
|
@ -279,3 +288,11 @@ pub struct GetUnreadCountResponse {
|
|||
pub mentions: i64,
|
||||
pub private_messages: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct VerifyEmail {
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct VerifyEmailResponse {}
|
||||
|
|
|
@ -3,6 +3,7 @@ use lemmy_db_views::{
|
|||
comment_view::CommentView,
|
||||
local_user_view::LocalUserSettingsView,
|
||||
post_view::PostView,
|
||||
registration_application_view::RegistrationApplicationView,
|
||||
site_view::SiteView,
|
||||
};
|
||||
use lemmy_db_views_actor::{
|
||||
|
@ -71,6 +72,7 @@ pub struct GetModlog {
|
|||
pub community_id: Option<CommunityId>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
pub auth: Option<Sensitive<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -98,6 +100,10 @@ pub struct CreateSite {
|
|||
pub open_registration: Option<bool>,
|
||||
pub enable_nsfw: Option<bool>,
|
||||
pub community_creation_admin_only: Option<bool>,
|
||||
pub require_email_verification: Option<bool>,
|
||||
pub require_application: Option<bool>,
|
||||
pub application_question: Option<String>,
|
||||
pub private_instance: Option<bool>,
|
||||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
|
@ -112,6 +118,10 @@ 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 require_application: Option<bool>,
|
||||
pub application_question: Option<String>,
|
||||
pub private_instance: Option<bool>,
|
||||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
|
@ -173,3 +183,40 @@ pub struct FederatedInstances {
|
|||
pub allowed: Option<Vec<String>>,
|
||||
pub blocked: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ListRegistrationApplications {
|
||||
/// Only shows the unread applications (IE those without an admin actor)
|
||||
pub unread_only: Option<bool>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
pub auth: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ListRegistrationApplicationsResponse {
|
||||
pub registration_applications: Vec<RegistrationApplicationView>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ApproveRegistrationApplication {
|
||||
pub id: i32,
|
||||
pub approve: bool,
|
||||
pub deny_reason: Option<String>,
|
||||
pub auth: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RegistrationApplicationResponse {
|
||||
pub registration_application: RegistrationApplicationView,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GetUnreadRegistrationApplicationCount {
|
||||
pub auth: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct GetUnreadRegistrationApplicationCountResponse {
|
||||
pub registration_applications: i64,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_api_crud"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -8,40 +8,40 @@ homepage = "https://join-lemmy.org/"
|
|||
documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
|
||||
[dependencies]
|
||||
lemmy_apub = { version = "=0.14.4-rc.4", path = "../apub" }
|
||||
lemmy_apub_lib = { version = "=0.14.4-rc.4", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.14.4-rc.4", path = "../utils" }
|
||||
lemmy_db_schema = { version = "=0.14.4-rc.4", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.4-rc.4", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.14.4-rc.4", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.14.4-rc.4", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.4-rc.4", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.14.4-rc.4", path = "../websocket" }
|
||||
lemmy_apub = { version = "=0.15.0-rc.6", path = "../apub" }
|
||||
lemmy_apub_lib = { version = "=0.15.0-rc.6", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.15.0-rc.6", path = "../utils" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.15.0-rc.6", path = "../db_views" }
|
||||
lemmy_db_views_moderator = { version = "=0.15.0-rc.6", path = "../db_views_moderator" }
|
||||
lemmy_db_views_actor = { version = "=0.15.0-rc.6", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.15.0-rc.6", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.15.0-rc.6", path = "../websocket" }
|
||||
diesel = "1.4.8"
|
||||
bcrypt = "0.10.1"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
serde_json = { version = "1.0.68", features = ["preserve_order"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde_json = { version = "1.0.72", features = ["preserve_order"] }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
actix = "0.12.0"
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false }
|
||||
actix-rt = { version = "2.2.0", default-features = false }
|
||||
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||
actix-rt = { version = "2.5.0", default-features = false }
|
||||
tracing = "0.1.29"
|
||||
rand = "0.8.4"
|
||||
strum = "0.21.0"
|
||||
strum_macros = "0.21.1"
|
||||
strum = "0.23.0"
|
||||
strum_macros = "0.23.1"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
openssl = "0.10.36"
|
||||
openssl = "0.10.38"
|
||||
http = "0.2.5"
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.10", default-features = false, features = ["sha-2"] }
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.14", default-features = false, features = ["sha-2"] }
|
||||
base64 = "0.13.0"
|
||||
tokio = "1.12.0"
|
||||
futures = "0.3.17"
|
||||
itertools = "0.10.1"
|
||||
tokio = "1.14.0"
|
||||
futures = "0.3.18"
|
||||
itertools = "0.10.3"
|
||||
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||
sha2 = "0.9.8"
|
||||
async-trait = "0.1.51"
|
||||
anyhow = "1.0.44"
|
||||
thiserror = "1.0.29"
|
||||
sha2 = "0.10.0"
|
||||
async-trait = "0.1.52"
|
||||
anyhow = "1.0.51"
|
||||
thiserror = "1.0.30"
|
||||
background-jobs = "0.11.0"
|
||||
reqwest = { version = "0.11.4", features = ["json"] }
|
||||
reqwest = { version = "0.11.7", features = ["json"] }
|
||||
webmention = "0.4.0"
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{blocking, comment::*, get_local_user_view_from_jwt_opt};
|
||||
use lemmy_api_common::{
|
||||
blocking,
|
||||
check_private_instance,
|
||||
comment::*,
|
||||
get_local_user_view_from_jwt_opt,
|
||||
};
|
||||
use lemmy_apub::{
|
||||
fetcher::webfinger::webfinger_resolve,
|
||||
objects::community::ApubCommunity,
|
||||
|
@ -31,6 +36,8 @@ impl PerformCrud for GetComment {
|
|||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let person_id = local_user_view.map(|u| u.person.id);
|
||||
let id = data.id;
|
||||
let comment_view = blocking(context.pool(), move |conn| {
|
||||
|
@ -63,6 +70,8 @@ impl PerformCrud for GetComments {
|
|||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let show_bot_accounts = local_user_view
|
||||
.as_ref()
|
||||
.map(|t| t.local_user.show_bot_accounts);
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt};
|
||||
use lemmy_api_common::{
|
||||
blocking,
|
||||
check_private_instance,
|
||||
community::*,
|
||||
get_local_user_view_from_jwt_opt,
|
||||
};
|
||||
use lemmy_apub::{
|
||||
fetcher::webfinger::webfinger_resolve,
|
||||
objects::community::ApubCommunity,
|
||||
|
@ -34,6 +39,9 @@ impl PerformCrud for GetCommunity {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let person_id = local_user_view.map(|u| u.person.id);
|
||||
|
||||
let community_id = match data.id {
|
||||
|
@ -105,6 +113,8 @@ impl PerformCrud for ListCommunities {
|
|||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let person_id = local_user_view.to_owned().map(|l| l.person.id);
|
||||
|
||||
// Don't show NSFW by default
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, mark_post_as_read, post::*};
|
||||
use lemmy_api_common::{
|
||||
blocking,
|
||||
check_private_instance,
|
||||
get_local_user_view_from_jwt_opt,
|
||||
mark_post_as_read,
|
||||
post::*,
|
||||
};
|
||||
use lemmy_apub::{
|
||||
fetcher::webfinger::webfinger_resolve,
|
||||
objects::community::ApubCommunity,
|
||||
|
@ -38,6 +44,8 @@ impl PerformCrud for GetPost {
|
|||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let show_bot_accounts = local_user_view
|
||||
.as_ref()
|
||||
.map(|t| t.local_user.show_bot_accounts);
|
||||
|
@ -130,6 +138,8 @@ impl PerformCrud for GetPosts {
|
|||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let person_id = local_user_view.to_owned().map(|l| l.person.id);
|
||||
|
||||
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
|
||||
|
|
|
@ -5,6 +5,7 @@ use lemmy_api_common::{
|
|||
check_person_block,
|
||||
get_local_user_view_from_jwt,
|
||||
person::{CreatePrivateMessage, PrivateMessageResponse},
|
||||
send_email_to_user,
|
||||
};
|
||||
use lemmy_apub::{
|
||||
generate_local_apub_endpoint,
|
||||
|
@ -20,11 +21,7 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use lemmy_db_views::local_user_view::LocalUserView;
|
||||
use lemmy_utils::{utils::remove_slurs, ConnectionId, LemmyError};
|
||||
use lemmy_websocket::{
|
||||
send::{send_email_to_user, send_pm_ws_message},
|
||||
LemmyContext,
|
||||
UserOperationCrud,
|
||||
};
|
||||
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for CreatePrivateMessage {
|
||||
|
|
|
@ -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,8 +45,13 @@ impl PerformCrud for GetSite {
|
|||
captcha_uuid: None,
|
||||
captcha_answer: None,
|
||||
honeypot: None,
|
||||
answer: 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 {
|
||||
|
@ -59,7 +64,11 @@ 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,
|
||||
require_email_verification: setup.require_email_verification,
|
||||
require_application: setup.require_application,
|
||||
application_question: setup.application_question.to_owned(),
|
||||
private_instance: setup.private_instance,
|
||||
auth: admin_jwt,
|
||||
};
|
||||
create_site.perform(context, websocket_id).await?;
|
||||
info!("Site {} created", setup.site_name);
|
||||
|
|
|
@ -11,7 +11,10 @@ use lemmy_db_schema::{
|
|||
diesel_option_overwrite,
|
||||
diesel_option_overwrite_to_url,
|
||||
naive_now,
|
||||
source::site::{Site, SiteForm},
|
||||
source::{
|
||||
local_user::LocalUser,
|
||||
site::{Site, SiteForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::site_view::SiteView;
|
||||
|
@ -42,6 +45,7 @@ impl PerformCrud for EditSite {
|
|||
|
||||
let sidebar = diesel_option_overwrite(&data.sidebar);
|
||||
let description = diesel_option_overwrite(&data.description);
|
||||
let application_question = diesel_option_overwrite(&data.application_question);
|
||||
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||
|
||||
|
@ -61,13 +65,41 @@ 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,
|
||||
require_application: data.require_application,
|
||||
application_question,
|
||||
private_instance: data.private_instance,
|
||||
};
|
||||
|
||||
let update_site = move |conn: &'_ _| Site::update(conn, 1, &site_form);
|
||||
blocking(context.pool(), update_site)
|
||||
let update_site = blocking(context.pool(), move |conn| {
|
||||
Site::update(conn, 1, &site_form)
|
||||
})
|
||||
.await?
|
||||
.map_err(LemmyError::from)
|
||||
.map_err(|e| e.with_message("couldnt_update_site"))?;
|
||||
|
||||
// TODO can't think of a better way to do this.
|
||||
// If the server suddenly requires email verification, or required applications, no old users
|
||||
// will be able to log in. It really only wants this to be a requirement for NEW signups.
|
||||
// So if it was set from false, to true, you need to update all current users columns to be verified.
|
||||
|
||||
if !found_site.require_application && update_site.require_application {
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalUser::set_all_users_registration_applications_accepted(conn)
|
||||
})
|
||||
.await?
|
||||
.map_err(LemmyError::from)
|
||||
.map_err(|e| e.with_message("couldnt_update_site"))?;
|
||||
.map_err(|e| e.with_message("couldnt_set_all_registrations_accepted"))?;
|
||||
}
|
||||
|
||||
if !found_site.require_email_verification && update_site.require_email_verification {
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalUser::set_all_users_email_verified(conn)
|
||||
})
|
||||
.await?
|
||||
.map_err(LemmyError::from)
|
||||
.map_err(|e| e.with_message("couldnt_set_all_email_verified"))?;
|
||||
}
|
||||
|
||||
let site_view = blocking(context.pool(), SiteView::read).await??;
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{blocking, honeypot_check, password_length_check, person::*};
|
||||
use lemmy_api_common::{
|
||||
blocking,
|
||||
honeypot_check,
|
||||
password_length_check,
|
||||
person::*,
|
||||
send_verification_email,
|
||||
};
|
||||
use lemmy_apub::{
|
||||
generate_followers_url,
|
||||
generate_inbox_url,
|
||||
|
@ -21,11 +27,10 @@ use lemmy_db_schema::{
|
|||
},
|
||||
local_user::{LocalUser, LocalUserForm},
|
||||
person::{Person, PersonForm},
|
||||
registration_application::{RegistrationApplication, RegistrationApplicationForm},
|
||||
site::Site,
|
||||
},
|
||||
traits::{Crud, Followable, Joinable},
|
||||
ListingType,
|
||||
SortType,
|
||||
};
|
||||
use lemmy_db_views_actor::person_view::PersonViewSafe;
|
||||
use lemmy_utils::{
|
||||
|
@ -49,16 +54,31 @@ impl PerformCrud for Register {
|
|||
) -> Result<LoginResponse, LemmyError> {
|
||||
let data: &Register = self;
|
||||
|
||||
// no email verification, or applications if the site is not setup yet
|
||||
let (mut email_verification, mut require_application) = (false, false);
|
||||
|
||||
// Make sure site has open registration
|
||||
if let Ok(site) = blocking(context.pool(), Site::read_simple).await? {
|
||||
if !site.open_registration {
|
||||
return Err(LemmyError::from_message("registration_closed"));
|
||||
}
|
||||
email_verification = site.require_email_verification;
|
||||
require_application = site.require_application;
|
||||
}
|
||||
|
||||
password_length_check(&data.password)?;
|
||||
honeypot_check(&data.honeypot)?;
|
||||
|
||||
if email_verification && data.email.is_none() {
|
||||
return Err(LemmyError::from_message("email_required"));
|
||||
}
|
||||
|
||||
if require_application && data.answer.is_none() {
|
||||
return Err(LemmyError::from_message(
|
||||
"registration_application_answer_required",
|
||||
));
|
||||
}
|
||||
|
||||
// Make sure passwords match
|
||||
if data.password != data.password_verify {
|
||||
return Err(LemmyError::from_message("passwords_dont_match"));
|
||||
|
@ -125,22 +145,13 @@ impl PerformCrud for Register {
|
|||
.map_err(|e| e.with_message("user_already_exists"))?;
|
||||
|
||||
// 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.as_deref().map(|s| s.to_owned())),
|
||||
password_encrypted: data.password.to_string(),
|
||||
password_encrypted: Some(data.password.to_string()),
|
||||
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(false),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
|
||||
let inserted_local_user = match blocking(context.pool(), move |conn| {
|
||||
|
@ -168,6 +179,21 @@ impl PerformCrud for Register {
|
|||
}
|
||||
};
|
||||
|
||||
if require_application {
|
||||
// Create the registration application
|
||||
let form = RegistrationApplicationForm {
|
||||
local_user_id: Some(inserted_local_user.id),
|
||||
// We already made sure answer was not null above
|
||||
answer: data.answer.to_owned(),
|
||||
..RegistrationApplicationForm::default()
|
||||
};
|
||||
|
||||
blocking(context.pool(), move |conn| {
|
||||
RegistrationApplication::create(conn, &form)
|
||||
})
|
||||
.await??;
|
||||
}
|
||||
|
||||
let main_community_keypair = generate_actor_keypair()?;
|
||||
|
||||
// Create the main community if it doesn't exist
|
||||
|
@ -231,14 +257,41 @@ impl PerformCrud for Register {
|
|||
.map_err(|e| e.with_message("community_moderator_already_exists"))?;
|
||||
}
|
||||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
jwt: Claims::jwt(
|
||||
inserted_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
})
|
||||
let mut login_response = LoginResponse {
|
||||
jwt: None,
|
||||
registration_created: false,
|
||||
verify_email_sent: false,
|
||||
};
|
||||
|
||||
// Log the user in directly if email verification and application aren't required
|
||||
if !require_application && !email_verification {
|
||||
login_response.jwt = Some(
|
||||
Claims::jwt(
|
||||
inserted_local_user.id.0,
|
||||
&context.secret().jwt_secret,
|
||||
&context.settings().hostname,
|
||||
)?
|
||||
.into(),
|
||||
);
|
||||
} else {
|
||||
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.pool(),
|
||||
&context.settings(),
|
||||
)
|
||||
.await?;
|
||||
login_response.verify_email_sent = true;
|
||||
}
|
||||
|
||||
if require_application {
|
||||
login_response.registration_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(login_response)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,15 +8,15 @@ use lemmy_websocket::LemmyContext;
|
|||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for DeleteAccount {
|
||||
type Response = LoginResponse;
|
||||
type Response = DeleteAccountResponse;
|
||||
|
||||
#[tracing::instrument(skip(self, context, _websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<LoginResponse, LemmyError> {
|
||||
let data: &DeleteAccount = self;
|
||||
) -> Result<Self::Response, LemmyError> {
|
||||
let data = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(data.auth.as_ref(), context.pool(), context.secret()).await?;
|
||||
|
||||
|
@ -50,8 +50,6 @@ impl PerformCrud for DeleteAccount {
|
|||
})
|
||||
.await??;
|
||||
|
||||
Ok(LoginResponse {
|
||||
jwt: data.auth.clone(),
|
||||
})
|
||||
Ok(DeleteAccountResponse {})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*};
|
||||
use lemmy_api_common::{
|
||||
blocking,
|
||||
check_private_instance,
|
||||
get_local_user_view_from_jwt_opt,
|
||||
person::*,
|
||||
};
|
||||
use lemmy_apub::{
|
||||
fetcher::webfinger::webfinger_resolve,
|
||||
objects::person::ApubPerson,
|
||||
|
@ -31,6 +36,8 @@ impl PerformCrud for GetPersonDetails {
|
|||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
|
||||
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
|
||||
let show_bot_accounts = local_user_view
|
||||
.as_ref()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_apub"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -13,45 +13,45 @@ path = "src/lib.rs"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.4-rc.4", path = "../utils" }
|
||||
lemmy_apub_lib = { version = "=0.14.4-rc.4", path = "../apub_lib" }
|
||||
lemmy_db_schema = { version = "=0.14.4-rc.4", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.4-rc.4", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.14.4-rc.4", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.14.4-rc.4", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.14.4-rc.4", path = "../websocket" }
|
||||
lemmy_utils = { version = "=0.15.0-rc.6", path = "../utils" }
|
||||
lemmy_apub_lib = { version = "=0.15.0-rc.6", path = "../apub_lib" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.15.0-rc.6", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.15.0-rc.6", path = "../db_views_actor" }
|
||||
lemmy_api_common = { version = "=0.15.0-rc.6", path = "../api_common" }
|
||||
lemmy_websocket = { version = "=0.15.0-rc.6", path = "../websocket" }
|
||||
diesel = "1.4.8"
|
||||
activitystreams-kinds = "0.1.2"
|
||||
bcrypt = "0.10.1"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
serde_json = { version = "1.0.68", features = ["preserve_order"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde_with = "1.10.0"
|
||||
serde_json = { version = "1.0.72", features = ["preserve_order"] }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
serde_with = "1.11.0"
|
||||
actix = "0.12.0"
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false }
|
||||
actix-rt = { version = "2.2.0", default-features = false }
|
||||
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||
actix-rt = { version = "2.5.0", default-features = false }
|
||||
tracing = "0.1.29"
|
||||
rand = "0.8.4"
|
||||
strum = "0.21.0"
|
||||
strum_macros = "0.21.1"
|
||||
strum = "0.23.0"
|
||||
strum_macros = "0.23.1"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
percent-encoding = "2.1.0"
|
||||
http = "0.2.5"
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.10", default-features = false, features = ["server", "sha-2"] }
|
||||
tokio = "1.12.0"
|
||||
futures = "0.3.17"
|
||||
itertools = "0.10.1"
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.14", default-features = false, features = ["server", "sha-2"] }
|
||||
tokio = "1.14.0"
|
||||
futures = "0.3.18"
|
||||
itertools = "0.10.3"
|
||||
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||
sha2 = "0.9.8"
|
||||
async-trait = "0.1.51"
|
||||
anyhow = "1.0.44"
|
||||
thiserror = "1.0.29"
|
||||
sha2 = "0.10.0"
|
||||
async-trait = "0.1.52"
|
||||
anyhow = "1.0.51"
|
||||
thiserror = "1.0.30"
|
||||
background-jobs = "0.11.0"
|
||||
reqwest = { version = "0.11.4", features = ["json"] }
|
||||
reqwest = { version = "0.11.7", features = ["json"] }
|
||||
html2md = "0.2.13"
|
||||
once_cell = "1.8.0"
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.5.1"
|
||||
assert-json-diff = "2.0.1"
|
||||
reqwest-middleware = "0.1.2"
|
||||
reqwest-middleware = "0.1.3"
|
||||
|
|
18
crates/apub/assets/pleroma/activities/follow.json
Normal file
18
crates/apub/assets/pleroma/activities/follow.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://mycrowd.ca/schemas/litepub-0.1.jsonld",
|
||||
{
|
||||
"@language": "und"
|
||||
}
|
||||
],
|
||||
"actor": "https://mycrowd.ca/users/kinetix",
|
||||
"cc": [],
|
||||
"id": "https://mycrowd.ca/activities/dab6a4d3-0db0-41ee-8aab-7bfa4929b4fd",
|
||||
"object": "https://lemmy.ca/u/kinetix",
|
||||
"state": "pending",
|
||||
"to": [
|
||||
"https://lemmy.ca/u/kinetix"
|
||||
],
|
||||
"type": "Follow"
|
||||
}
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
http::{create_apub_response, create_apub_tombstone_response},
|
||||
objects::comment::ApubComment,
|
||||
};
|
||||
use actix_web::{body::AnyBody, web, web::Path, HttpResponse};
|
||||
use actix_web::{web, web::Path, HttpResponse};
|
||||
use diesel::result::Error::NotFound;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::traits::ApubObject;
|
||||
|
@ -21,7 +21,7 @@ pub(crate) struct CommentQuery {
|
|||
pub(crate) async fn get_apub_comment(
|
||||
info: Path<CommentQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let id = CommentId(info.comment_id.parse::<i32>()?);
|
||||
let comment: ApubComment = blocking(context.pool(), move |conn| Comment::read(conn, id))
|
||||
.await??
|
||||
|
|
|
@ -21,7 +21,7 @@ use crate::{
|
|||
collections::group_followers::GroupFollowers,
|
||||
},
|
||||
};
|
||||
use actix_web::{body::AnyBody, web, web::Payload, HttpRequest, HttpResponse};
|
||||
use actix_web::{web, web::Payload, HttpRequest, HttpResponse};
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
|
||||
use lemmy_db_schema::source::community::Community;
|
||||
|
@ -40,7 +40,7 @@ pub(crate) struct CommunityQuery {
|
|||
pub(crate) async fn get_apub_community_http(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community: ApubCommunity = blocking(context.pool(), move |conn| {
|
||||
Community::read_from_name(conn, &info.community_name)
|
||||
})
|
||||
|
@ -98,7 +98,7 @@ pub(in crate::http) async fn receive_group_inbox(
|
|||
pub(crate) async fn get_apub_community_followers(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community = blocking(context.pool(), move |conn| {
|
||||
Community::read_from_name(conn, &info.community_name)
|
||||
})
|
||||
|
@ -112,7 +112,7 @@ pub(crate) async fn get_apub_community_followers(
|
|||
pub(crate) async fn get_apub_community_outbox(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community = blocking(context.pool(), move |conn| {
|
||||
Community::read_from_name(conn, &info.community_name)
|
||||
})
|
||||
|
@ -129,7 +129,7 @@ pub(crate) async fn get_apub_community_outbox(
|
|||
pub(crate) async fn get_apub_community_moderators(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community: ApubCommunity = blocking(context.pool(), move |conn| {
|
||||
Community::read_from_name(conn, &info.community_name)
|
||||
})
|
||||
|
|
|
@ -7,7 +7,6 @@ use crate::{
|
|||
insert_activity,
|
||||
};
|
||||
use actix_web::{
|
||||
body::AnyBody,
|
||||
web,
|
||||
web::{Bytes, BytesMut, Payload},
|
||||
HttpRequest,
|
||||
|
@ -119,7 +118,7 @@ where
|
|||
|
||||
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
|
||||
/// headers.
|
||||
fn create_apub_response<T>(data: &T) -> HttpResponse<AnyBody>
|
||||
fn create_apub_response<T>(data: &T) -> HttpResponse
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
|
@ -128,13 +127,13 @@ where
|
|||
.json(WithContext::new(data))
|
||||
}
|
||||
|
||||
fn create_json_apub_response(data: serde_json::Value) -> HttpResponse<AnyBody> {
|
||||
fn create_json_apub_response(data: serde_json::Value) -> HttpResponse {
|
||||
HttpResponse::Ok()
|
||||
.content_type(APUB_JSON_CONTENT_TYPE)
|
||||
.json(data)
|
||||
}
|
||||
|
||||
fn create_apub_tombstone_response<T>(data: &T) -> HttpResponse<AnyBody>
|
||||
fn create_apub_tombstone_response<T>(data: &T) -> HttpResponse
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
|
@ -155,7 +154,7 @@ pub struct ActivityQuery {
|
|||
pub(crate) async fn get_activity(
|
||||
info: web::Path<ActivityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let settings = context.settings();
|
||||
let activity_id = Url::parse(&format!(
|
||||
"{}/activities/{}/{}",
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
objects::person::ApubPerson,
|
||||
protocol::collections::person_outbox::PersonOutbox,
|
||||
};
|
||||
use actix_web::{body::AnyBody, web, web::Payload, HttpRequest, HttpResponse};
|
||||
use actix_web::{web, web::Payload, HttpRequest, HttpResponse};
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::traits::ApubObject;
|
||||
use lemmy_db_schema::source::person::Person;
|
||||
|
@ -30,7 +30,7 @@ pub struct PersonQuery {
|
|||
pub(crate) async fn get_apub_person_http(
|
||||
info: web::Path<PersonQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let user_name = info.into_inner().user_name;
|
||||
// TODO: this needs to be able to read deleted persons, so that it can send tombstones
|
||||
let person: ApubPerson = blocking(context.pool(), move |conn| {
|
||||
|
@ -75,7 +75,7 @@ pub(in crate::http) async fn receive_person_inbox(
|
|||
pub(crate) async fn get_apub_person_outbox(
|
||||
info: web::Path<PersonQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let person = blocking(context.pool(), move |conn| {
|
||||
Person::find_by_name(conn, &info.user_name)
|
||||
})
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
http::{create_apub_response, create_apub_tombstone_response},
|
||||
objects::post::ApubPost,
|
||||
};
|
||||
use actix_web::{body::AnyBody, web, HttpResponse};
|
||||
use actix_web::{web, HttpResponse};
|
||||
use diesel::result::Error::NotFound;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::traits::ApubObject;
|
||||
|
@ -21,7 +21,7 @@ pub(crate) struct PostQuery {
|
|||
pub(crate) async fn get_apub_post(
|
||||
info: web::Path<PostQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let id = PostId(info.post_id.parse::<i32>()?);
|
||||
let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, id))
|
||||
.await??
|
||||
|
|
|
@ -4,6 +4,7 @@ pub mod post;
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
context::WithContext,
|
||||
objects::tests::file_to_json_object,
|
||||
protocol::{
|
||||
activities::create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost},
|
||||
|
@ -23,8 +24,12 @@ mod tests {
|
|||
"assets/lemmy/activities/create_or_update/create_note.json",
|
||||
);
|
||||
|
||||
file_to_json_object::<CreateOrUpdateComment>("assets/pleroma/activities/create_note.json");
|
||||
file_to_json_object::<CreateOrUpdateComment>("assets/smithereen/activities/create_note.json");
|
||||
file_to_json_object::<WithContext<CreateOrUpdateComment>>(
|
||||
"assets/pleroma/activities/create_note.json",
|
||||
);
|
||||
file_to_json_object::<WithContext<CreateOrUpdateComment>>(
|
||||
"assets/smithereen/activities/create_note.json",
|
||||
);
|
||||
file_to_json_object::<CreateOrUpdateComment>("assets/mastodon/activities/create_note.json");
|
||||
|
||||
file_to_json_object::<CreateOrUpdatePost>("assets/lotide/activities/create_page.json");
|
||||
|
|
|
@ -4,13 +4,17 @@ pub mod undo_follow;
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::protocol::{
|
||||
activities::following::{
|
||||
accept::AcceptFollowCommunity,
|
||||
follow::FollowCommunity,
|
||||
undo_follow::UndoFollowCommunity,
|
||||
use crate::{
|
||||
context::WithContext,
|
||||
objects::tests::file_to_json_object,
|
||||
protocol::{
|
||||
activities::following::{
|
||||
accept::AcceptFollowCommunity,
|
||||
follow::FollowCommunity,
|
||||
undo_follow::UndoFollowCommunity,
|
||||
},
|
||||
tests::test_parse_lemmy_item,
|
||||
},
|
||||
tests::test_parse_lemmy_item,
|
||||
};
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@ -20,5 +24,7 @@ mod tests {
|
|||
test_parse_lemmy_item::<UndoFollowCommunity>(
|
||||
"assets/lemmy/activities/following/undo_follow.json",
|
||||
);
|
||||
|
||||
file_to_json_object::<WithContext<FollowCommunity>>("assets/pleroma/activities/follow.json");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::ToString;
|
||||
use strum_macros::Display;
|
||||
|
||||
pub mod community;
|
||||
pub mod create_or_update;
|
||||
|
@ -8,7 +8,7 @@ pub mod following;
|
|||
pub mod private_message;
|
||||
pub mod voting;
|
||||
|
||||
#[derive(Clone, Debug, ToString, Deserialize, Serialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Display, Deserialize, Serialize, PartialEq)]
|
||||
pub enum CreateOrUpdateType {
|
||||
Create,
|
||||
Update,
|
||||
|
|
|
@ -7,7 +7,7 @@ use lemmy_apub_lib::object_id::ObjectId;
|
|||
use lemmy_utils::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use strum_macros::ToString;
|
||||
use strum_macros::Display;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
|
@ -26,7 +26,7 @@ pub struct Vote {
|
|||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Display, Deserialize, Serialize)]
|
||||
pub enum VoteType {
|
||||
Like,
|
||||
Dislike,
|
||||
|
|
|
@ -18,6 +18,7 @@ pub struct Endpoints {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
context::WithContext,
|
||||
objects::tests::file_to_json_object,
|
||||
protocol::{
|
||||
objects::{chat_message::ChatMessage, group::Group, note::Note, page::Page, person::Person},
|
||||
|
@ -33,11 +34,11 @@ mod tests {
|
|||
test_parse_lemmy_item::<Note>("assets/lemmy/objects/note.json");
|
||||
test_parse_lemmy_item::<ChatMessage>("assets/lemmy/objects/chat_message.json");
|
||||
|
||||
file_to_json_object::<Person>("assets/pleroma/objects/person.json");
|
||||
file_to_json_object::<Note>("assets/pleroma/objects/note.json");
|
||||
file_to_json_object::<ChatMessage>("assets/pleroma/objects/chat_message.json");
|
||||
file_to_json_object::<WithContext<Person>>("assets/pleroma/objects/person.json");
|
||||
file_to_json_object::<WithContext<Note>>("assets/pleroma/objects/note.json");
|
||||
file_to_json_object::<WithContext<ChatMessage>>("assets/pleroma/objects/chat_message.json");
|
||||
|
||||
file_to_json_object::<Person>("assets/smithereen/objects/person.json");
|
||||
file_to_json_object::<WithContext<Person>>("assets/smithereen/objects/person.json");
|
||||
file_to_json_object::<Note>("assets/smithereen/objects/note.json");
|
||||
|
||||
file_to_json_object::<Person>("assets/mastodon/objects/person.json");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_apub_lib"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -8,24 +8,24 @@ homepage = "https://join-lemmy.org/"
|
|||
documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.4-rc.4", path = "../utils" }
|
||||
lemmy_apub_lib_derive = { version = "=0.14.4-rc.4", path = "../apub_lib_derive" }
|
||||
activitystreams = "0.7.0-alpha.11"
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
async-trait = "0.1.51"
|
||||
lemmy_utils = { version = "=0.15.0-rc.6", path = "../utils" }
|
||||
lemmy_apub_lib_derive = { version = "=0.15.0-rc.6", path = "../apub_lib_derive" }
|
||||
activitystreams = "0.7.0-alpha.14"
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
async-trait = "0.1.52"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
serde_json = { version = "1.0.68", features = ["preserve_order"] }
|
||||
anyhow = "1.0.44"
|
||||
reqwest = { version = "0.11.4", features = ["json"] }
|
||||
reqwest-middleware = "0.1.2"
|
||||
serde_json = { version = "1.0.72", features = ["preserve_order"] }
|
||||
anyhow = "1.0.51"
|
||||
reqwest = { version = "0.11.7", features = ["json"] }
|
||||
reqwest-middleware = "0.1.3"
|
||||
tracing = "0.1.29"
|
||||
base64 = "0.13.0"
|
||||
openssl = "0.10.36"
|
||||
openssl = "0.10.38"
|
||||
once_cell = "1.8.0"
|
||||
http = "0.2.5"
|
||||
sha2 = "0.9.8"
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false }
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.10", default-features = false, features = ["server", "sha-2"] }
|
||||
http-signature-normalization-reqwest = { version = "0.3.0", default-features = false, features = ["sha-2", "middleware"] }
|
||||
sha2 = "0.10.0"
|
||||
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||
http-signature-normalization-actix = { version = "0.5.0-beta.14", default-features = false, features = ["server", "sha-2"] }
|
||||
http-signature-normalization-reqwest = { version = "0.4.0", default-features = false, features = ["sha-2", "middleware"] }
|
||||
background-jobs = "0.11.0"
|
||||
diesel = "1.4.8"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_apub_lib_derive"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -11,9 +11,9 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
|
|||
proc-macro = true
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = { version = "1.0.45", features = ["diff"] }
|
||||
trybuild = { version = "1.0.53", features = ["diff"] }
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.29"
|
||||
syn = "1.0.77"
|
||||
quote = "1.0.9"
|
||||
proc-macro2 = "1.0.33"
|
||||
syn = "1.0.82"
|
||||
quote = "1.0.10"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_db_schema"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -8,23 +8,25 @@ homepage = "https://join-lemmy.org/"
|
|||
documentation = "https://join-lemmy.org/docs/en/index.html"
|
||||
|
||||
[lib]
|
||||
name = "lemmy_db_schema"
|
||||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.4-rc.4", path = "../utils" }
|
||||
lemmy_apub_lib = { version = "=0.14.4-rc.4", path = "../apub_lib" }
|
||||
lemmy_utils = { version = "=0.15.0-rc.6", path = "../utils" }
|
||||
lemmy_apub_lib = { version = "=0.15.0-rc.6", 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"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde_json = { version = "1.0.68", features = ["preserve_order"] }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
serde_json = { version = "1.0.72", features = ["preserve_order"] }
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
diesel-derive-newtype = "0.1.2"
|
||||
regex = "1.5.4"
|
||||
once_cell = "1.8.0"
|
||||
strum = "0.21.0"
|
||||
strum_macros = "0.21.1"
|
||||
sha2 = "0.9.8"
|
||||
strum = "0.23.0"
|
||||
strum_macros = "0.23.1"
|
||||
sha2 = "0.10.0"
|
||||
bcrypt = "0.10.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -65,6 +65,10 @@ mod tests {
|
|||
enable_nsfw: None,
|
||||
updated: None,
|
||||
community_creation_admin_only: Some(false),
|
||||
require_email_verification: None,
|
||||
require_application: None,
|
||||
application_question: None,
|
||||
private_instance: None,
|
||||
};
|
||||
|
||||
Site::create(&conn, &site_form).unwrap();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
functions::lower,
|
||||
naive_now,
|
||||
newtypes::{CommunityId, DbUrl, PersonId},
|
||||
source::community::{
|
||||
|
@ -95,7 +96,7 @@ impl Community {
|
|||
use crate::schema::community::dsl::*;
|
||||
community
|
||||
.filter(local.eq(true))
|
||||
.filter(name.eq(community_name))
|
||||
.filter(lower(name).eq(lower(community_name)))
|
||||
.first::<Self>(conn)
|
||||
}
|
||||
|
||||
|
|
55
crates/db_schema/src/impls/email_verification.rs
Normal file
55
crates/db_schema/src/impls/email_verification.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use crate::{newtypes::LocalUserId, source::email_verification::*, traits::Crud};
|
||||
use diesel::{
|
||||
dsl::*,
|
||||
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))
|
||||
.filter(published.gt(now - 7.days()))
|
||||
.first::<Self>(conn)
|
||||
}
|
||||
pub fn delete_old_tokens_for_local_user(
|
||||
conn: &PgConnection,
|
||||
local_user_id_: LocalUserId,
|
||||
) -> Result<usize, Error> {
|
||||
use crate::schema::email_verification::dsl::*;
|
||||
diesel::delete(email_verification.filter(local_user_id.eq(local_user_id_))).execute(conn)
|
||||
}
|
||||
}
|
|
@ -31,6 +31,8 @@ mod safe_settings_type {
|
|||
show_scores,
|
||||
show_read_posts,
|
||||
show_new_post_notifs,
|
||||
email_verified,
|
||||
accepted_application,
|
||||
);
|
||||
|
||||
impl ToSafeSettings for LocalUser {
|
||||
|
@ -54,6 +56,8 @@ mod safe_settings_type {
|
|||
show_scores,
|
||||
show_read_posts,
|
||||
show_new_post_notifs,
|
||||
email_verified,
|
||||
accepted_application,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -62,8 +66,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)
|
||||
|
@ -83,6 +89,20 @@ impl LocalUser {
|
|||
))
|
||||
.get_result::<Self>(conn)
|
||||
}
|
||||
|
||||
pub fn set_all_users_email_verified(conn: &PgConnection) -> Result<Vec<Self>, Error> {
|
||||
diesel::update(local_user)
|
||||
.set(email_verified.eq(true))
|
||||
.get_results::<Self>(conn)
|
||||
}
|
||||
|
||||
pub fn set_all_users_registration_applications_accepted(
|
||||
conn: &PgConnection,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
diesel::update(local_user)
|
||||
.set(accepted_application.eq(true))
|
||||
.get_results::<Self>(conn)
|
||||
}
|
||||
}
|
||||
|
||||
impl Crud for LocalUser {
|
||||
|
|
|
@ -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;
|
||||
|
@ -12,5 +13,6 @@ pub mod person_mention;
|
|||
pub mod post;
|
||||
pub mod post_report;
|
||||
pub mod private_message;
|
||||
pub mod registration_application;
|
||||
pub mod secret;
|
||||
pub mod site;
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
functions::lower,
|
||||
naive_now,
|
||||
newtypes::{DbUrl, PersonId},
|
||||
schema::person::dsl::*,
|
||||
|
@ -194,7 +195,7 @@ impl Person {
|
|||
person
|
||||
.filter(deleted.eq(false))
|
||||
.filter(local.eq(true))
|
||||
.filter(name.eq(from_name))
|
||||
.filter(lower(name).eq(lower(from_name)))
|
||||
.first::<Person>(conn)
|
||||
}
|
||||
|
||||
|
|
42
crates/db_schema/src/impls/registration_application.rs
Normal file
42
crates/db_schema/src/impls/registration_application.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use crate::{newtypes::LocalUserId, source::registration_application::*, traits::Crud};
|
||||
use diesel::{insert_into, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
|
||||
|
||||
impl Crud for RegistrationApplication {
|
||||
type Form = RegistrationApplicationForm;
|
||||
type IdType = i32;
|
||||
fn create(conn: &PgConnection, form: &Self::Form) -> Result<Self, Error> {
|
||||
use crate::schema::registration_application::dsl::*;
|
||||
insert_into(registration_application)
|
||||
.values(form)
|
||||
.get_result::<Self>(conn)
|
||||
}
|
||||
|
||||
fn read(conn: &PgConnection, id_: Self::IdType) -> Result<Self, Error> {
|
||||
use crate::schema::registration_application::dsl::*;
|
||||
registration_application.find(id_).first::<Self>(conn)
|
||||
}
|
||||
|
||||
fn update(conn: &PgConnection, id_: Self::IdType, form: &Self::Form) -> Result<Self, Error> {
|
||||
use crate::schema::registration_application::dsl::*;
|
||||
diesel::update(registration_application.find(id_))
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
}
|
||||
|
||||
fn delete(conn: &PgConnection, id_: Self::IdType) -> Result<usize, Error> {
|
||||
use crate::schema::registration_application::dsl::*;
|
||||
diesel::delete(registration_application.find(id_)).execute(conn)
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistrationApplication {
|
||||
pub fn find_by_local_user_id(
|
||||
conn: &PgConnection,
|
||||
local_user_id_: LocalUserId,
|
||||
) -> Result<Self, Error> {
|
||||
use crate::schema::registration_application::dsl::*;
|
||||
registration_application
|
||||
.filter(local_user_id.eq(local_user_id_))
|
||||
.first::<Self>(conn)
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ pub fn get_database_url_from_env() -> Result<String, VarError> {
|
|||
env::var("LEMMY_DATABASE_URL")
|
||||
}
|
||||
|
||||
#[derive(EnumString, ToString, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum SortType {
|
||||
Active,
|
||||
Hot,
|
||||
|
@ -46,7 +46,7 @@ pub enum SortType {
|
|||
NewComments,
|
||||
}
|
||||
|
||||
#[derive(EnumString, ToString, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum ListingType {
|
||||
All,
|
||||
Local,
|
||||
|
@ -54,7 +54,7 @@ pub enum ListingType {
|
|||
Community,
|
||||
}
|
||||
|
||||
#[derive(EnumString, ToString, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum SearchType {
|
||||
All,
|
||||
Comments,
|
||||
|
@ -143,6 +143,8 @@ pub mod functions {
|
|||
sql_function! {
|
||||
fn hot_rank(score: BigInt, time: Timestamp) -> Integer;
|
||||
}
|
||||
|
||||
sql_function!(fn lower(x: Text) -> Text);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -43,7 +43,9 @@ impl fmt::Display for CommentId {
|
|||
)]
|
||||
pub struct CommunityId(pub i32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
|
||||
#[derive(
|
||||
Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize, DieselNewType,
|
||||
)]
|
||||
pub struct LocalUserId(pub i32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
|
||||
|
|
|
@ -157,6 +157,8 @@ table! {
|
|||
show_scores -> Bool,
|
||||
show_read_posts -> Bool,
|
||||
show_new_post_notifs -> Bool,
|
||||
email_verified -> Bool,
|
||||
accepted_application -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,6 +449,10 @@ table! {
|
|||
banner -> Nullable<Varchar>,
|
||||
description -> Nullable<Text>,
|
||||
community_creation_admin_only -> Bool,
|
||||
require_email_verification -> Bool,
|
||||
require_application -> Bool,
|
||||
application_question -> Nullable<Text>,
|
||||
private_instance -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,6 +564,27 @@ table! {
|
|||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
email_verification (id) {
|
||||
id -> Int4,
|
||||
local_user_id -> Int4,
|
||||
email -> Text,
|
||||
verification_token -> Varchar,
|
||||
published -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
registration_application (id) {
|
||||
id -> Int4,
|
||||
local_user_id -> Int4,
|
||||
answer -> Text,
|
||||
admin_id -> Nullable<Int4>,
|
||||
deny_reason -> Nullable<Text>,
|
||||
published -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
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 +646,9 @@ 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));
|
||||
joinable!(registration_application -> local_user (local_user_id));
|
||||
joinable!(registration_application -> person (admin_id));
|
||||
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
activity,
|
||||
|
@ -662,4 +692,6 @@ allow_tables_to_appear_in_same_query!(
|
|||
comment_alias_1,
|
||||
person_alias_1,
|
||||
person_alias_2,
|
||||
email_verification,
|
||||
registration_application
|
||||
);
|
||||
|
|
19
crates/db_schema/src/source/email_verification.rs
Normal file
19
crates/db_schema/src/source/email_verification.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
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,
|
||||
pub published: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
#[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,16 @@ pub struct LocalUser {
|
|||
pub show_scores: bool,
|
||||
pub show_read_posts: bool,
|
||||
pub show_new_post_notifs: bool,
|
||||
pub email_verified: bool,
|
||||
pub accepted_application: 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 +45,8 @@ 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>,
|
||||
pub accepted_application: Option<bool>,
|
||||
}
|
||||
|
||||
/// A local user view that removes password encrypted
|
||||
|
@ -64,4 +68,6 @@ pub struct LocalUserSettings {
|
|||
pub show_scores: bool,
|
||||
pub show_read_posts: bool,
|
||||
pub show_new_post_notifs: bool,
|
||||
pub email_verified: bool,
|
||||
pub accepted_application: bool,
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -12,5 +13,6 @@ pub mod person_mention;
|
|||
pub mod post;
|
||||
pub mod post_report;
|
||||
pub mod private_message;
|
||||
pub mod registration_application;
|
||||
pub mod secret;
|
||||
pub mod site;
|
||||
|
|
25
crates/db_schema/src/source/registration_application.rs
Normal file
25
crates/db_schema/src/source/registration_application.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use crate::{
|
||||
newtypes::{LocalUserId, PersonId},
|
||||
schema::registration_application,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
|
||||
#[table_name = "registration_application"]
|
||||
pub struct RegistrationApplication {
|
||||
pub id: i32,
|
||||
pub local_user_id: LocalUserId,
|
||||
pub answer: String,
|
||||
pub admin_id: Option<PersonId>,
|
||||
pub deny_reason: Option<String>,
|
||||
pub published: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Insertable, AsChangeset, Default)]
|
||||
#[table_name = "registration_application"]
|
||||
pub struct RegistrationApplicationForm {
|
||||
pub local_user_id: Option<LocalUserId>,
|
||||
pub answer: Option<String>,
|
||||
pub admin_id: Option<PersonId>,
|
||||
pub deny_reason: Option<Option<String>>,
|
||||
}
|
|
@ -20,9 +20,13 @@ pub struct Site {
|
|||
pub banner: Option<DbUrl>,
|
||||
pub description: Option<String>,
|
||||
pub community_creation_admin_only: bool,
|
||||
pub require_email_verification: bool,
|
||||
pub require_application: bool,
|
||||
pub application_question: Option<String>,
|
||||
pub private_instance: bool,
|
||||
}
|
||||
|
||||
#[derive(Insertable, AsChangeset)]
|
||||
#[derive(Insertable, AsChangeset, Default)]
|
||||
#[table_name = "site"]
|
||||
pub struct SiteForm {
|
||||
pub name: String,
|
||||
|
@ -37,4 +41,8 @@ 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>,
|
||||
pub require_application: Option<bool>,
|
||||
pub application_question: Option<Option<String>>,
|
||||
pub private_instance: Option<bool>,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_db_views"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -11,9 +11,9 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_db_schema = { version = "=0.14.4-rc.4", path = "../db_schema" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "../db_schema" }
|
||||
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
tracing = "0.1.29"
|
||||
url = "2.2.2"
|
||||
|
||||
|
|
|
@ -7,4 +7,5 @@ pub mod local_user_view;
|
|||
pub mod post_report_view;
|
||||
pub mod post_view;
|
||||
pub mod private_message_view;
|
||||
pub mod registration_application_view;
|
||||
pub mod site_view;
|
||||
|
|
396
crates/db_views/src/registration_application_view.rs
Normal file
396
crates/db_views/src/registration_application_view.rs
Normal file
|
@ -0,0 +1,396 @@
|
|||
use diesel::{dsl::count, result::Error, *};
|
||||
use lemmy_db_schema::{
|
||||
limit_and_offset,
|
||||
schema::{local_user, person, person_alias_1, registration_application},
|
||||
source::{
|
||||
local_user::{LocalUser, LocalUserSettings},
|
||||
person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
|
||||
registration_application::RegistrationApplication,
|
||||
},
|
||||
traits::{MaybeOptional, ToSafe, ToSafeSettings, ViewToVec},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
pub struct RegistrationApplicationView {
|
||||
pub registration_application: RegistrationApplication,
|
||||
pub creator_local_user: LocalUserSettings,
|
||||
pub creator: PersonSafe,
|
||||
pub admin: Option<PersonSafeAlias1>,
|
||||
}
|
||||
|
||||
type RegistrationApplicationViewTuple = (
|
||||
RegistrationApplication,
|
||||
LocalUserSettings,
|
||||
PersonSafe,
|
||||
Option<PersonSafeAlias1>,
|
||||
);
|
||||
|
||||
impl RegistrationApplicationView {
|
||||
pub fn read(conn: &PgConnection, registration_application_id: i32) -> Result<Self, Error> {
|
||||
let (registration_application, creator_local_user, creator, admin) =
|
||||
registration_application::table
|
||||
.find(registration_application_id)
|
||||
.inner_join(
|
||||
local_user::table.on(registration_application::local_user_id.eq(local_user::id)),
|
||||
)
|
||||
.inner_join(person::table.on(local_user::person_id.eq(person::id)))
|
||||
.left_join(
|
||||
person_alias_1::table
|
||||
.on(registration_application::admin_id.eq(person_alias_1::id.nullable())),
|
||||
)
|
||||
.order_by(registration_application::published.desc())
|
||||
.select((
|
||||
registration_application::all_columns,
|
||||
LocalUser::safe_settings_columns_tuple(),
|
||||
Person::safe_columns_tuple(),
|
||||
PersonAlias1::safe_columns_tuple().nullable(),
|
||||
))
|
||||
.first::<RegistrationApplicationViewTuple>(conn)?;
|
||||
|
||||
Ok(RegistrationApplicationView {
|
||||
registration_application,
|
||||
creator_local_user,
|
||||
creator,
|
||||
admin,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the current unread registration_application count
|
||||
pub fn get_unread_count(conn: &PgConnection, verified_email_only: bool) -> Result<i64, Error> {
|
||||
let mut query = registration_application::table
|
||||
.inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id)))
|
||||
.inner_join(person::table.on(local_user::person_id.eq(person::id)))
|
||||
.left_join(
|
||||
person_alias_1::table
|
||||
.on(registration_application::admin_id.eq(person_alias_1::id.nullable())),
|
||||
)
|
||||
.filter(registration_application::admin_id.is_null())
|
||||
.into_boxed();
|
||||
|
||||
if verified_email_only {
|
||||
query = query.filter(local_user::email_verified.eq(true))
|
||||
}
|
||||
|
||||
query
|
||||
.select(count(registration_application::id))
|
||||
.first::<i64>(conn)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RegistrationApplicationQueryBuilder<'a> {
|
||||
conn: &'a PgConnection,
|
||||
unread_only: Option<bool>,
|
||||
verified_email_only: Option<bool>,
|
||||
page: Option<i64>,
|
||||
limit: Option<i64>,
|
||||
}
|
||||
|
||||
impl<'a> RegistrationApplicationQueryBuilder<'a> {
|
||||
pub fn create(conn: &'a PgConnection) -> Self {
|
||||
RegistrationApplicationQueryBuilder {
|
||||
conn,
|
||||
unread_only: None,
|
||||
verified_email_only: None,
|
||||
page: None,
|
||||
limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unread_only<T: MaybeOptional<bool>>(mut self, unread_only: T) -> Self {
|
||||
self.unread_only = unread_only.get_optional();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn verified_email_only<T: MaybeOptional<bool>>(mut self, verified_email_only: T) -> Self {
|
||||
self.verified_email_only = verified_email_only.get_optional();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
|
||||
self.page = page.get_optional();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
|
||||
self.limit = limit.get_optional();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn list(self) -> Result<Vec<RegistrationApplicationView>, Error> {
|
||||
let mut query = registration_application::table
|
||||
.inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id)))
|
||||
.inner_join(person::table.on(local_user::person_id.eq(person::id)))
|
||||
.left_join(
|
||||
person_alias_1::table
|
||||
.on(registration_application::admin_id.eq(person_alias_1::id.nullable())),
|
||||
)
|
||||
.order_by(registration_application::published.desc())
|
||||
.select((
|
||||
registration_application::all_columns,
|
||||
LocalUser::safe_settings_columns_tuple(),
|
||||
Person::safe_columns_tuple(),
|
||||
PersonAlias1::safe_columns_tuple().nullable(),
|
||||
))
|
||||
.into_boxed();
|
||||
|
||||
if self.unread_only.unwrap_or(false) {
|
||||
query = query.filter(registration_application::admin_id.is_null())
|
||||
}
|
||||
|
||||
if self.verified_email_only.unwrap_or(false) {
|
||||
query = query.filter(local_user::email_verified.eq(true))
|
||||
}
|
||||
|
||||
let (limit, offset) = limit_and_offset(self.page, self.limit);
|
||||
|
||||
query = query
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.order_by(registration_application::published.desc());
|
||||
|
||||
let res = query.load::<RegistrationApplicationViewTuple>(self.conn)?;
|
||||
|
||||
Ok(RegistrationApplicationView::from_tuple_to_vec(res))
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewToVec for RegistrationApplicationView {
|
||||
type DbTuple = RegistrationApplicationViewTuple;
|
||||
fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
|
||||
items
|
||||
.iter()
|
||||
.map(|a| Self {
|
||||
registration_application: a.0.to_owned(),
|
||||
creator_local_user: a.1.to_owned(),
|
||||
creator: a.2.to_owned(),
|
||||
admin: a.3.to_owned(),
|
||||
})
|
||||
.collect::<Vec<Self>>()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::registration_application_view::{
|
||||
RegistrationApplicationQueryBuilder,
|
||||
RegistrationApplicationView,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
establish_unpooled_connection,
|
||||
source::{
|
||||
local_user::{LocalUser, LocalUserForm, LocalUserSettings},
|
||||
person::*,
|
||||
registration_application::{RegistrationApplication, RegistrationApplicationForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use serial_test::serial;
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_crud() {
|
||||
let conn = establish_unpooled_connection();
|
||||
|
||||
let timmy_person_form = PersonForm {
|
||||
name: "timmy_rav".into(),
|
||||
admin: Some(true),
|
||||
..PersonForm::default()
|
||||
};
|
||||
|
||||
let inserted_timmy_person = Person::create(&conn, &timmy_person_form).unwrap();
|
||||
|
||||
let timmy_local_user_form = LocalUserForm {
|
||||
person_id: Some(inserted_timmy_person.id),
|
||||
password_encrypted: Some("nada".to_string()),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
|
||||
let _inserted_timmy_local_user = LocalUser::create(&conn, &timmy_local_user_form).unwrap();
|
||||
|
||||
let sara_person_form = PersonForm {
|
||||
name: "sara_rav".into(),
|
||||
..PersonForm::default()
|
||||
};
|
||||
|
||||
let inserted_sara_person = Person::create(&conn, &sara_person_form).unwrap();
|
||||
|
||||
let sara_local_user_form = LocalUserForm {
|
||||
person_id: Some(inserted_sara_person.id),
|
||||
password_encrypted: Some("nada".to_string()),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
|
||||
let inserted_sara_local_user = LocalUser::create(&conn, &sara_local_user_form).unwrap();
|
||||
|
||||
// Sara creates an application
|
||||
let sara_app_form = RegistrationApplicationForm {
|
||||
local_user_id: Some(inserted_sara_local_user.id),
|
||||
answer: Some("LET ME IIIIINN".to_string()),
|
||||
..RegistrationApplicationForm::default()
|
||||
};
|
||||
|
||||
let sara_app = RegistrationApplication::create(&conn, &sara_app_form).unwrap();
|
||||
|
||||
let read_sara_app_view = RegistrationApplicationView::read(&conn, sara_app.id).unwrap();
|
||||
|
||||
let jess_person_form = PersonForm {
|
||||
name: "jess_rav".into(),
|
||||
..PersonForm::default()
|
||||
};
|
||||
|
||||
let inserted_jess_person = Person::create(&conn, &jess_person_form).unwrap();
|
||||
|
||||
let jess_local_user_form = LocalUserForm {
|
||||
person_id: Some(inserted_jess_person.id),
|
||||
password_encrypted: Some("nada".to_string()),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
|
||||
let inserted_jess_local_user = LocalUser::create(&conn, &jess_local_user_form).unwrap();
|
||||
|
||||
// Sara creates an application
|
||||
let jess_app_form = RegistrationApplicationForm {
|
||||
local_user_id: Some(inserted_jess_local_user.id),
|
||||
answer: Some("LET ME IIIIINN".to_string()),
|
||||
..RegistrationApplicationForm::default()
|
||||
};
|
||||
|
||||
let jess_app = RegistrationApplication::create(&conn, &jess_app_form).unwrap();
|
||||
|
||||
let read_jess_app_view = RegistrationApplicationView::read(&conn, jess_app.id).unwrap();
|
||||
|
||||
let mut expected_sara_app_view = RegistrationApplicationView {
|
||||
registration_application: sara_app.to_owned(),
|
||||
creator_local_user: LocalUserSettings {
|
||||
id: inserted_sara_local_user.id,
|
||||
person_id: inserted_sara_local_user.person_id,
|
||||
email: inserted_sara_local_user.email,
|
||||
show_nsfw: inserted_sara_local_user.show_nsfw,
|
||||
theme: inserted_sara_local_user.theme,
|
||||
default_sort_type: inserted_sara_local_user.default_sort_type,
|
||||
default_listing_type: inserted_sara_local_user.default_listing_type,
|
||||
lang: inserted_sara_local_user.lang,
|
||||
show_avatars: inserted_sara_local_user.show_avatars,
|
||||
send_notifications_to_email: inserted_sara_local_user.send_notifications_to_email,
|
||||
validator_time: inserted_sara_local_user.validator_time,
|
||||
show_bot_accounts: inserted_sara_local_user.show_bot_accounts,
|
||||
show_scores: inserted_sara_local_user.show_scores,
|
||||
show_read_posts: inserted_sara_local_user.show_read_posts,
|
||||
show_new_post_notifs: inserted_sara_local_user.show_new_post_notifs,
|
||||
email_verified: inserted_sara_local_user.email_verified,
|
||||
accepted_application: inserted_sara_local_user.accepted_application,
|
||||
},
|
||||
creator: PersonSafe {
|
||||
id: inserted_sara_person.id,
|
||||
name: inserted_sara_person.name.to_owned(),
|
||||
display_name: None,
|
||||
published: inserted_sara_person.published,
|
||||
avatar: None,
|
||||
actor_id: inserted_sara_person.actor_id.to_owned(),
|
||||
local: true,
|
||||
banned: false,
|
||||
deleted: false,
|
||||
admin: false,
|
||||
bot_account: false,
|
||||
bio: None,
|
||||
banner: None,
|
||||
updated: None,
|
||||
inbox_url: inserted_sara_person.inbox_url.to_owned(),
|
||||
shared_inbox_url: None,
|
||||
matrix_user_id: None,
|
||||
},
|
||||
admin: None,
|
||||
};
|
||||
|
||||
assert_eq!(read_sara_app_view, expected_sara_app_view);
|
||||
|
||||
// Do a batch read of the applications
|
||||
let apps = RegistrationApplicationQueryBuilder::create(&conn)
|
||||
.unread_only(true)
|
||||
.list()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
apps,
|
||||
[
|
||||
read_jess_app_view.to_owned(),
|
||||
expected_sara_app_view.to_owned()
|
||||
]
|
||||
);
|
||||
|
||||
// Make sure the counts are correct
|
||||
let unread_count = RegistrationApplicationView::get_unread_count(&conn, false).unwrap();
|
||||
assert_eq!(unread_count, 2);
|
||||
|
||||
// Approve the application
|
||||
let approve_form = RegistrationApplicationForm {
|
||||
admin_id: Some(inserted_timmy_person.id),
|
||||
deny_reason: None,
|
||||
..RegistrationApplicationForm::default()
|
||||
};
|
||||
|
||||
RegistrationApplication::update(&conn, sara_app.id, &approve_form).unwrap();
|
||||
|
||||
// Update the local_user row
|
||||
let approve_local_user_form = LocalUserForm {
|
||||
accepted_application: Some(true),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
|
||||
LocalUser::update(&conn, inserted_sara_local_user.id, &approve_local_user_form).unwrap();
|
||||
|
||||
let read_sara_app_view_after_approve =
|
||||
RegistrationApplicationView::read(&conn, sara_app.id).unwrap();
|
||||
|
||||
// Make sure the columns changed
|
||||
expected_sara_app_view
|
||||
.creator_local_user
|
||||
.accepted_application = true;
|
||||
expected_sara_app_view.registration_application.admin_id = Some(inserted_timmy_person.id);
|
||||
|
||||
expected_sara_app_view.admin = Some(PersonSafeAlias1 {
|
||||
id: inserted_timmy_person.id,
|
||||
name: inserted_timmy_person.name.to_owned(),
|
||||
display_name: None,
|
||||
published: inserted_timmy_person.published,
|
||||
avatar: None,
|
||||
actor_id: inserted_timmy_person.actor_id.to_owned(),
|
||||
local: true,
|
||||
banned: false,
|
||||
deleted: false,
|
||||
admin: true,
|
||||
bot_account: false,
|
||||
bio: None,
|
||||
banner: None,
|
||||
updated: None,
|
||||
inbox_url: inserted_timmy_person.inbox_url.to_owned(),
|
||||
shared_inbox_url: None,
|
||||
matrix_user_id: None,
|
||||
});
|
||||
assert_eq!(read_sara_app_view_after_approve, expected_sara_app_view);
|
||||
|
||||
// Do a batch read of apps again
|
||||
// It should show only jessicas which is unresolved
|
||||
let apps_after_resolve = RegistrationApplicationQueryBuilder::create(&conn)
|
||||
.unread_only(true)
|
||||
.list()
|
||||
.unwrap();
|
||||
assert_eq!(apps_after_resolve, vec![read_jess_app_view]);
|
||||
|
||||
// Make sure the counts are correct
|
||||
let unread_count_after_approve =
|
||||
RegistrationApplicationView::get_unread_count(&conn, false).unwrap();
|
||||
assert_eq!(unread_count_after_approve, 1);
|
||||
|
||||
// Make sure the not undenied_only has all the apps
|
||||
let all_apps = RegistrationApplicationQueryBuilder::create(&conn)
|
||||
.list()
|
||||
.unwrap();
|
||||
assert_eq!(all_apps.len(), 2);
|
||||
|
||||
Person::delete(&conn, inserted_timmy_person.id).unwrap();
|
||||
Person::delete(&conn, inserted_sara_person.id).unwrap();
|
||||
Person::delete(&conn, inserted_jess_person.id).unwrap();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_db_views_actor"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
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.4-rc.4", path = "../db_schema" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "../db_schema" }
|
||||
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_db_views_moderator"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
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.4-rc.4", path = "../db_schema" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "../db_schema" }
|
||||
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_routes"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -11,28 +11,28 @@ documentation = "https://join-lemmy.org/docs/en/index.html"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.4-rc.4", path = "../utils" }
|
||||
lemmy_websocket = { version = "=0.14.4-rc.4", path = "../websocket" }
|
||||
lemmy_db_views = { version = "=0.14.4-rc.4", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.14.4-rc.4", path = "../db_views_actor" }
|
||||
lemmy_db_schema = { version = "=0.14.4-rc.4", path = "../db_schema" }
|
||||
lemmy_api_common = { version = "=0.14.4-rc.4", path = "../api_common" }
|
||||
lemmy_apub = { version = "=0.14.4-rc.4", path = "../apub" }
|
||||
lemmy_utils = { version = "=0.15.0-rc.6", path = "../utils" }
|
||||
lemmy_websocket = { version = "=0.15.0-rc.6", path = "../websocket" }
|
||||
lemmy_db_views = { version = "=0.15.0-rc.6", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.15.0-rc.6", path = "../db_views_actor" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "../db_schema" }
|
||||
lemmy_api_common = { version = "=0.15.0-rc.6", path = "../api_common" }
|
||||
lemmy_apub = { version = "=0.15.0-rc.6", path = "../apub" }
|
||||
diesel = "1.4.8"
|
||||
actix = "0.12.0"
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["rustls"] }
|
||||
actix-web-actors = { version = "4.0.0-beta.7", default-features = false }
|
||||
actix-http = "3.0.0-beta.10"
|
||||
sha2 = "0.9.8"
|
||||
anyhow = "1.0.44"
|
||||
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["rustls"] }
|
||||
actix-web-actors = { version = "4.0.0-beta.8", default-features = false }
|
||||
actix-http = "3.0.0-beta.15"
|
||||
sha2 = "0.10.0"
|
||||
anyhow = "1.0.51"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
futures = "0.3.18"
|
||||
reqwest = { version = "0.11.7", features = ["stream"] }
|
||||
reqwest-middleware = "0.1.2"
|
||||
rss = "1.10.0"
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
reqwest-middleware = "0.1.3"
|
||||
rss = "2.0.0"
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
strum = "0.21.0"
|
||||
strum = "0.23.0"
|
||||
once_cell = "1.8.0"
|
||||
tracing = "0.1.29"
|
||||
tokio = { version = "1", features = ["sync"] }
|
||||
tokio = { version = "1.14.0", features = ["sync"] }
|
||||
|
|
|
@ -27,7 +27,7 @@ use rss::{
|
|||
ItemBuilder,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
use std::{collections::BTreeMap, str::FromStr};
|
||||
use strum::ParseError;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -49,8 +49,8 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||
.route("/feeds/local.xml", web::get().to(get_local_feed));
|
||||
}
|
||||
|
||||
static RSS_NAMESPACE: Lazy<HashMap<String, String>> = Lazy::new(|| {
|
||||
let mut h = HashMap::new();
|
||||
static RSS_NAMESPACE: Lazy<BTreeMap<String, String>> = Lazy::new(|| {
|
||||
let mut h = BTreeMap::new();
|
||||
h.insert(
|
||||
"dc".to_string(),
|
||||
rss::extension::dublincore::NAMESPACE.to_string(),
|
||||
|
@ -109,7 +109,7 @@ async fn get_feed_data(
|
|||
channel_builder.description(&site_desc);
|
||||
}
|
||||
|
||||
let rss = channel_builder.build().map_err(|e| anyhow!(e))?.to_string();
|
||||
let rss = channel_builder.build().to_string();
|
||||
Ok(
|
||||
HttpResponse::Ok()
|
||||
.content_type("application/rss+xml")
|
||||
|
@ -154,7 +154,7 @@ async fn get_feed(
|
|||
.await?
|
||||
.map_err(ErrorBadRequest)?;
|
||||
|
||||
let rss = builder.build().map_err(ErrorBadRequest)?.to_string();
|
||||
let rss = builder.build().to_string();
|
||||
|
||||
Ok(
|
||||
HttpResponse::Ok()
|
||||
|
@ -373,17 +373,13 @@ fn build_item(
|
|||
let dt = DateTime::<Utc>::from_utc(*published, Utc);
|
||||
i.pub_date(dt.to_rfc2822());
|
||||
i.comments(url.to_owned());
|
||||
let guid = GuidBuilder::default()
|
||||
.permalink(true)
|
||||
.value(url)
|
||||
.build()
|
||||
.map_err(|e| anyhow!(e))?;
|
||||
let guid = GuidBuilder::default().permalink(true).value(url).build();
|
||||
i.guid(guid);
|
||||
i.link(url.to_owned());
|
||||
// TODO add images
|
||||
let html = markdown_to_html(&content.to_string());
|
||||
i.description(html);
|
||||
Ok(i.build().map_err(|e| anyhow!(e))?)
|
||||
Ok(i.build())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
|
@ -410,8 +406,7 @@ fn create_post_items(
|
|||
let guid = GuidBuilder::default()
|
||||
.permalink(true)
|
||||
.value(&post_url)
|
||||
.build()
|
||||
.map_err(|e| anyhow!(e))?;
|
||||
.build();
|
||||
i.guid(guid);
|
||||
|
||||
let community_url = format!("{}/c/{}", protocol_and_hostname, p.community.name);
|
||||
|
@ -439,8 +434,8 @@ fn create_post_items(
|
|||
|
||||
i.description(description);
|
||||
|
||||
i.dublin_core_ext(dc_extension.build().map_err(|e| anyhow!(e))?);
|
||||
items.push(i.build().map_err(|e| anyhow!(e))?);
|
||||
i.dublin_core_ext(dc_extension.build());
|
||||
items.push(i.build());
|
||||
}
|
||||
|
||||
Ok(items)
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use actix_http::{
|
||||
header::{HeaderName, HOST},
|
||||
http::header::ACCEPT_ENCODING,
|
||||
};
|
||||
use actix_http::header::{HeaderName, ACCEPT_ENCODING, HOST};
|
||||
use actix_web::{body::BodyStream, http::StatusCode, web::Data, *};
|
||||
use anyhow::anyhow;
|
||||
use futures::stream::{Stream, StreamExt};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use actix_web::{body::AnyBody, error::ErrorBadRequest, *};
|
||||
use actix_web::{error::ErrorBadRequest, *};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_db_views::site_view::SiteView;
|
||||
|
@ -15,7 +15,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||
|
||||
async fn node_info_well_known(
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse<AnyBody>, LemmyError> {
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let node_info = NodeInfoWellKnown {
|
||||
links: vec![NodeInfoWellKnownLinks {
|
||||
rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0")?,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_utils"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -15,33 +15,33 @@ doctest = false
|
|||
[dependencies]
|
||||
regex = "1.5.4"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
lettre = "0.10.0-rc.3"
|
||||
lettre = "0.10.0-rc.4"
|
||||
tracing = "0.1.29"
|
||||
tracing-error = "0.2.0"
|
||||
itertools = "0.10.1"
|
||||
itertools = "0.10.3"
|
||||
rand = "0.8.4"
|
||||
percent-encoding = "2.1.0"
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde_json = { version = "1.0.68", features = ["preserve_order"] }
|
||||
thiserror = "1.0.29"
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
serde_json = { version = "1.0.72", features = ["preserve_order"] }
|
||||
thiserror = "1.0.30"
|
||||
comrak = { version = "0.12.1", default-features = false }
|
||||
once_cell = "1.8.0"
|
||||
openssl = "0.10.36"
|
||||
openssl = "0.10.38"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["rustls"] }
|
||||
actix-rt = { version = "2.2.0", default-features = false }
|
||||
anyhow = "1.0.44"
|
||||
reqwest = { version = "0.11.4", features = ["json"] }
|
||||
reqwest-middleware = "0.1.2"
|
||||
tokio = { version = "1.12.0", features = ["sync"] }
|
||||
strum = "0.21.0"
|
||||
strum_macros = "0.21.1"
|
||||
futures = "0.3.17"
|
||||
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["rustls"] }
|
||||
actix-rt = { version = "2.5.0", default-features = false }
|
||||
anyhow = "1.0.51"
|
||||
reqwest = { version = "0.11.7", features = ["json"] }
|
||||
reqwest-middleware = "0.1.3"
|
||||
tokio = { version = "1.14.0", features = ["sync"] }
|
||||
strum = "0.23.0"
|
||||
strum_macros = "0.23.1"
|
||||
futures = "0.3.18"
|
||||
diesel = "1.4.8"
|
||||
http = "0.2.5"
|
||||
deser-hjson = "1.0.2"
|
||||
smart-default = "0.6.0"
|
||||
webpage = { version = "1.3.0", default-features = false, features = ["serde"] }
|
||||
webpage = { version = "1.4.0", default-features = false, features = ["serde"] }
|
||||
jsonwebtoken = "7.2.0"
|
||||
doku = "0.10.1"
|
||||
doku = "0.10.2"
|
||||
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::settings::structs::Settings;
|
||||
use crate::{settings::structs::Settings, LemmyError};
|
||||
use lettre::{
|
||||
message::{header, Mailbox, MultiPart, SinglePart},
|
||||
transport::smtp::{
|
||||
|
@ -20,12 +20,21 @@ pub fn send_email(
|
|||
to_username: &str,
|
||||
html: &str,
|
||||
settings: &Settings,
|
||||
) -> Result<(), String> {
|
||||
let email_config = settings.email.to_owned().ok_or("no_email_setup")?;
|
||||
) -> Result<(), LemmyError> {
|
||||
let email_config = settings
|
||||
.email
|
||||
.to_owned()
|
||||
.ok_or_else(|| LemmyError::from_message("no_email_setup"))?;
|
||||
let domain = settings.hostname.to_owned();
|
||||
|
||||
let (smtp_server, smtp_port) = {
|
||||
let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
|
||||
if email_and_port.len() == 1 {
|
||||
return Err(LemmyError::from_message(
|
||||
"email.smtp_server needs a port, IE smtp.xxx.com:465",
|
||||
));
|
||||
}
|
||||
|
||||
(
|
||||
email_and_port[0],
|
||||
email_and_port[1]
|
||||
|
@ -87,6 +96,6 @@ pub fn send_email(
|
|||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
Err(e) => Err(LemmyError::from(e).with_message("email_send_failed")),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ static SETTINGS: Lazy<RwLock<Settings>> =
|
|||
Lazy::new(|| RwLock::new(Settings::init().expect("Failed to load settings file")));
|
||||
static WEBFINGER_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(&format!(
|
||||
"^acct:([a-z0-9_]{{3,}})@{}$",
|
||||
"^acct:([a-zA-Z0-9_]{{3,}})@{}$",
|
||||
Settings::get().hostname
|
||||
))
|
||||
.expect("compile webfinger regex")
|
||||
|
|
|
@ -169,8 +169,8 @@ pub struct SetupConfig {
|
|||
/// Username for the admin user
|
||||
#[doku(example = "admin")]
|
||||
pub admin_username: String,
|
||||
/// Password for the admin user
|
||||
#[doku(example = "my_passwd")]
|
||||
/// Password for the admin user. It must be at least 10 characters.
|
||||
#[doku(example = "my_passwd_longer_than_ten_characters")]
|
||||
pub admin_password: String,
|
||||
/// Name of the site (can be changed later)
|
||||
#[doku(example = "My Lemmy Instance")]
|
||||
|
@ -194,4 +194,12 @@ pub struct SetupConfig {
|
|||
pub enable_nsfw: Option<bool>,
|
||||
#[default(None)]
|
||||
pub community_creation_admin_only: Option<bool>,
|
||||
#[default(None)]
|
||||
pub require_email_verification: Option<bool>,
|
||||
#[default(None)]
|
||||
pub require_application: Option<bool>,
|
||||
#[default(None)]
|
||||
pub application_question: Option<String>,
|
||||
#[default(None)]
|
||||
pub private_instance: Option<bool>,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "lemmy_websocket"
|
||||
version = "0.14.4-rc.4"
|
||||
version = "0.15.0-rc.6"
|
||||
edition = "2018"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -13,26 +13,26 @@ path = "src/lib.rs"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { version = "=0.14.4-rc.4", path = "../utils" }
|
||||
lemmy_api_common = { version = "=0.14.4-rc.4", path = "../api_common" }
|
||||
lemmy_db_schema = { version = "=0.14.4-rc.4", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.14.4-rc.4", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.14.4-rc.4", path = "../db_views_actor" }
|
||||
reqwest = { version = "0.11.4", features = ["json"] }
|
||||
reqwest-middleware = "0.1.2"
|
||||
lemmy_utils = { version = "=0.15.0-rc.6", path = "../utils" }
|
||||
lemmy_api_common = { version = "=0.15.0-rc.6", path = "../api_common" }
|
||||
lemmy_db_schema = { version = "=0.15.0-rc.6", path = "../db_schema" }
|
||||
lemmy_db_views = { version = "=0.15.0-rc.6", path = "../db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.15.0-rc.6", path = "../db_views_actor" }
|
||||
reqwest = { version = "0.11.7", features = ["json"] }
|
||||
reqwest-middleware = "0.1.3"
|
||||
tracing = "0.1.29"
|
||||
rand = "0.8.4"
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde_json = { version = "1.0.68", features = ["preserve_order"] }
|
||||
serde = { version = "1.0.131", features = ["derive"] }
|
||||
serde_json = { version = "1.0.72", features = ["preserve_order"] }
|
||||
actix = "0.12.0"
|
||||
anyhow = "1.0.44"
|
||||
anyhow = "1.0.51"
|
||||
diesel = "1.4.8"
|
||||
background-jobs = "0.11.0"
|
||||
tokio = "1.12.0"
|
||||
strum = "0.21.0"
|
||||
strum_macros = "0.21.1"
|
||||
tokio = "1.14.0"
|
||||
strum = "0.23.0"
|
||||
strum_macros = "0.23.1"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["rustls"] }
|
||||
actix-web-actors = { version = "4.0.0-beta.7", default-features = false }
|
||||
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["rustls"] }
|
||||
actix-web-actors = { version = "4.0.0-beta.8", default-features = false }
|
||||
opentelemetry = "0.16"
|
||||
tracing-opentelemetry = "0.16"
|
||||
|
|
|
@ -97,7 +97,7 @@ where
|
|||
Ok(serde_json::to_string(&response)?)
|
||||
}
|
||||
|
||||
#[derive(EnumString, ToString, Debug, Clone)]
|
||||
#[derive(EnumString, Display, Debug, Clone)]
|
||||
pub enum UserOperation {
|
||||
Login,
|
||||
GetCaptcha,
|
||||
|
@ -117,6 +117,7 @@ pub enum UserOperation {
|
|||
ListPostReports,
|
||||
GetReportCount,
|
||||
GetUnreadCount,
|
||||
VerifyEmail,
|
||||
FollowCommunity,
|
||||
GetReplies,
|
||||
GetPersonMentions,
|
||||
|
@ -125,6 +126,9 @@ pub enum UserOperation {
|
|||
BanFromCommunity,
|
||||
AddModToCommunity,
|
||||
AddAdmin,
|
||||
GetUnreadRegistrationApplicationCount,
|
||||
ListRegistrationApplications,
|
||||
ApproveRegistrationApplication,
|
||||
BanPerson,
|
||||
Search,
|
||||
ResolveObject,
|
||||
|
@ -147,7 +151,7 @@ pub enum UserOperation {
|
|||
BlockPerson,
|
||||
}
|
||||
|
||||
#[derive(EnumString, ToString, Debug, Clone)]
|
||||
#[derive(EnumString, Display, Debug, Clone)]
|
||||
pub enum UserOperationCrud {
|
||||
// Site
|
||||
CreateSite,
|
||||
|
|
|
@ -10,6 +10,7 @@ use lemmy_api_common::{
|
|||
community::CommunityResponse,
|
||||
person::PrivateMessageResponse,
|
||||
post::PostResponse,
|
||||
send_email_to_user,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
|
||||
|
@ -28,14 +29,7 @@ use lemmy_db_views::{
|
|||
private_message_view::PrivateMessageView,
|
||||
};
|
||||
use lemmy_db_views_actor::community_view::CommunityView;
|
||||
use lemmy_utils::{
|
||||
email::send_email,
|
||||
settings::structs::Settings,
|
||||
utils::MentionData,
|
||||
ConnectionId,
|
||||
LemmyError,
|
||||
};
|
||||
use tracing::error;
|
||||
use lemmy_utils::{utils::MentionData, ConnectionId, LemmyError};
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send_post_ws_message<OP: ToString + Send + OperationType + 'static>(
|
||||
|
@ -302,40 +296,3 @@ pub async fn send_local_notifs(
|
|||
};
|
||||
Ok(recipient_ids)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn send_email_to_user(
|
||||
local_user_view: &LocalUserView,
|
||||
subject_text: &str,
|
||||
body_text: &str,
|
||||
comment_content: &str,
|
||||
settings: &Settings,
|
||||
) {
|
||||
if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(user_email) = &local_user_view.local_user.email {
|
||||
let subject = &format!(
|
||||
"{} - {} {}",
|
||||
subject_text, settings.hostname, local_user_view.person.name,
|
||||
);
|
||||
let html = &format!(
|
||||
"<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
|
||||
body_text,
|
||||
local_user_view.person.name,
|
||||
comment_content,
|
||||
settings.get_protocol_and_hostname()
|
||||
);
|
||||
match send_email(
|
||||
subject,
|
||||
user_email,
|
||||
&local_user_view.person.name,
|
||||
html,
|
||||
settings,
|
||||
) {
|
||||
Ok(_o) => _o,
|
||||
Err(e) => error!("{}", e),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ services:
|
|||
|
||||
pictrs:
|
||||
restart: always
|
||||
image: asonix/pictrs:v0.2.6-r2
|
||||
image: asonix/pictrs:0.3.0-beta.11
|
||||
user: 991:991
|
||||
volumes:
|
||||
- ./volumes/pictrs_alpha:/mnt
|
||||
|
|
|
@ -14,7 +14,7 @@ services:
|
|||
|
||||
pictrs:
|
||||
restart: always
|
||||
image: asonix/pictrs:v0.2.6-r2
|
||||
image: asonix/pictrs:0.3.0-beta.11
|
||||
user: 991:991
|
||||
volumes:
|
||||
- ./volumes/pictrs:/mnt
|
||||
|
|
|
@ -37,7 +37,7 @@ services:
|
|||
- lemmy
|
||||
|
||||
pictrs:
|
||||
image: asonix/pictrs:v0.2.6-r2
|
||||
image: asonix/pictrs:0.3.0-beta.11
|
||||
ports:
|
||||
- "127.0.0.1:8537:8080"
|
||||
user: 991:991
|
||||
|
|
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;
|
14
migrations/2021-11-23-132840_email_verification/up.sql
Normal file
14
migrations/2021-11-23-132840_email_verification/up.sql
Normal file
|
@ -0,0 +1,14 @@
|
|||
-- 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
|
||||
);
|
|
@ -0,0 +1,9 @@
|
|||
-- Add columns to site table
|
||||
alter table site drop column require_application;
|
||||
alter table site drop column application_question;
|
||||
alter table site drop column private_instance;
|
||||
|
||||
-- Add pending to local_user
|
||||
alter table local_user drop column accepted_application;
|
||||
|
||||
drop table registration_application;
|
19
migrations/2021-11-23-153753_add_invite_only_columns/up.sql
Normal file
19
migrations/2021-11-23-153753_add_invite_only_columns/up.sql
Normal file
|
@ -0,0 +1,19 @@
|
|||
-- Add columns to site table
|
||||
alter table site add column require_application boolean not null default false;
|
||||
alter table site add column application_question text;
|
||||
alter table site add column private_instance boolean not null default false;
|
||||
|
||||
-- Add pending to local_user
|
||||
alter table local_user add column accepted_application boolean not null default false;
|
||||
|
||||
create table registration_application (
|
||||
id serial primary key,
|
||||
local_user_id int references local_user on update cascade on delete cascade not null,
|
||||
answer text not null,
|
||||
admin_id int references person on update cascade on delete cascade,
|
||||
deny_reason text,
|
||||
published timestamp not null default now(),
|
||||
unique(local_user_id)
|
||||
);
|
||||
|
||||
create index idx_registration_application_published on registration_application (published desc);
|
|
@ -0,0 +1 @@
|
|||
alter table email_verification drop column published;
|
|
@ -0,0 +1 @@
|
|||
alter table email_verification add column published timestamp not null default now();
|
|
@ -210,13 +210,26 @@ 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>))
|
||||
.route("/verify_email", web::post().to(route_post::<VerifyEmail>)),
|
||||
)
|
||||
// Admin Actions
|
||||
.service(
|
||||
web::resource("/admin/add")
|
||||
web::scope("/admin")
|
||||
.wrap(rate_limit.message())
|
||||
.route(web::post().to(route_post::<AddAdmin>)),
|
||||
.route("/add", web::post().to(route_post::<AddAdmin>))
|
||||
.route(
|
||||
"/registration_application/count",
|
||||
web::get().to(route_get::<GetUnreadRegistrationApplicationCount>),
|
||||
)
|
||||
.route(
|
||||
"/registration_application/list",
|
||||
web::get().to(route_get::<ListRegistrationApplications>),
|
||||
)
|
||||
.route(
|
||||
"/registration_application/approve",
|
||||
web::put().to(route_post::<ApproveRegistrationApplication>),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use diesel::{
|
|||
};
|
||||
use doku::json::{AutoComments, Formatting};
|
||||
use lemmy_api::match_websocket_operation;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_api_common::{blocking, check_private_instance_and_federation_enabled};
|
||||
use lemmy_api_crud::match_websocket_operation_crud;
|
||||
use lemmy_apub_lib::activity_queue::create_activity_queue;
|
||||
use lemmy_db_schema::{get_database_url_from_env, source::secret::Secret};
|
||||
|
@ -103,6 +103,8 @@ async fn main() -> Result<(), LemmyError> {
|
|||
|
||||
let activity_queue = queue_manager.queue_handle().clone();
|
||||
|
||||
check_private_instance_and_federation_enabled(&pool, &settings).await?;
|
||||
|
||||
let chat_server = ChatServer::startup(
|
||||
pool.clone(),
|
||||
rate_limiter.clone(),
|
||||
|
|
Loading…
Reference in a new issue