mas_handlers/views/register/steps/
display_name.rs1use anyhow::Context as _;
7use axum::{
8 Form,
9 extract::{Path, State},
10 response::{Html, IntoResponse, Response},
11};
12use mas_axum_utils::{
13 FancyError,
14 cookies::CookieJar,
15 csrf::{CsrfExt as _, ProtectedForm},
16};
17use mas_router::{PostAuthAction, UrlBuilder};
18use mas_storage::{BoxClock, BoxRepository, BoxRng};
19use mas_templates::{
20 FieldError, RegisterStepsDisplayNameContext, RegisterStepsDisplayNameFormField,
21 TemplateContext as _, Templates, ToFormState,
22};
23use serde::{Deserialize, Serialize};
24use ulid::Ulid;
25
26use crate::{PreferredLanguage, views::shared::OptionalPostAuthAction};
27
28#[derive(Deserialize, Default)]
29#[serde(rename_all = "snake_case")]
30enum FormAction {
31 #[default]
32 Set,
33 Skip,
34}
35
36#[derive(Deserialize, Serialize)]
37pub(crate) struct DisplayNameForm {
38 #[serde(skip_serializing, default)]
39 action: FormAction,
40 #[serde(default)]
41 display_name: String,
42}
43
44impl ToFormState for DisplayNameForm {
45 type Field = mas_templates::RegisterStepsDisplayNameFormField;
46}
47
48#[tracing::instrument(
49 name = "handlers.views.register.steps.display_name.get",
50 fields(user_registration.id = %id),
51 skip_all,
52)]
53pub(crate) async fn get(
54 mut rng: BoxRng,
55 clock: BoxClock,
56 PreferredLanguage(locale): PreferredLanguage,
57 State(templates): State<Templates>,
58 State(url_builder): State<UrlBuilder>,
59 mut repo: BoxRepository,
60 Path(id): Path<Ulid>,
61 cookie_jar: CookieJar,
62) -> Result<Response, FancyError> {
63 let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
64
65 let registration = repo
66 .user_registration()
67 .lookup(id)
68 .await?
69 .context("Could not find user registration")?;
70
71 if registration.completed_at.is_some() {
75 let post_auth_action: Option<PostAuthAction> = registration
76 .post_auth_action
77 .map(serde_json::from_value)
78 .transpose()?;
79
80 return Ok((
81 cookie_jar,
82 OptionalPostAuthAction::from(post_auth_action)
83 .go_next(&url_builder)
84 .into_response(),
85 )
86 .into_response());
87 }
88
89 let ctx = RegisterStepsDisplayNameContext::new()
90 .with_csrf(csrf_token.form_value())
91 .with_language(locale);
92
93 let content = templates.render_register_steps_display_name(&ctx)?;
94
95 Ok((cookie_jar, Html(content)).into_response())
96}
97
98#[tracing::instrument(
99 name = "handlers.views.register.steps.display_name.post",
100 fields(user_registration.id = %id),
101 skip_all,
102)]
103pub(crate) async fn post(
104 mut rng: BoxRng,
105 clock: BoxClock,
106 PreferredLanguage(locale): PreferredLanguage,
107 State(templates): State<Templates>,
108 State(url_builder): State<UrlBuilder>,
109 mut repo: BoxRepository,
110 Path(id): Path<Ulid>,
111 cookie_jar: CookieJar,
112 Form(form): Form<ProtectedForm<DisplayNameForm>>,
113) -> Result<Response, FancyError> {
114 let registration = repo
115 .user_registration()
116 .lookup(id)
117 .await?
118 .context("Could not find user registration")?;
119
120 if registration.completed_at.is_some() {
124 let post_auth_action: Option<PostAuthAction> = registration
125 .post_auth_action
126 .map(serde_json::from_value)
127 .transpose()?;
128
129 return Ok((
130 cookie_jar,
131 OptionalPostAuthAction::from(post_auth_action)
132 .go_next(&url_builder)
133 .into_response(),
134 )
135 .into_response());
136 }
137
138 let form = cookie_jar.verify_form(&clock, form)?;
139
140 let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
141
142 let display_name = match form.action {
143 FormAction::Set => {
144 let display_name = form.display_name.trim();
145
146 if display_name.is_empty() || display_name.len() > 255 {
147 let ctx = RegisterStepsDisplayNameContext::new()
148 .with_form_state(form.to_form_state().with_error_on_field(
149 RegisterStepsDisplayNameFormField::DisplayName,
150 FieldError::Invalid,
151 ))
152 .with_csrf(csrf_token.form_value())
153 .with_language(locale);
154
155 return Ok((
156 cookie_jar,
157 Html(templates.render_register_steps_display_name(&ctx)?),
158 )
159 .into_response());
160 }
161
162 display_name.to_owned()
163 }
164 FormAction::Skip => {
165 registration.username.clone()
168 }
169 };
170
171 let registration = repo
172 .user_registration()
173 .set_display_name(registration, display_name)
174 .await?;
175
176 repo.save().await?;
177
178 let destination = mas_router::RegisterFinish::new(registration.id);
179 return Ok((cookie_jar, url_builder.redirect(&destination)).into_response());
180}