maquina 0.4.0 → 0.5.1
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/.ruby-version +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +163 -179
- data/app/controllers/concerns/maquina/authenticate.rb +33 -13
- data/app/controllers/concerns/maquina/resourceful.rb +1 -1
- data/app/controllers/maquina/first_runs_controller.rb +43 -0
- data/app/controllers/maquina/sessions_controller.rb +22 -25
- data/app/helpers/maquina/application_helper.rb +3 -5
- data/app/models/concerns/maquina/searchable.rb +4 -0
- data/app/models/maquina/organization.rb +2 -0
- data/app/models/maquina/user.rb +2 -0
- data/app/views/layouts/maquina/sessions.html.erb +6 -4
- data/app/views/maquina/accept_invitations/new_view.rb +1 -1
- data/app/views/maquina/application/alert.rb +4 -4
- data/app/views/maquina/application/components/action_text_component.rb +1 -1
- data/app/views/maquina/application/components/checkbox_component.rb +1 -1
- data/app/views/maquina/application/components/date_component.rb +1 -1
- data/app/views/maquina/application/components/file_component.rb +1 -1
- data/app/views/maquina/application/components/input_component.rb +1 -1
- data/app/views/maquina/application/components/select_component.rb +1 -1
- data/app/views/maquina/application/components/text_area_component.rb +1 -1
- data/app/views/maquina/application/edit.rb +1 -1
- data/app/views/maquina/application/form.rb +1 -1
- data/app/views/maquina/application/index_header.rb +1 -1
- data/app/views/maquina/application/index_modal.rb +1 -1
- data/app/views/maquina/application/index_table.rb +1 -1
- data/app/views/maquina/application/new.rb +1 -1
- data/app/views/maquina/application/sessions_header.rb +5 -3
- data/app/views/maquina/application_view.rb +1 -1
- data/app/views/maquina/first_runs/form.rb +28 -0
- data/app/views/maquina/first_runs/show.html.erb +5 -0
- data/app/views/maquina/form.rb +44 -0
- data/app/views/maquina/navbar/menu.rb +1 -1
- data/app/views/maquina/navbar/menu_item_link.rb +1 -1
- data/app/views/maquina/navbar/mobile_button.rb +1 -1
- data/app/views/maquina/navbar/mobile_menu.rb +1 -1
- data/app/views/maquina/navbar/notification.rb +1 -1
- data/app/views/maquina/navbar/profile.rb +1 -1
- data/app/views/maquina/navbar/profile_button.rb +1 -1
- data/app/views/maquina/navbar/profile_menu.rb +1 -1
- data/app/views/maquina/navbar/profile_menu_item_link.rb +1 -1
- data/app/views/maquina/navbar/search.rb +1 -1
- data/app/views/maquina/navbar/title.rb +1 -1
- data/app/views/maquina/sessions/form.rb +3 -4
- data/app/views/maquina/sessions/new.html.erb +1 -4
- data/config/importmap.rb +7 -6
- data/config/locales/flash.es.yml +14 -5
- data/config/locales/forms.es.yml +13 -1
- data/config/locales/models.es.yml +1 -0
- data/config/locales/views.es.yml +8 -0
- data/config/routes.rb +1 -0
- data/lib/generators/maquina/install_stimulus_controllers/install_stimulus_controllers_generator.rb +23 -0
- data/lib/maquina/version.rb +1 -1
- data/lib/maquina.rb +2 -2
- metadata +31 -74
- data/app/views/maquina/sessions/create.turbo_stream.erb +0 -11
@@ -2,55 +2,52 @@
|
|
2
2
|
|
3
3
|
module Maquina
|
4
4
|
class SessionsController < ApplicationController
|
5
|
+
allow_unauthenticated_access only: [:new, :create]
|
6
|
+
|
5
7
|
layout "maquina/sessions"
|
6
8
|
|
9
|
+
before_action :ensure_user_exists, only: :new
|
10
|
+
|
7
11
|
def new
|
8
|
-
@
|
9
|
-
@return_to = nil if @return_to == "/" || @return_to == "%2F"
|
12
|
+
@user = Maquina::User.new
|
10
13
|
end
|
11
14
|
|
12
15
|
def create
|
13
|
-
|
14
|
-
|
15
|
-
@return_to = params.dig(:return_to)
|
16
|
-
result = create_session(user, @return_to)
|
17
|
-
return redirect_to(result, status: :see_other, format: :html) if result.present?
|
16
|
+
reset_session
|
18
17
|
|
19
|
-
|
20
|
-
flash.now.alert = t("flash.sessions.create.alert")
|
18
|
+
@user = Maquina::User.authenticate_by(email: params.dig(:email), password: params.dig(:password))
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
if @user.present?
|
21
|
+
active_session = start_new_session_for(@user)
|
22
|
+
if active_session.present?
|
23
|
+
return redirect_to(calculate_redirect_path(active_session), notice: t("flash.sessions.create.notice"), status: :see_other)
|
24
|
+
end
|
25
25
|
end
|
26
|
+
|
27
|
+
@user = Maquina::User.new(email: params.dig(:email))
|
28
|
+
flash.now.alert = t("flash.sessions.create.alert")
|
29
|
+
render :new, status: :unprocessable_entity
|
26
30
|
end
|
27
31
|
|
28
32
|
def destroy
|
29
|
-
|
33
|
+
reset_session
|
30
34
|
Maquina::Current.reset
|
31
35
|
|
32
36
|
flash.notice = t("flash.sessions.destroy.notice")
|
33
|
-
redirect_to main_app.
|
37
|
+
redirect_to main_app.sign_in_path, status: :see_other
|
34
38
|
end
|
35
39
|
|
36
40
|
private
|
37
41
|
|
42
|
+
def ensure_user_exists
|
43
|
+
redirect_to maquina.first_run_url if Maquina::User.none?
|
44
|
+
end
|
45
|
+
|
38
46
|
def calculate_redirect_path(active_session)
|
39
47
|
return maquina.new_multifactor_path if active_session.user.multifactor?
|
40
48
|
return active_session.return_url if active_session.return_url.present?
|
41
49
|
|
42
50
|
active_session.user.management? ? maquina.root_path : main_app.root_path
|
43
51
|
end
|
44
|
-
|
45
|
-
def create_session(user, return_to)
|
46
|
-
return nil if user.blank?
|
47
|
-
|
48
|
-
active_session = ActiveSession.create(user: user, user_agent: request.user_agent, remote_addr: request.remote_ip, return_url: return_to)
|
49
|
-
return nil if !active_session.persisted?
|
50
|
-
|
51
|
-
session["--active_session"] = active_session.id
|
52
|
-
flash.notice = t("flash.sessions.create.notice")
|
53
|
-
calculate_redirect_path(active_session)
|
54
|
-
end
|
55
52
|
end
|
56
53
|
end
|
@@ -2,12 +2,10 @@
|
|
2
2
|
|
3
3
|
module Maquina
|
4
4
|
module ApplicationHelper
|
5
|
-
def maquina_importmap_tags(entry_point = "application",
|
5
|
+
def maquina_importmap_tags(entry_point = "application", importmap: Maquina.configuration.importmap)
|
6
6
|
safe_join [
|
7
|
-
javascript_inline_importmap_tag(
|
8
|
-
javascript_importmap_module_preload_tags(
|
9
|
-
(javascript_importmap_shim_nonce_configuration_tag if shim),
|
10
|
-
(javascript_importmap_shim_tag if shim),
|
7
|
+
javascript_inline_importmap_tag(importmap.to_json(resolver: self)),
|
8
|
+
javascript_importmap_module_preload_tags(importmap),
|
11
9
|
javascript_import_module_tag(entry_point)
|
12
10
|
].compact, "\n"
|
13
11
|
end
|
data/app/models/maquina/user.rb
CHANGED
@@ -6,11 +6,13 @@ module Maquina
|
|
6
6
|
include Maquina::AuthenticateBy
|
7
7
|
include Maquina::Blockeable
|
8
8
|
include Maquina::Multifactor
|
9
|
+
# include Maquina::Searchable
|
9
10
|
|
10
11
|
PASSWORD_COMPLEXITY_REGEX = /\A(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#-=+])[A-Za-z\d@$!%*?&#-=+]{8,}\z/
|
11
12
|
has_secure_password
|
12
13
|
|
13
14
|
has_many :memberships, class_name: "Maquina::Membership", foreign_key: :maquina_user_id, inverse_of: :user
|
15
|
+
has_many :active_sessions, class_name: "Maquina::ActiveSession", foreign_key: :maquina_user_id, dependent: :destroy
|
14
16
|
|
15
17
|
validates :email, presence: true, uniqueness: true, format: {with: URI::MailTo::EMAIL_REGEXP}
|
16
18
|
validates :password, format: {with: PASSWORD_COMPLEXITY_REGEX}, unless: ->(user) { user.password.blank? }
|
@@ -3,18 +3,20 @@
|
|
3
3
|
<head>
|
4
4
|
<title><%= t("application_name") %></title>
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
6
7
|
<%= csrf_meta_tags %>
|
7
8
|
<%= csp_meta_tag %>
|
8
9
|
|
9
|
-
<%=
|
10
|
+
<%= yield :head %>
|
10
11
|
|
12
|
+
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
|
11
13
|
<%= maquina_importmap_tags %>
|
14
|
+
|
15
|
+
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>
|
12
16
|
</head>
|
13
17
|
|
14
18
|
<body class="h-full">
|
15
|
-
<%=
|
16
|
-
<%= render Maquina::Application::Alert.new(flash) %>
|
17
|
-
<% end %>
|
19
|
+
<%= render Maquina::Application::Alert.new(flash) %>
|
18
20
|
|
19
21
|
<main class="container mx-auto">
|
20
22
|
<%= yield %>
|
@@ -12,7 +12,7 @@ module Maquina
|
|
12
12
|
@resource = resource
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def view_template
|
16
16
|
div(class: "mt-8 sm:mx-auto sm:w-full sm:max-w-md") do
|
17
17
|
div(class: "bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10") do
|
18
18
|
@resource.new_record? ? invalid_invitation : build_form
|
@@ -6,17 +6,17 @@ module Maquina
|
|
6
6
|
include Maquina::ApplicationView
|
7
7
|
|
8
8
|
def initialize(flash)
|
9
|
-
notice = flash.notice
|
10
|
-
alert = flash.alert
|
9
|
+
notice = flash.notice || flash.now[:notice]
|
10
|
+
alert = flash.alert || flash.now[:alert]
|
11
11
|
|
12
12
|
@flash = notice || alert
|
13
13
|
@success = notice.present?
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def view_template
|
17
17
|
return if @flash.blank?
|
18
18
|
|
19
|
-
div(aria_live: "assertive", class: "pointer-events-none fixed inset-0 flex items-end px-4 py-6 sm:items-start sm:p-6 z-30", data: {controller: "alert"
|
19
|
+
div(aria_live: "assertive", class: "pointer-events-none fixed inset-0 flex items-end px-4 py-6 sm:items-start sm:p-6 z-30", data: {controller: "alert"}) do
|
20
20
|
div(class: "flex w-full flex-col items-center space-y-4 sm:items-end hidden", data: transition_attributes) do
|
21
21
|
div(class: "pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5") do
|
22
22
|
div(class: "p-4") do
|
@@ -6,7 +6,7 @@ module Maquina
|
|
6
6
|
include ApplicationView
|
7
7
|
register_element :turbo_frame
|
8
8
|
|
9
|
-
def
|
9
|
+
def view_template
|
10
10
|
div(data_controller: "modal", class: "modal", data_modal_backdrop_outlet: ".modal-backdrop") do
|
11
11
|
div(class: "hidden fixed inset-0 z-30 overflow-y-auto", aria_labelledby: "modal-title", role: "dialog",
|
12
12
|
aria_modal: "true", data_modal_target: "container") do
|
@@ -13,7 +13,7 @@ module Maquina
|
|
13
13
|
@pagination = pagination
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def view_template
|
17
17
|
div(class: "mt-8 flex flex-col") do
|
18
18
|
div(class: "-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8") do
|
19
19
|
div(class: "inline-block min-w-full py-2 align-middle md:px-6 lg:px-8") do
|
@@ -5,15 +5,17 @@ module Maquina
|
|
5
5
|
class SessionsHeader < Phlex::HTML
|
6
6
|
include Maquina::ApplicationView
|
7
7
|
include Phlex::DeferredRender
|
8
|
+
include Phlex::Rails::Helpers::Translate
|
8
9
|
|
9
|
-
def initialize(brand_icon:)
|
10
|
+
def initialize(brand_icon:, translation_key: "maquina.application.sessions_header")
|
10
11
|
@brand_icon = brand_icon
|
12
|
+
@translation_key = translation_key
|
11
13
|
end
|
12
14
|
|
13
|
-
def
|
15
|
+
def view_template
|
14
16
|
div(class: "sm:mx-auto sm:w-full sm:max-w-md") do
|
15
17
|
image_tag(@brand_icon, class: "mx-auto h-12 w-auto", alt: t("application_name"))
|
16
|
-
h2(class: "mt-6 text-center text-3xl font-bold tracking-tight text-skin-base") { t(".title") }
|
18
|
+
h2(class: "mt-6 text-center text-3xl font-bold tracking-tight text-skin-base") { t("#{@translation_key}.title") }
|
17
19
|
|
18
20
|
if @description.present?
|
19
21
|
p(class: "mt-2 text-center text-sm text-skin-dimmed") do
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Maquina
|
4
4
|
module ApplicationView
|
5
5
|
include Maquina::Engine.routes.url_helpers
|
6
|
-
include Phlex::Rails::Helpers::
|
6
|
+
include Phlex::Rails::Helpers::Translate
|
7
7
|
|
8
8
|
delegate :resource_class, :l, :default_url_options, :policy_class, :show_link, :allowed_to?, to: :helpers
|
9
9
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Maquina
|
2
|
+
module FirstRuns
|
3
|
+
class Form < Phlex::HTML
|
4
|
+
include Maquina::ApplicationView
|
5
|
+
include Maquina::Form
|
6
|
+
|
7
|
+
def initialize(resource)
|
8
|
+
@resource = resource
|
9
|
+
@scope = "first_runs"
|
10
|
+
end
|
11
|
+
|
12
|
+
def view_template
|
13
|
+
div(class: "mt-8 sm:mx-auto sm:w-full sm:max-w-md") do
|
14
|
+
div(class: "bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10") do
|
15
|
+
p(class: "text-center text-skin-muted") { t("maquina.application.first_runs_header.description") }
|
16
|
+
form_with(model: @resource, url: first_run_path, method: :post, class: "space-y-6") do |form|
|
17
|
+
text_field(form, field_name: :email, required: true)
|
18
|
+
password_field(form, field_name: :password, required: true)
|
19
|
+
div do
|
20
|
+
form.submit t("helpers.submit.first_runs.create"), class: "flex w-full justify-center button button-accented"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
2
|
+
|
3
|
+
<%= render Maquina::Application::SessionsHeader.new(brand_icon: brand_icon, translation_key: "maquina.application.first_runs_header") %>
|
4
|
+
<%= render Maquina::FirstRuns::Form.new(@user) %>
|
5
|
+
</div>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Maquina
|
2
|
+
module Form
|
3
|
+
include Phlex::Rails::Helpers::FormWith
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def text_field(form, field_name:, required: false, label_class: "block label", input_class: "w-full block input", **options)
|
8
|
+
div do
|
9
|
+
form.label field_name, class: label_class
|
10
|
+
div(class: "mt-1") do
|
11
|
+
form.text_field field_name, required: required, class: input_class,
|
12
|
+
**options.merge(field_attributes(field_name))
|
13
|
+
field_help(field_name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def password_field(form, field_name:, required: false, label_class: "block label", input_class: "w-full block input", **options)
|
19
|
+
div do
|
20
|
+
form.label field_name, class: label_class
|
21
|
+
div(class: "mt-1") do
|
22
|
+
form.password_field field_name, required: required, class: input_class,
|
23
|
+
**options.merge(field_attributes(field_name))
|
24
|
+
field_help(field_name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def field_attributes(field_name)
|
30
|
+
@scope ||= ""
|
31
|
+
{
|
32
|
+
maxlength: t("helpers.maxlength.#{@scope}.#{field_name}", default: t("helpers.maxlength.default")),
|
33
|
+
placeholder: t("placeholder.#{@scope}.#{field_name}", default: "")
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def field_help(field_name)
|
38
|
+
help = t("help.#{@scope}.#{field_name}", default: "")
|
39
|
+
if help.present?
|
40
|
+
div(class: "mt-2 text-sm text-skin-dimmed") { help }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -5,7 +5,7 @@ module Maquina
|
|
5
5
|
class MobileButton < Phlex::HTML
|
6
6
|
include ApplicationView
|
7
7
|
|
8
|
-
def
|
8
|
+
def view_template(&block)
|
9
9
|
div class: "flex items-center lg:hidden" do
|
10
10
|
button type: "button", class: "mobile-button", "aria-controls": "mobile-menu", "aria-expanded": false, "data-action": "mobile-menu#toggle" do
|
11
11
|
span(class: "sr-only") { "Open main menu" }
|
@@ -5,7 +5,7 @@ module Maquina
|
|
5
5
|
class Profile < Phlex::HTML
|
6
6
|
include ApplicationView
|
7
7
|
|
8
|
-
def
|
8
|
+
def view_template
|
9
9
|
div class: "ml-4 relative flex-shrink-0", "data-controller": "popup-menu" do
|
10
10
|
render Maquina::Navbar::ProfileButton.new
|
11
11
|
render Maquina::Navbar::ProfileMenu.new
|
@@ -5,7 +5,7 @@ module Maquina
|
|
5
5
|
class ProfileButton < Phlex::HTML
|
6
6
|
include ApplicationView
|
7
7
|
|
8
|
-
def
|
8
|
+
def view_template
|
9
9
|
div do
|
10
10
|
button type: "button", class: "bg-white rounded-full hidden lg:flex text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", id: "user-menu-button", "aria-expanded": false, "aria-haspopup": true, "data-action": "popup-menu#toggleTransition" do
|
11
11
|
span(class: "sr-only") { "Open user menu" }
|
@@ -13,7 +13,7 @@ module Maquina
|
|
13
13
|
@query = query
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def view_template
|
17
17
|
div class: "flex-1 flex items-center justify-center px-2 lg:ml-6 lg:justify-end" do
|
18
18
|
div class: "max-w-lg w-full lg:max-w-xs" do
|
19
19
|
form_with(url: @url, method: :get, data: {controller: "submit-form"}) do |form|
|
@@ -10,11 +10,10 @@ module Maquina
|
|
10
10
|
@resource = resource
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def view_template
|
14
14
|
div(class: "mt-8 sm:mx-auto sm:w-full sm:max-w-md") do
|
15
15
|
div(class: "bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10") do
|
16
|
-
form_with(url: sessions_path, method: :post, class: "space-y-6"
|
17
|
-
form.hidden_field :return_to, value: @resource.return_to
|
16
|
+
form_with(url: sessions_path, method: :post, class: "space-y-6") do |form|
|
18
17
|
div do
|
19
18
|
form.label t("form.sessions.email"), for: :email, class: "block label"
|
20
19
|
div(class: "mt-1") do
|
@@ -31,7 +30,7 @@ module Maquina
|
|
31
30
|
div do
|
32
31
|
end
|
33
32
|
div(class: "text-sm") do
|
34
|
-
a(href: "#", class: "link"
|
33
|
+
a(href: "#", class: "link") { t("form.sessions.forgot_password") }
|
35
34
|
end
|
36
35
|
end
|
37
36
|
div do
|
@@ -2,8 +2,5 @@
|
|
2
2
|
|
3
3
|
<%= render Maquina::Application::SessionsHeader.new(brand_icon: brand_icon) %>
|
4
4
|
|
5
|
-
<%=
|
6
|
-
<%= render Maquina::Sessions::Form.new(OpenStruct.new(params.slice(:email, :password, :return_to))) %>
|
7
|
-
<% end %>
|
8
|
-
|
5
|
+
<%= render Maquina::Sessions::Form.new(@user) %>
|
9
6
|
</div>
|
data/config/importmap.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
pin "application" # , to: "maquina/application.js", preload: true
|
4
|
+
|
3
5
|
# Stimulus & Turbo
|
4
|
-
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
5
|
-
pin "@hotwired/stimulus", to: "stimulus.
|
6
|
-
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
7
|
-
pin "stimulus-use", to: "https://ga.jspm.io/npm:stimulus-use@0.52.2/dist/index.js"
|
6
|
+
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
7
|
+
pin "@hotwired/stimulus", to: "@hotwired--stimulus.js" # @3.2.2
|
8
|
+
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
9
|
+
pin "stimulus-use" # , to: "https://ga.jspm.io/npm:stimulus-use@0.52.2/dist/index.js"
|
8
10
|
|
9
11
|
# Maquina entrypoint
|
10
|
-
pin "application", to: "maquina/application.js", preload: true
|
11
12
|
|
12
|
-
pin_all_from Maquina::Engine.root.join("app/assets/javascripts/maquina/controllers"), under: "controllers", to: "maquina/controllers"
|
13
|
+
pin_all_from Maquina::Engine.root.join("app/assets/javascripts/maquina/controllers"), under: "controllers" # , to: "maquina/controllers"
|