doido_auth/controllers/
registrations.rs1use crate::handlers::{register_user, sign_in};
4use crate::user::{AuthUser, RegisterableAuthUser};
5use doido_auth_macros::auth_controller;
6use doido_controller::respond::Format;
7use doido_core::Result;
8use doido_model::password::HasSecurePassword;
9use serde::Deserialize;
10use serde::Serialize;
11use std::marker::PhantomData;
12
13pub struct AuthRegistrations<U>(PhantomData<U>);
15
16#[derive(Debug, Deserialize)]
17pub struct SignUpForm {
18 pub email: String,
19 pub password: String,
20 #[serde(default)]
21 pub password_confirmation: Option<String>,
22}
23
24#[auth_controller]
25impl<U> AuthRegistrations<U>
26where
27 U: AuthUser + HasSecurePassword + RegisterableAuthUser + Serialize + Send + Sync + 'static,
28{
29 pub async fn new(ctx: doido_controller::Context) -> doido_controller::Response {
31 ctx.render("auth/sign_up", serde_json::json!({}))
32 }
33
34 pub async fn create(mut ctx: doido_controller::Context) -> Result<doido_controller::Response> {
36 let json = ctx.negotiated_format() == Format::Json;
37 let form: SignUpForm = if json {
38 ctx.body_json().await?
39 } else {
40 ctx.form().await?
41 };
42
43 if let Some(ref confirm) = form.password_confirmation {
44 if form.password != *confirm {
45 return registration_error(ctx, json, "Password confirmation does not match");
46 }
47 }
48
49 let db = ctx.db().clone();
50 let user =
51 match register_user::<U, _, _>(&db, &form.email, &form.password, |email, digest| {
52 let db = db.clone();
53 async move { U::register(&db, email, digest).await }
54 })
55 .await
56 {
57 Ok(user) => user,
58 Err(crate::error::AuthError::EmailTaken) => {
59 return registration_error(ctx, json, "Email has already been taken");
60 }
61 Err(e) => return Err(doido_core::anyhow::anyhow!(e.to_string())),
62 };
63
64 sign_in(ctx, &user)?;
65 if json {
66 Ok(ctx.json(user))
67 } else {
68 Ok(ctx.redirect_to("/"))
69 }
70 }
71}
72
73fn registration_error(
74 ctx: &doido_controller::Context,
75 json: bool,
76 message: &str,
77) -> Result<doido_controller::Response> {
78 if json {
79 Ok(ctx.status(422))
80 } else {
81 Ok(ctx.render("auth/sign_up", serde_json::json!({ "error": message })))
82 }
83}