1 use crate::PerformCrud;
2 use actix_web::web::Data;
3 use lemmy_api_common::{
8 send_verification_email,
11 generate_followers_url,
13 generate_local_apub_endpoint,
14 generate_shared_inbox_url,
17 use lemmy_db_schema::{
18 aggregates::person_aggregates::PersonAggregates,
19 newtypes::CommunityId,
24 CommunityFollowerForm,
27 CommunityModeratorForm,
29 local_user::{LocalUser, LocalUserForm},
30 person::{Person, PersonForm},
31 registration_application::{RegistrationApplication, RegistrationApplicationForm},
34 traits::{Crud, Followable, Joinable},
36 use lemmy_db_views::local_user_view::LocalUserView;
37 use lemmy_db_views_actor::person_view::PersonViewSafe;
39 apub::generate_actor_keypair,
41 utils::{check_slurs, is_valid_actor_name},
45 use lemmy_websocket::{messages::CheckCaptcha, LemmyContext};
47 #[async_trait::async_trait(?Send)]
48 impl PerformCrud for Register {
49 type Response = LoginResponse;
51 #[tracing::instrument(skip(self, context, _websocket_id))]
54 context: &Data<LemmyContext>,
55 _websocket_id: Option<ConnectionId>,
56 ) -> Result<LoginResponse, LemmyError> {
57 let data: &Register = self;
59 // no email verification, or applications if the site is not setup yet
60 let (mut email_verification, mut require_application) = (false, false);
62 // Make sure site has open registration
63 if let Ok(site) = blocking(context.pool(), Site::read_local_site).await? {
64 if !site.open_registration {
65 return Err(LemmyError::from_message("registration_closed"));
67 email_verification = site.require_email_verification;
68 require_application = site.require_application;
71 password_length_check(&data.password)?;
72 honeypot_check(&data.honeypot)?;
74 if email_verification && data.email.is_none() {
75 return Err(LemmyError::from_message("email_required"));
78 if require_application && data.answer.is_none() {
79 return Err(LemmyError::from_message(
80 "registration_application_answer_required",
84 // Make sure passwords match
85 if data.password != data.password_verify {
86 return Err(LemmyError::from_message("passwords_dont_match"));
89 // Check if there are admins. False if admins exist
90 let no_admins = blocking(context.pool(), move |conn| {
91 PersonViewSafe::admins(conn).map(|a| a.is_empty())
95 // If its not the admin, check the captcha
96 if !no_admins && context.settings().captcha.enabled {
103 .unwrap_or_else(|| "".to_string()),
107 .unwrap_or_else(|| "".to_string()),
111 return Err(LemmyError::from_message("captcha_incorrect"));
115 check_slurs(&data.username, &context.settings().slur_regex())?;
117 let actor_keypair = generate_actor_keypair()?;
118 if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
119 return Err(LemmyError::from_message("invalid_username"));
121 let actor_id = generate_local_apub_endpoint(
122 EndpointType::Person,
124 &context.settings().get_protocol_and_hostname(),
127 // We have to create both a person, and local_user
129 // Register the new person
130 let person_form = PersonForm {
131 name: data.username.to_owned(),
132 actor_id: Some(actor_id.clone()),
133 private_key: Some(Some(actor_keypair.private_key)),
134 public_key: actor_keypair.public_key,
135 inbox_url: Some(generate_inbox_url(&actor_id)?),
136 shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
137 admin: Some(no_admins),
138 ..PersonForm::default()
142 let inserted_person = blocking(context.pool(), move |conn| {
143 Person::create(conn, &person_form)
146 .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
148 // Create the local user
149 let local_user_form = LocalUserForm {
150 person_id: Some(inserted_person.id),
151 email: Some(data.email.as_deref().map(|s| s.to_owned())),
152 password_encrypted: Some(data.password.to_string()),
153 show_nsfw: Some(data.show_nsfw),
154 email_verified: Some(false),
155 ..LocalUserForm::default()
158 let inserted_local_user = match blocking(context.pool(), move |conn| {
159 LocalUser::register(conn, &local_user_form)
165 let err_type = if e.to_string()
166 == "duplicate key value violates unique constraint \"local_user_email_key\""
168 "email_already_exists"
170 "user_already_exists"
173 // If the local user creation errored, then delete that person
174 blocking(context.pool(), move |conn| {
175 Person::delete(conn, inserted_person.id)
179 return Err(LemmyError::from_error_message(e, err_type));
183 if require_application {
184 // Create the registration application
185 let form = RegistrationApplicationForm {
186 local_user_id: Some(inserted_local_user.id),
187 // We already made sure answer was not null above
188 answer: data.answer.to_owned(),
189 ..RegistrationApplicationForm::default()
192 blocking(context.pool(), move |conn| {
193 RegistrationApplication::create(conn, &form)
198 let main_community_keypair = generate_actor_keypair()?;
200 // Create the main community if it doesn't exist
201 let protocol_and_hostname = context.settings().get_protocol_and_hostname();
202 let main_community = match blocking(context.pool(), move |conn| {
203 Community::read(conn, CommunityId(2))
209 let default_community_name = "main";
210 let actor_id = generate_local_apub_endpoint(
211 EndpointType::Community,
212 default_community_name,
213 &protocol_and_hostname,
215 let community_form = CommunityForm {
216 name: default_community_name.to_string(),
217 title: "The Default Community".to_string(),
218 description: Some("The Default Community".to_string()),
219 actor_id: Some(actor_id.to_owned()),
220 private_key: Some(Some(main_community_keypair.private_key)),
221 public_key: main_community_keypair.public_key,
222 followers_url: Some(generate_followers_url(&actor_id)?),
223 inbox_url: Some(generate_inbox_url(&actor_id)?),
224 shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
225 ..CommunityForm::default()
227 blocking(context.pool(), move |conn| {
228 Community::create(conn, &community_form)
234 // Sign them up for main community no matter what
235 let community_follower_form = CommunityFollowerForm {
236 community_id: main_community.id,
237 person_id: inserted_person.id,
241 let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
242 blocking(context.pool(), follow)
244 .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
246 // If its an admin, add them as a mod and follower to main
248 let community_moderator_form = CommunityModeratorForm {
249 community_id: main_community.id,
250 person_id: inserted_person.id,
253 let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
254 blocking(context.pool(), join)
256 .map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?;
259 let mut login_response = LoginResponse {
261 registration_created: false,
262 verify_email_sent: false,
265 // Log the user in directly if email verification and application aren't required
266 if !require_application && !email_verification {
267 login_response.jwt = Some(
269 inserted_local_user.id.0,
270 &context.secret().jwt_secret,
271 &context.settings().hostname,
276 if email_verification {
277 let local_user_view = LocalUserView {
278 local_user: inserted_local_user,
279 person: inserted_person,
280 counts: PersonAggregates::default(),
282 // we check at the beginning of this method that email is set
283 let email = local_user_view
287 .expect("email was provided");
288 send_verification_email(
295 login_response.verify_email_sent = true;
298 if require_application {
299 login_response.registration_created = true;