trek 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.node-version +1 -1
- data/AGENTS.md +7 -4
- data/CHANGELOG.md +37 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +16 -3
- data/Makefile +2 -0
- data/app/components/trek/dashboard/tile_component/tile_component.css +9 -0
- data/app/components/trek/dashboard/tile_component/tile_component.html.slim +4 -2
- data/app/components/trek/dashboard/tile_component.rb +12 -2
- data/app/components/trek/form/content_editor_component/content_editor_component.css +5 -0
- data/app/components/trek/form/content_editor_component/content_editor_component.html.slim +19 -93
- data/app/components/trek/form/content_editor_component/content_editor_component.js +94 -251
- data/app/components/trek/form/content_editor_component/extensions/block_view.js +40 -0
- data/app/components/trek/form/content_editor_component/extensions/image_block.js +28 -26
- data/app/components/trek/form/content_editor_component/extensions/prompts_block.js +15 -26
- data/app/components/trek/form/content_editor_component/extensions/table_of_contents.js +1 -3
- data/app/components/trek/form/content_editor_component.rb +15 -0
- data/app/components/trek/form/image_field_component/image_field_component.css +40 -1
- data/app/components/trek/form/image_field_component/image_field_component.html.slim +0 -9
- data/app/components/trek/form/image_field_component.rb +36 -9
- data/app/content/trek/content/cleaner.rb +53 -0
- data/app/content/trek/content/document.rb +2 -2
- data/app/content/trek/content/prompts_block.rb +1 -2
- data/app/content/trek/content.rb +6 -0
- data/app/controllers/concerns/trek/reorderable.rb +24 -6
- data/app/controllers/trek/csp_reports_controller.rb +14 -1
- data/app/controllers/trek/panels/prompts_controller.rb +1 -1
- data/app/controllers/trek/resource_controller.rb +5 -5
- data/app/jobs/trek/purge_expired_sessions_job.rb +4 -3
- data/app/models/concerns/trek/contentable.rb +15 -1
- data/app/models/concerns/trek/users/sign_in_code.rb +70 -0
- data/app/models/concerns/trek/users/sign_in_lockout.rb +4 -2
- data/app/views/trek/panels/links/_form.html.slim +2 -4
- data/app/views/trek/panels/prompts/create.turbo_stream.slim +0 -1
- data/config/locales/trek.en.yml +26 -11
- data/config/locales/trek.fr.yml +26 -11
- data/docs/.vitepress/config.mjs +4 -1
- data/docs/background-jobs.md +69 -0
- data/docs/content-editor.md +219 -0
- data/docs/customize.md +2 -0
- data/docs/deploy.md +2 -2
- data/docs/getting-started.md +1 -1
- data/docs/index.md +1 -1
- data/docs/reference/components.md +2 -2
- data/docs/reference/concerns/contentable.md +10 -4
- data/docs/reference/concerns/formattable.md +1 -1
- data/docs/reference/environment-variables.md +1 -0
- data/docs/reference/formatters.md +2 -30
- data/docs/reference/forms.md +1 -1
- data/docs/reference/generators/audit-log.md +2 -2
- data/docs/reference/generators/install.md +3 -2
- data/docs/reference/generators/public-accounts.md +141 -0
- data/docs/reference/generators/security-headers.md +1 -1
- data/docs/reference/index.md +2 -1
- data/docs/reference/models/user.md +3 -1
- data/docs/what-is-trek.md +1 -1
- data/lib/generators/trek/audit_log_generator.rb +54 -0
- data/lib/generators/trek/install/admin/users_generator.rb +6 -1
- data/lib/generators/trek/install/audit_log_generator.rb +9 -45
- data/lib/generators/trek/install/good_job_generator.rb +161 -0
- data/lib/generators/trek/install/model_specs_generator.rb +3 -3
- data/lib/generators/trek/install/ruby_generator.rb +1 -1
- data/lib/generators/trek/install/scalingo_generator.rb +7 -45
- data/lib/generators/trek/install_generator.rb +15 -0
- data/lib/generators/trek/public_accounts_generator.rb +267 -0
- data/lib/generators/trek/scalingo_generator.rb +55 -0
- data/lib/generators/trek/templates/.gitlab-ci.yml +1 -1
- data/lib/generators/trek/templates/controllers/admin/password_renewals_controller.rb +11 -0
- data/lib/generators/trek/templates/controllers/admin/user_sessions_controller.rb +1 -1
- data/lib/generators/trek/templates/controllers/admin/users_controller.rb +0 -8
- data/lib/generators/trek/templates/initializers/content_security_policy.rb +3 -2
- data/lib/generators/trek/templates/initializers/good_job.rb.tt +38 -0
- data/lib/generators/trek/templates/jobs/mail_delivery_job.rb +6 -0
- data/lib/generators/trek/templates/layouts/admin.html.slim +5 -0
- data/lib/generators/trek/templates/mailers/admin_mailer.rb +4 -2
- data/lib/generators/trek/templates/package.json.tt +1 -1
- data/lib/generators/trek/templates/public_accounts/api/authentication.rb.tt +76 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/accounts_controller.rb.tt +22 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/active_sessions_controller.rb.tt +21 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/base_controller.rb.tt +71 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/email_changes_controller.rb.tt +55 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/email_confirmations_controller.rb.tt +19 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/password_resets_controller.rb.tt +28 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/passwords_controller.rb.tt +17 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/registrations_controller.rb.tt +17 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/sessions_controller.rb.tt +45 -0
- data/lib/generators/trek/templates/public_accounts/api/controllers/sign_in_codes_controller.rb.tt +15 -0
- data/lib/generators/trek/templates/public_accounts/jobs/sign_in_code_job.rb.tt +13 -0
- data/lib/generators/trek/templates/public_accounts/locales/accounts.en.yml.tt +205 -0
- data/lib/generators/trek/templates/public_accounts/locales/accounts.fr.yml.tt +205 -0
- data/lib/generators/trek/templates/public_accounts/mailers/account_mailer.rb.tt +85 -0
- data/lib/generators/trek/templates/public_accounts/migrations/create_accounts.rb.tt +46 -0
- data/lib/generators/trek/templates/public_accounts/models/account/session.rb.tt +47 -0
- data/lib/generators/trek/templates/public_accounts/models/account/sign_in_code.rb.tt +8 -0
- data/lib/generators/trek/templates/public_accounts/models/account.rb.tt +73 -0
- data/lib/generators/trek/templates/public_accounts/spec/api/accounts_spec.rb.tt +90 -0
- data/lib/generators/trek/templates/public_accounts/spec/api/sign_ins_spec.rb.tt +158 -0
- data/lib/generators/trek/templates/public_accounts/spec/models/account/sign_in_code_spec.rb.tt +56 -0
- data/lib/generators/trek/templates/public_accounts/spec/models/account_spec.rb.tt +109 -0
- data/lib/generators/trek/templates/public_accounts/spec/web/accounts_spec.rb.tt +126 -0
- data/lib/generators/trek/templates/public_accounts/spec/web/authentication_spec.rb.tt +43 -0
- data/lib/generators/trek/templates/public_accounts/spec/web/sign_ins_spec.rb.tt +215 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_change.en.html.slim.tt +10 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_change.en.text.erb.tt +8 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_change.fr.html.slim.tt +13 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_change.fr.text.erb.tt +8 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_change_code.en.html.slim.tt +9 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_change_code.en.text.erb.tt +11 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_change_code.fr.html.slim.tt +11 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_change_code.fr.text.erb.tt +11 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_confirmation.en.html.slim.tt +10 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_confirmation.en.text.erb.tt +8 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_confirmation.fr.html.slim.tt +10 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/email_confirmation.fr.text.erb.tt +8 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/password_reset.en.html.slim.tt +10 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/password_reset.en.text.erb.tt +8 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/password_reset.fr.html.slim.tt +13 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/password_reset.fr.text.erb.tt +8 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/sign_in_code.en.html.slim.tt +9 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/sign_in_code.en.text.erb.tt +11 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/sign_in_code.fr.html.slim.tt +9 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/sign_in_code.fr.text.erb.tt +11 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/sign_in_locked.en.html.slim.tt +23 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/sign_in_locked.en.text.erb.tt +14 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/sign_in_locked.fr.html.slim.tt +24 -0
- data/lib/generators/trek/templates/public_accounts/views/mailer/sign_in_locked.fr.text.erb.tt +14 -0
- data/lib/generators/trek/templates/public_accounts/web/authentication.rb.tt +97 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/accounts_controller.rb.tt +33 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/active_sessions_controller.rb.tt +15 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/base_controller.rb.tt +52 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/email_changes_controller.rb.tt +85 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/email_confirmations_controller.rb.tt +19 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/password_resets_controller.rb.tt +41 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/passwords_controller.rb.tt +23 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/registrations_controller.rb.tt +24 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/sessions_controller.rb.tt +78 -0
- data/lib/generators/trek/templates/public_accounts/web/controllers/sign_in_codes_controller.rb.tt +27 -0
- data/lib/generators/trek/templates/public_accounts/web/views/_flash.html.slim.tt +5 -0
- data/lib/generators/trek/templates/public_accounts/web/views/accounts/show.html.slim.tt +52 -0
- data/lib/generators/trek/templates/public_accounts/web/views/email_changes/edit.html.slim.tt +17 -0
- data/lib/generators/trek/templates/public_accounts/web/views/email_changes/new.html.slim.tt +15 -0
- data/lib/generators/trek/templates/public_accounts/web/views/password_resets/edit.html.slim.tt +26 -0
- data/lib/generators/trek/templates/public_accounts/web/views/password_resets/new.html.slim.tt +15 -0
- data/lib/generators/trek/templates/public_accounts/web/views/passwords/edit.html.slim.tt +25 -0
- data/lib/generators/trek/templates/public_accounts/web/views/registrations/new.html.slim.tt +28 -0
- data/lib/generators/trek/templates/public_accounts/web/views/sessions/new.html.slim.tt +38 -0
- data/lib/generators/trek/templates/public_accounts/web/views/sign_in_codes/new.html.slim.tt +17 -0
- data/lib/generators/trek/templates/spec/jobs/mail_delivery_job_spec.rb +11 -0
- data/lib/generators/trek/templates/spec/mailers/admin_mailer_spec.rb +10 -0
- data/lib/generators/trek/templates/spec/requests/admin/fragments_spec.rb +1 -1
- data/lib/generators/trek/templates/spec/requests/admin/good_job_spec.rb +34 -0
- data/lib/generators/trek/templates/spec/requests/admin/pages_spec.rb +1 -1
- data/lib/generators/trek/templates/spec/requests/admin/panels_spec.rb +16 -0
- data/lib/generators/trek/templates/spec/requests/admin/password_renewal_spec.rb +2 -2
- data/lib/generators/trek/templates/spec/requests/admin/sign_in_lockout_spec.rb +1 -1
- data/lib/generators/trek/templates/spec/requests/admin/user_sessions_spec.rb +2 -2
- data/lib/generators/trek/templates/views/admin/users/index.html.slim +1 -1
- data/lib/generators/trek/templates/views/admin_mailer/new_sign_in.en.html.slim +1 -1
- data/lib/generators/trek/templates/views/admin_mailer/new_sign_in.en.text.erb +1 -1
- data/lib/generators/trek/templates/views/admin_mailer/new_sign_in.fr.html.slim +1 -1
- data/lib/generators/trek/templates/views/admin_mailer/new_sign_in.fr.text.erb +1 -1
- data/lib/trek/csp_reports.rb +22 -4
- data/lib/trek/typography_formatter.rb +35 -4
- data/lib/trek/version.rb +1 -1
- data/package.json +12 -8
- data/trek.gemspec +1 -1
- data/vitest.config.mjs +24 -0
- data/yarn.lock +1004 -288
- metadata +90 -3
data/lib/generators/trek/templates/public_accounts/api/controllers/email_changes_controller.rb.tt
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<% if code? -%>
|
|
2
|
+
# Changing the account's email: POST { email } sends a code to the new address, PATCH { email, otp }
|
|
3
|
+
# confirms it's theirs and changes it.
|
|
4
|
+
<% else -%>
|
|
5
|
+
# Changing the account's email: POST { email } sends a link to the new address, PATCH { token } (the
|
|
6
|
+
# link's, which the app opens, possibly signed out) confirms it's theirs and changes it.
|
|
7
|
+
<% end -%>
|
|
8
|
+
class <%= controllers_module %>::EmailChangesController < <%= controllers_module %>::BaseController
|
|
9
|
+
<% if password? -%>
|
|
10
|
+
allow_unauthenticated_access only: :update
|
|
11
|
+
<% end -%>
|
|
12
|
+
rate_limit to: 10, within: 15.minutes, with: -> { render_rate_limited }
|
|
13
|
+
|
|
14
|
+
def create
|
|
15
|
+
email = normalized_email(params[:email])
|
|
16
|
+
return render_api_error :unprocessable_content, :invalid_email unless valid_email?(email) && email != Current.<%= file_name %>.email
|
|
17
|
+
return render_rate_limited if too_many_emails_to?(email)
|
|
18
|
+
|
|
19
|
+
<% if code? -%>
|
|
20
|
+
<%= accounts_module %>::SignInCodeJob.perform_later(email, purpose: "email_change", <%= file_name %>: Current.<%= file_name %>)
|
|
21
|
+
<% else -%>
|
|
22
|
+
Current.<%= file_name %>.update_column(:unconfirmed_email, email) # rubocop:disable Rails/SkipsModelValidations -- checked above
|
|
23
|
+
<%= class_name %>Mailer.email_change(Current.<%= file_name %>).deliver_later
|
|
24
|
+
<% end -%>
|
|
25
|
+
head :accepted
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Taken by another account is only told once the <%= code? ? "code" : "link" %> proved the address is theirs.
|
|
29
|
+
def update
|
|
30
|
+
<% if code? -%>
|
|
31
|
+
return render_rate_limited if too_many_failed_sign_ins_from_ip?
|
|
32
|
+
|
|
33
|
+
email = normalized_email(params[:email])
|
|
34
|
+
unless <%= class_name %>::SignInCode.redeem(email, params[:otp], purpose: "email_change", <%= file_name %>: Current.<%= file_name %>)
|
|
35
|
+
count_failed_sign_in_from_ip
|
|
36
|
+
return render_api_error :unprocessable_content, :invalid_code
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if Current.<%= file_name %>.update(email:)
|
|
40
|
+
render json: <%= file_name %>_json(Current.<%= file_name %>)
|
|
41
|
+
else
|
|
42
|
+
render_api_error :conflict, :email_taken
|
|
43
|
+
end
|
|
44
|
+
<% else -%>
|
|
45
|
+
@<%= file_name %> = <%= class_name %>.find_by_token_for(:email_change, params[:token].to_s)
|
|
46
|
+
return render_api_error :unprocessable_content, :invalid_token unless @<%= file_name %>
|
|
47
|
+
|
|
48
|
+
if @<%= file_name %>.change_email!
|
|
49
|
+
render json: <%= file_name %>_json(@<%= file_name %>)
|
|
50
|
+
else
|
|
51
|
+
render_api_error :conflict, :email_taken
|
|
52
|
+
end
|
|
53
|
+
<% end -%>
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# POST: sends the link confirming the account's email again. PATCH { token }: the token of the link,
|
|
2
|
+
# which the app opens, possibly signed out: the token is enough.
|
|
3
|
+
class <%= controllers_module %>::EmailConfirmationsController < <%= controllers_module %>::BaseController
|
|
4
|
+
allow_unauthenticated_access only: :update
|
|
5
|
+
|
|
6
|
+
def create
|
|
7
|
+
<%= file_name %> = Current.<%= file_name %>
|
|
8
|
+
<%= class_name %>Mailer.email_confirmation(<%= file_name %>).deliver_later unless <%= file_name %>.confirmed? || too_many_emails_to?(<%= file_name %>.email)
|
|
9
|
+
head :accepted
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def update
|
|
13
|
+
@<%= file_name %> = <%= class_name %>.find_by_token_for(:email_confirmation, params[:token].to_s)
|
|
14
|
+
return render_api_error :unprocessable_content, :invalid_token unless @<%= file_name %>
|
|
15
|
+
|
|
16
|
+
@<%= file_name %>.confirm_email!
|
|
17
|
+
render json: <%= file_name %>_json(@<%= file_name %>)
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/generators/trek/templates/public_accounts/api/controllers/password_resets_controller.rb.tt
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# POST { email }: emails a link to choose a new password, if the email has an account, with the same
|
|
2
|
+
# answer (202) either way. PATCH { token, password }: the token of the link, which the app opens, and
|
|
3
|
+
# the new password; signs out every session.
|
|
4
|
+
class <%= controllers_module %>::PasswordResetsController < <%= controllers_module %>::BaseController
|
|
5
|
+
allow_unauthenticated_access
|
|
6
|
+
rate_limit to: 10, within: 15.minutes, with: -> { render_rate_limited }
|
|
7
|
+
|
|
8
|
+
def create
|
|
9
|
+
email = normalized_email(params[:email])
|
|
10
|
+
@<%= file_name %> = <%= class_name %>.find_by(email:) if valid_email?(email)
|
|
11
|
+
<%= class_name %>Mailer.password_reset(@<%= file_name %>).deliver_later if @<%= file_name %> && !too_many_emails_to?(email)
|
|
12
|
+
|
|
13
|
+
head :accepted
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def update
|
|
17
|
+
@<%= file_name %> = <%= class_name %>.find_by_token_for(:password_reset, params[:token].to_s)
|
|
18
|
+
return render_api_error :unprocessable_content, :invalid_token unless @<%= file_name %>
|
|
19
|
+
|
|
20
|
+
@<%= file_name %>.password = params[:password].to_s
|
|
21
|
+
if @<%= file_name %>.save
|
|
22
|
+
@<%= file_name %>.sessions.delete_all
|
|
23
|
+
head :no_content
|
|
24
|
+
else
|
|
25
|
+
render_api_error :unprocessable_content, :invalid, errors: @<%= file_name %>.errors
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# PATCH { password }: changes the account's password, after a recent sign-in; a forgotten one goes
|
|
2
|
+
# through the password reset. Signs out the account's other sessions.
|
|
3
|
+
class <%= controllers_module %>::PasswordsController < <%= controllers_module %>::BaseController
|
|
4
|
+
before_action :require_recent_<%= file_name %>_sign_in
|
|
5
|
+
|
|
6
|
+
def update
|
|
7
|
+
<%= file_name %> = Current.<%= file_name %>
|
|
8
|
+
<%= file_name %>.password = params[:password].to_s
|
|
9
|
+
|
|
10
|
+
if <%= file_name %>.save
|
|
11
|
+
<%= file_name %>.sessions.where.not(id: Current.<%= file_name %>_session.id).delete_all
|
|
12
|
+
head :no_content
|
|
13
|
+
else
|
|
14
|
+
render_api_error :unprocessable_content, :invalid, errors: <%= file_name %>.errors
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/generators/trek/templates/public_accounts/api/controllers/registrations_controller.rb.tt
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# POST { email, password, name }: signs up and in, answering with the token; the email is confirmed
|
|
2
|
+
# later by a link (confirmed_at), which the app opens and sends to PATCH /account/confirmation.
|
|
3
|
+
class <%= controllers_module %>::RegistrationsController < <%= controllers_module %>::BaseController
|
|
4
|
+
allow_unauthenticated_access
|
|
5
|
+
rate_limit to: 5, within: 15.minutes, with: -> { render_rate_limited }
|
|
6
|
+
|
|
7
|
+
def create
|
|
8
|
+
@<%= file_name %> = <%= class_name %>.new(params.permit(:email, :name, :password))
|
|
9
|
+
|
|
10
|
+
if @<%= file_name %>.save
|
|
11
|
+
<%= class_name %>Mailer.email_confirmation(@<%= file_name %>).deliver_later
|
|
12
|
+
render_new_session @<%= file_name %>
|
|
13
|
+
else
|
|
14
|
+
render_api_error :unprocessable_content, :invalid, errors: @<%= file_name %>.errors
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<% if code? -%>
|
|
2
|
+
# POST { email, otp }: signs in with the emailed code, to the email's account, created if it had none;
|
|
3
|
+
# answers with the token for the next requests. DELETE: signs out, ending the token's session.
|
|
4
|
+
<% else -%>
|
|
5
|
+
# POST { email, password }: signs in, and answers with the token for the next requests. DELETE: signs
|
|
6
|
+
# out, ending the token's session.
|
|
7
|
+
<% end -%>
|
|
8
|
+
class <%= controllers_module %>::SessionsController < <%= controllers_module %>::BaseController
|
|
9
|
+
allow_unauthenticated_access only: :create
|
|
10
|
+
rate_limit to: <%= code? ? 10 : 5 %>, within: 3.minutes, only: :create, with: -> { render_rate_limited }
|
|
11
|
+
|
|
12
|
+
def create
|
|
13
|
+
return render_rate_limited if too_many_failed_sign_ins_from_ip?
|
|
14
|
+
|
|
15
|
+
<% if code? -%>
|
|
16
|
+
email = normalized_email(params[:email])
|
|
17
|
+
unless <%= class_name %>::SignInCode.redeem(email, params[:otp])
|
|
18
|
+
count_failed_sign_in_from_ip
|
|
19
|
+
return render_api_error :unauthorized, :invalid_code
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@<%= file_name %> = <%= class_name %>.for_verified_email!(email)
|
|
23
|
+
render_new_session @<%= file_name %>
|
|
24
|
+
<% else -%>
|
|
25
|
+
# Hashes the password sent even when no account matches, so the response takes as long
|
|
26
|
+
<%= file_name %> = <%= class_name %>.authenticate_by(email: params[:email].to_s, password: params[:password].to_s)
|
|
27
|
+
@<%= file_name %> = <%= file_name %> || <%= class_name %>.find_by(email: params[:email].to_s)
|
|
28
|
+
|
|
29
|
+
# A locked account is refused even with the right password, with the same answer as a wrong one
|
|
30
|
+
if <%= file_name %> && !<%= file_name %>.sign_in_locked?
|
|
31
|
+
<%= file_name %>.record_successful_sign_in!
|
|
32
|
+
render_new_session <%= file_name %>
|
|
33
|
+
else
|
|
34
|
+
@<%= file_name %>&.record_failed_sign_in!
|
|
35
|
+
count_failed_sign_in_from_ip
|
|
36
|
+
render_api_error :unauthorized, :invalid_password
|
|
37
|
+
end
|
|
38
|
+
<% end -%>
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def destroy
|
|
42
|
+
terminate_<%= file_name %>_session
|
|
43
|
+
head :no_content
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/generators/trek/templates/public_accounts/api/controllers/sign_in_codes_controller.rb.tt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# POST { email }: emails a code to sign in with. Whether or not the email has an account, the answer
|
|
2
|
+
# is the same (202); the code creates the account on its first use.
|
|
3
|
+
class <%= controllers_module %>::SignInCodesController < <%= controllers_module %>::BaseController
|
|
4
|
+
allow_unauthenticated_access
|
|
5
|
+
rate_limit to: 10, within: 15.minutes, with: -> { render_rate_limited }
|
|
6
|
+
|
|
7
|
+
def create
|
|
8
|
+
email = normalized_email(params[:email])
|
|
9
|
+
return render_api_error :unprocessable_content, :invalid_email unless valid_email?(email)
|
|
10
|
+
return render_rate_limited if too_many_emails_to?(email)
|
|
11
|
+
|
|
12
|
+
<%= accounts_module %>::SignInCodeJob.perform_later(email)
|
|
13
|
+
head :accepted
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Issues a code and emails it, in the job rather than the request: the code in clear only exists
|
|
2
|
+
# here, never in the job's arguments, which the queue keeps. Active Job runs it in the request's
|
|
3
|
+
# locale.
|
|
4
|
+
class <%= accounts_module %>::SignInCodeJob < ApplicationJob
|
|
5
|
+
# To sign in to `email`'s account (created on the code's first use), or for `<%= file_name %>` to
|
|
6
|
+
# change its email to `email` (purpose: "email_change").
|
|
7
|
+
def perform(email, purpose: "sign_in", <%= file_name %>: nil)
|
|
8
|
+
sign_in_code = <%= class_name %>::SignInCode.issue(email, purpose:, <%= file_name %>:)
|
|
9
|
+
mail = purpose == "email_change" ? :email_change_code : :sign_in_code
|
|
10
|
+
|
|
11
|
+
<%= class_name %>Mailer.public_send(mail, email, sign_in_code.code).deliver_now
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
en:
|
|
2
|
+
activerecord:
|
|
3
|
+
attributes:
|
|
4
|
+
<%= file_name %>:
|
|
5
|
+
email: Email
|
|
6
|
+
name: Name
|
|
7
|
+
<% if password? -%>
|
|
8
|
+
password: Password
|
|
9
|
+
unconfirmed_email: New email
|
|
10
|
+
errors:
|
|
11
|
+
models:
|
|
12
|
+
<%= file_name %>:
|
|
13
|
+
attributes:
|
|
14
|
+
password:
|
|
15
|
+
common: is among the most common passwords, choose a less predictable one
|
|
16
|
+
digit_and_symbol: must contain a digit and a symbol, unless it is at least %{count} characters long
|
|
17
|
+
personal: must not contain your name or email
|
|
18
|
+
pwned: appears in known data breaches, choose another one
|
|
19
|
+
sequence: must not contain a sequence such as 12345, azerty or aaaaa
|
|
20
|
+
<% end -%>
|
|
21
|
+
models:
|
|
22
|
+
<%= file_name %>:
|
|
23
|
+
one: <%= human_name %>
|
|
24
|
+
other: <%= human_name.pluralize %>
|
|
25
|
+
<%= file_name %>_mailer:
|
|
26
|
+
<% if code? -%>
|
|
27
|
+
email_change_code:
|
|
28
|
+
subject: Confirm your new email address
|
|
29
|
+
sign_in_code:
|
|
30
|
+
subject: Your sign-in code
|
|
31
|
+
<% else -%>
|
|
32
|
+
email_change:
|
|
33
|
+
subject: Confirm your new email address
|
|
34
|
+
email_confirmation:
|
|
35
|
+
subject: Confirm your email address
|
|
36
|
+
password_reset:
|
|
37
|
+
subject: Choose a new password
|
|
38
|
+
sign_in_locked:
|
|
39
|
+
subject: Signing in to your account is paused
|
|
40
|
+
<% end -%>
|
|
41
|
+
<% unless model_only? -%>
|
|
42
|
+
<%= accounts_path %>:
|
|
43
|
+
<% if api? -%>
|
|
44
|
+
api:
|
|
45
|
+
errors:
|
|
46
|
+
email_taken: This email is already used by another account.
|
|
47
|
+
invalid: Some fields are invalid.
|
|
48
|
+
<% if code? -%>
|
|
49
|
+
invalid_code: This code is wrong or expired. Check the last email, or ask for a new code.
|
|
50
|
+
<% end -%>
|
|
51
|
+
invalid_email: Enter a valid email address.
|
|
52
|
+
<% if password? -%>
|
|
53
|
+
invalid_password: Wrong email or password.
|
|
54
|
+
invalid_token: This link is wrong or expired. Ask for a new one.
|
|
55
|
+
<% end -%>
|
|
56
|
+
rate_limited: Too many attempts. Try again in a few minutes.
|
|
57
|
+
recent_sign_in_needed: For your security, sign in again to continue.
|
|
58
|
+
sign_in_needed: Sign in to continue.
|
|
59
|
+
<% else -%>
|
|
60
|
+
accounts:
|
|
61
|
+
destroy:
|
|
62
|
+
success: Your account is deleted.
|
|
63
|
+
show:
|
|
64
|
+
change_email: Change
|
|
65
|
+
<% if password? -%>
|
|
66
|
+
change_password: Change your password
|
|
67
|
+
<% end -%>
|
|
68
|
+
delete: Delete my account
|
|
69
|
+
delete_confirm: Delete your account for good?
|
|
70
|
+
delete_intro: Your account and everything attached to it are deleted for good.
|
|
71
|
+
delete_title: Delete your account
|
|
72
|
+
<% if password? -%>
|
|
73
|
+
pending_email: "%{email} will replace it once you follow the link we sent there."
|
|
74
|
+
resend_confirmation: Send the confirmation link again
|
|
75
|
+
<% end -%>
|
|
76
|
+
save: Save
|
|
77
|
+
sessions: Signed-in browsers
|
|
78
|
+
sign_out: Sign out
|
|
79
|
+
sign_out_others: Sign out of all other browsers
|
|
80
|
+
sign_out_session: Sign out
|
|
81
|
+
signed_in: signed in on %{date}
|
|
82
|
+
this_browser: "(this browser)"
|
|
83
|
+
title: Your account
|
|
84
|
+
<% if password? -%>
|
|
85
|
+
unconfirmed: This email isn't confirmed yet. Follow the link we sent to it.
|
|
86
|
+
<% end -%>
|
|
87
|
+
update:
|
|
88
|
+
success: Your account is saved.
|
|
89
|
+
active_sessions:
|
|
90
|
+
destroy:
|
|
91
|
+
success: The browser is signed out.
|
|
92
|
+
destroy_others:
|
|
93
|
+
success: The other browsers are signed out.
|
|
94
|
+
authentication:
|
|
95
|
+
rate_limited: Too many attempts. Try again in a few minutes.
|
|
96
|
+
recent_sign_in_needed: For your security, sign in again to continue.
|
|
97
|
+
sign_in_needed: Sign in to continue.
|
|
98
|
+
email_changes:
|
|
99
|
+
create:
|
|
100
|
+
invalid_email: Enter a valid email address, other than your current one.
|
|
101
|
+
<% if code? -%>
|
|
102
|
+
sent: We sent a code to %{email}.
|
|
103
|
+
edit:
|
|
104
|
+
back: Back to your account
|
|
105
|
+
code: Code
|
|
106
|
+
intro: Enter the code we sent to %{email}.
|
|
107
|
+
submit: Confirm
|
|
108
|
+
title: Confirm your new email
|
|
109
|
+
<% else -%>
|
|
110
|
+
sent: We sent a link to %{email}. Your email changes once you follow it.
|
|
111
|
+
<% end -%>
|
|
112
|
+
new:
|
|
113
|
+
back: Back to your account
|
|
114
|
+
email: New email
|
|
115
|
+
<% if code? -%>
|
|
116
|
+
intro: We'll send a code to the new address to confirm it's yours.
|
|
117
|
+
submit: Send me a code
|
|
118
|
+
<% else -%>
|
|
119
|
+
intro: We'll send a link to the new address to confirm it's yours.
|
|
120
|
+
submit: Send me a link
|
|
121
|
+
<% end -%>
|
|
122
|
+
title: Change your email
|
|
123
|
+
<% if code? -%>
|
|
124
|
+
update:
|
|
125
|
+
failure: This code is wrong or expired.
|
|
126
|
+
success: Your email is changed.
|
|
127
|
+
taken: This email is already used by another account.
|
|
128
|
+
<% else -%>
|
|
129
|
+
show:
|
|
130
|
+
invalid: This link is wrong or expired. Ask for a new one from your account.
|
|
131
|
+
success: Your email is changed.
|
|
132
|
+
taken: This email is already used by another account.
|
|
133
|
+
email_confirmations:
|
|
134
|
+
create:
|
|
135
|
+
sent: We sent a confirmation link to %{email}.
|
|
136
|
+
show:
|
|
137
|
+
invalid: This link is wrong or expired. Ask for a new one from your account.
|
|
138
|
+
success: Your email is confirmed.
|
|
139
|
+
password_resets:
|
|
140
|
+
create:
|
|
141
|
+
sent: If this email has an account, we sent it a link to choose a new password.
|
|
142
|
+
edit:
|
|
143
|
+
rules: At least %{min} characters, with a digit and a symbol unless it is at least %{passphrase} characters long.
|
|
144
|
+
submit: Save
|
|
145
|
+
title: Choose a new password
|
|
146
|
+
invalid: This link is wrong or expired. Ask for a new one.
|
|
147
|
+
new:
|
|
148
|
+
intro: Enter your email. We'll send you a link to choose a new password.
|
|
149
|
+
sign_in: Back to signing in
|
|
150
|
+
submit: Send me a link
|
|
151
|
+
title: Forgot your password?
|
|
152
|
+
update:
|
|
153
|
+
success: Your password is saved. Sign in with it.
|
|
154
|
+
passwords:
|
|
155
|
+
edit:
|
|
156
|
+
back: Back to your account
|
|
157
|
+
intro: At least %{min} characters, with a digit and a symbol unless it is at least %{passphrase} characters long.
|
|
158
|
+
submit: Save
|
|
159
|
+
title: Your password
|
|
160
|
+
update:
|
|
161
|
+
success: Your password is saved. Your other browsers are signed out.
|
|
162
|
+
registrations:
|
|
163
|
+
create:
|
|
164
|
+
success: Welcome! We sent a link to %{email} to confirm it.
|
|
165
|
+
new:
|
|
166
|
+
rules: At least %{min} characters, with a digit and a symbol unless it is at least %{passphrase} characters long.
|
|
167
|
+
sign_in: I already have an account
|
|
168
|
+
submit: Sign up
|
|
169
|
+
title: Create your account
|
|
170
|
+
<% end -%>
|
|
171
|
+
sessions:
|
|
172
|
+
create:
|
|
173
|
+
<% if code? -%>
|
|
174
|
+
failure: This code is wrong or expired. Check the last email, or ask for a new code.
|
|
175
|
+
<% else -%>
|
|
176
|
+
failure: Wrong email or password.
|
|
177
|
+
<% end -%>
|
|
178
|
+
success: You're signed in.
|
|
179
|
+
destroy:
|
|
180
|
+
success: You're signed out.
|
|
181
|
+
new:
|
|
182
|
+
<% if code? -%>
|
|
183
|
+
code: Code
|
|
184
|
+
intro: We sent a 6-digit code to %{email}. It is valid for %{validity}.
|
|
185
|
+
other_email: Use another email
|
|
186
|
+
submit: Sign in
|
|
187
|
+
title: Enter your code
|
|
188
|
+
<% else -%>
|
|
189
|
+
forgot_password: Forgot your password?
|
|
190
|
+
sign_up: Create an account
|
|
191
|
+
submit: Sign in
|
|
192
|
+
title: Sign in
|
|
193
|
+
<% end -%>
|
|
194
|
+
<% if code? -%>
|
|
195
|
+
sign_in_codes:
|
|
196
|
+
create:
|
|
197
|
+
invalid_email: Enter a valid email address.
|
|
198
|
+
sent: We sent a code to %{email}.
|
|
199
|
+
new:
|
|
200
|
+
intro: Enter your email. We'll send you a code to sign in, or to create your account if you don't have one yet.
|
|
201
|
+
submit: Send me a code
|
|
202
|
+
title: Sign in
|
|
203
|
+
<% end -%>
|
|
204
|
+
<% end -%>
|
|
205
|
+
<% end -%>
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
fr:
|
|
2
|
+
activerecord:
|
|
3
|
+
attributes:
|
|
4
|
+
<%= file_name %>:
|
|
5
|
+
email: Adresse e-mail
|
|
6
|
+
name: Nom
|
|
7
|
+
<% if password? -%>
|
|
8
|
+
password: Mot de passe
|
|
9
|
+
unconfirmed_email: Nouvelle adresse e-mail
|
|
10
|
+
errors:
|
|
11
|
+
models:
|
|
12
|
+
<%= file_name %>:
|
|
13
|
+
attributes:
|
|
14
|
+
password:
|
|
15
|
+
common: fait partie des mots de passe les plus courants, choisissez-en un moins prévisible
|
|
16
|
+
digit_and_symbol: doit contenir un chiffre et un caractère spécial, sauf s'il fait au moins %{count} caractères
|
|
17
|
+
personal: ne doit pas contenir votre nom ni votre adresse e-mail
|
|
18
|
+
pwned: figure dans des fuites de données connues, choisissez-en un autre
|
|
19
|
+
sequence: ne doit pas contenir de suite comme 12345, azerty ou aaaaa
|
|
20
|
+
<% end -%>
|
|
21
|
+
models:
|
|
22
|
+
<%= file_name %>:
|
|
23
|
+
one: <%= human_name %>
|
|
24
|
+
other: <%= human_name.pluralize %>
|
|
25
|
+
<%= file_name %>_mailer:
|
|
26
|
+
<% if code? -%>
|
|
27
|
+
email_change_code:
|
|
28
|
+
subject: Confirmez votre nouvelle adresse e-mail
|
|
29
|
+
sign_in_code:
|
|
30
|
+
subject: Votre code de connexion
|
|
31
|
+
<% else -%>
|
|
32
|
+
email_change:
|
|
33
|
+
subject: Confirmez votre nouvelle adresse e-mail
|
|
34
|
+
email_confirmation:
|
|
35
|
+
subject: Confirmez votre adresse e-mail
|
|
36
|
+
password_reset:
|
|
37
|
+
subject: Choisissez un nouveau mot de passe
|
|
38
|
+
sign_in_locked:
|
|
39
|
+
subject: La connexion à votre compte est suspendue
|
|
40
|
+
<% end -%>
|
|
41
|
+
<% unless model_only? -%>
|
|
42
|
+
<%= accounts_path %>:
|
|
43
|
+
<% if api? -%>
|
|
44
|
+
api:
|
|
45
|
+
errors:
|
|
46
|
+
email_taken: Cette adresse e-mail est déjà utilisée par un autre compte.
|
|
47
|
+
invalid: Certains champs ne sont pas valides.
|
|
48
|
+
<% if code? -%>
|
|
49
|
+
invalid_code: Ce code est erroné ou expiré. Vérifiez le dernier e-mail, ou demandez un nouveau code.
|
|
50
|
+
<% end -%>
|
|
51
|
+
invalid_email: Saisissez une adresse e-mail valide.
|
|
52
|
+
<% if password? -%>
|
|
53
|
+
invalid_password: Adresse e-mail ou mot de passe incorrect.
|
|
54
|
+
invalid_token: Ce lien est erroné ou expiré. Demandez-en un nouveau.
|
|
55
|
+
<% end -%>
|
|
56
|
+
rate_limited: Trop de tentatives. Réessayez dans quelques minutes.
|
|
57
|
+
recent_sign_in_needed: Pour votre sécurité, reconnectez-vous pour continuer.
|
|
58
|
+
sign_in_needed: Connectez-vous pour continuer.
|
|
59
|
+
<% else -%>
|
|
60
|
+
accounts:
|
|
61
|
+
destroy:
|
|
62
|
+
success: Votre compte est supprimé.
|
|
63
|
+
show:
|
|
64
|
+
change_email: Changer
|
|
65
|
+
<% if password? -%>
|
|
66
|
+
change_password: Changer votre mot de passe
|
|
67
|
+
<% end -%>
|
|
68
|
+
delete: Supprimer mon compte
|
|
69
|
+
delete_confirm: Supprimer votre compte définitivement ?
|
|
70
|
+
delete_intro: Votre compte et tout ce qui y est rattaché sont supprimés définitivement.
|
|
71
|
+
delete_title: Supprimer votre compte
|
|
72
|
+
<% if password? -%>
|
|
73
|
+
pending_email: "%{email} la remplacera une fois le lien envoyé à cette adresse suivi."
|
|
74
|
+
resend_confirmation: Renvoyer le lien de confirmation
|
|
75
|
+
<% end -%>
|
|
76
|
+
save: Enregistrer
|
|
77
|
+
sessions: Navigateurs connectés
|
|
78
|
+
sign_out: Se déconnecter
|
|
79
|
+
sign_out_others: Déconnecter tous les autres navigateurs
|
|
80
|
+
sign_out_session: Déconnecter
|
|
81
|
+
signed_in: connecté le %{date}
|
|
82
|
+
this_browser: "(ce navigateur)"
|
|
83
|
+
title: Votre compte
|
|
84
|
+
<% if password? -%>
|
|
85
|
+
unconfirmed: Cette adresse n'est pas encore confirmée. Suivez le lien que nous y avons envoyé.
|
|
86
|
+
<% end -%>
|
|
87
|
+
update:
|
|
88
|
+
success: Votre compte est enregistré.
|
|
89
|
+
active_sessions:
|
|
90
|
+
destroy:
|
|
91
|
+
success: Le navigateur est déconnecté.
|
|
92
|
+
destroy_others:
|
|
93
|
+
success: Les autres navigateurs sont déconnectés.
|
|
94
|
+
authentication:
|
|
95
|
+
rate_limited: Trop de tentatives. Réessayez dans quelques minutes.
|
|
96
|
+
recent_sign_in_needed: Pour votre sécurité, reconnectez-vous pour continuer.
|
|
97
|
+
sign_in_needed: Connectez-vous pour continuer.
|
|
98
|
+
email_changes:
|
|
99
|
+
create:
|
|
100
|
+
invalid_email: Saisissez une adresse e-mail valide, autre que l'actuelle.
|
|
101
|
+
<% if code? -%>
|
|
102
|
+
sent: Nous avons envoyé un code à %{email}.
|
|
103
|
+
edit:
|
|
104
|
+
back: Retour à votre compte
|
|
105
|
+
code: Code
|
|
106
|
+
intro: Saisissez le code envoyé à %{email}.
|
|
107
|
+
submit: Confirmer
|
|
108
|
+
title: Confirmez votre nouvelle adresse
|
|
109
|
+
<% else -%>
|
|
110
|
+
sent: Nous avons envoyé un lien à %{email}. Votre adresse changera une fois ce lien suivi.
|
|
111
|
+
<% end -%>
|
|
112
|
+
new:
|
|
113
|
+
back: Retour à votre compte
|
|
114
|
+
email: Nouvelle adresse e-mail
|
|
115
|
+
<% if code? -%>
|
|
116
|
+
intro: Nous enverrons un code à la nouvelle adresse pour confirmer qu'elle est bien à vous.
|
|
117
|
+
submit: Recevoir un code
|
|
118
|
+
<% else -%>
|
|
119
|
+
intro: Nous enverrons un lien à la nouvelle adresse pour confirmer qu'elle est bien à vous.
|
|
120
|
+
submit: Recevoir un lien
|
|
121
|
+
<% end -%>
|
|
122
|
+
title: Changer votre adresse e-mail
|
|
123
|
+
<% if code? -%>
|
|
124
|
+
update:
|
|
125
|
+
failure: Ce code est erroné ou expiré.
|
|
126
|
+
success: Votre adresse e-mail est changée.
|
|
127
|
+
taken: Cette adresse e-mail est déjà utilisée par un autre compte.
|
|
128
|
+
<% else -%>
|
|
129
|
+
show:
|
|
130
|
+
invalid: Ce lien est erroné ou expiré. Demandez-en un nouveau depuis votre compte.
|
|
131
|
+
success: Votre adresse e-mail est changée.
|
|
132
|
+
taken: Cette adresse e-mail est déjà utilisée par un autre compte.
|
|
133
|
+
email_confirmations:
|
|
134
|
+
create:
|
|
135
|
+
sent: Nous avons envoyé un lien de confirmation à %{email}.
|
|
136
|
+
show:
|
|
137
|
+
invalid: Ce lien est erroné ou expiré. Demandez-en un nouveau depuis votre compte.
|
|
138
|
+
success: Votre adresse e-mail est confirmée.
|
|
139
|
+
password_resets:
|
|
140
|
+
create:
|
|
141
|
+
sent: Si cette adresse a un compte, nous lui avons envoyé un lien pour choisir un nouveau mot de passe.
|
|
142
|
+
edit:
|
|
143
|
+
rules: Au moins %{min} caractères, avec un chiffre et un caractère spécial s'il fait moins de %{passphrase} caractères.
|
|
144
|
+
submit: Enregistrer
|
|
145
|
+
title: Choisissez un nouveau mot de passe
|
|
146
|
+
invalid: Ce lien est erroné ou expiré. Demandez-en un nouveau.
|
|
147
|
+
new:
|
|
148
|
+
intro: Saisissez votre adresse e-mail. Nous vous enverrons un lien pour choisir un nouveau mot de passe.
|
|
149
|
+
sign_in: Retour à la connexion
|
|
150
|
+
submit: Recevoir un lien
|
|
151
|
+
title: Mot de passe oublié ?
|
|
152
|
+
update:
|
|
153
|
+
success: Votre mot de passe est enregistré. Connectez-vous avec.
|
|
154
|
+
passwords:
|
|
155
|
+
edit:
|
|
156
|
+
back: Retour à votre compte
|
|
157
|
+
intro: Au moins %{min} caractères, avec un chiffre et un caractère spécial s'il fait moins de %{passphrase} caractères.
|
|
158
|
+
submit: Enregistrer
|
|
159
|
+
title: Votre mot de passe
|
|
160
|
+
update:
|
|
161
|
+
success: Votre mot de passe est enregistré. Vos autres navigateurs sont déconnectés.
|
|
162
|
+
registrations:
|
|
163
|
+
create:
|
|
164
|
+
success: Bienvenue ! Nous avons envoyé un lien à %{email} pour la confirmer.
|
|
165
|
+
new:
|
|
166
|
+
rules: Au moins %{min} caractères, avec un chiffre et un caractère spécial s'il fait moins de %{passphrase} caractères.
|
|
167
|
+
sign_in: J'ai déjà un compte
|
|
168
|
+
submit: Créer mon compte
|
|
169
|
+
title: Créer votre compte
|
|
170
|
+
<% end -%>
|
|
171
|
+
sessions:
|
|
172
|
+
create:
|
|
173
|
+
<% if code? -%>
|
|
174
|
+
failure: Ce code est erroné ou expiré. Vérifiez le dernier e-mail, ou demandez un nouveau code.
|
|
175
|
+
<% else -%>
|
|
176
|
+
failure: Adresse e-mail ou mot de passe incorrect.
|
|
177
|
+
<% end -%>
|
|
178
|
+
success: Vous êtes connecté.
|
|
179
|
+
destroy:
|
|
180
|
+
success: Vous êtes déconnecté.
|
|
181
|
+
new:
|
|
182
|
+
<% if code? -%>
|
|
183
|
+
code: Code
|
|
184
|
+
intro: Nous avons envoyé un code à 6 chiffres à %{email}. Il est valable %{validity}.
|
|
185
|
+
other_email: Utiliser une autre adresse
|
|
186
|
+
submit: Se connecter
|
|
187
|
+
title: Saisissez votre code
|
|
188
|
+
<% else -%>
|
|
189
|
+
forgot_password: Mot de passe oublié ?
|
|
190
|
+
sign_up: Créer un compte
|
|
191
|
+
submit: Se connecter
|
|
192
|
+
title: Se connecter
|
|
193
|
+
<% end -%>
|
|
194
|
+
<% if code? -%>
|
|
195
|
+
sign_in_codes:
|
|
196
|
+
create:
|
|
197
|
+
invalid_email: Saisissez une adresse e-mail valide.
|
|
198
|
+
sent: Nous avons envoyé un code à %{email}.
|
|
199
|
+
new:
|
|
200
|
+
intro: Saisissez votre adresse e-mail. Nous vous enverrons un code pour vous connecter, ou pour créer votre compte si vous n'en avez pas encore.
|
|
201
|
+
submit: Recevoir un code
|
|
202
|
+
title: Se connecter
|
|
203
|
+
<% end -%>
|
|
204
|
+
<% end -%>
|
|
205
|
+
<% end -%>
|