moduler 0.1.0 → 0.3.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/app/assets/builds/moduler/tailwind.css +1359 -0
- data/app/assets/config/moduler/manifest.js +2 -0
- data/app/assets/javascripts/moduler/application.js +9 -0
- data/app/assets/javascripts/moduler/controllers/flash_controller.js +19 -0
- data/app/assets/stylesheets/moduler/tailwind.css +21 -0
- data/app/components/moduler/administration/page_component.html.erb +7 -0
- data/app/components/moduler/administration/page_component.rb +6 -0
- data/app/components/moduler/badge_component.html.erb +8 -0
- data/app/components/moduler/badge_component.rb +10 -0
- data/app/components/moduler/button_component.html.erb +7 -0
- data/app/components/moduler/button_component.rb +19 -0
- data/app/components/moduler/button_link_component.html.erb +5 -0
- data/app/components/moduler/button_link_component.rb +15 -0
- data/app/components/moduler/filter_component.html.erb +12 -0
- data/app/components/moduler/filter_component.rb +15 -0
- data/app/components/moduler/form/base_error_component.html.erb +1 -0
- data/app/components/moduler/form/base_error_component.rb +9 -0
- data/app/components/moduler/form/email_component.html.erb +14 -0
- data/app/components/moduler/form/email_component.rb +11 -0
- data/app/components/moduler/form/password_component.html.erb +14 -0
- data/app/components/moduler/form/password_component.rb +11 -0
- data/app/components/moduler/form/section_component.html.erb +5 -0
- data/app/components/moduler/form/section_component.rb +14 -0
- data/app/components/moduler/form/select_component.html.erb +14 -0
- data/app/components/moduler/form/select_component.rb +12 -0
- data/app/components/moduler/form/submit_component.html.erb +3 -0
- data/app/components/moduler/form/submit_component.rb +10 -0
- data/app/components/moduler/form/text_component.html.erb +14 -0
- data/app/components/moduler/form/text_component.rb +11 -0
- data/app/components/moduler/list_component.html.erb +15 -0
- data/app/components/moduler/list_component.rb +18 -0
- data/app/components/moduler/list_item_component.html.erb +23 -0
- data/app/components/moduler/list_item_component.rb +14 -0
- data/app/components/moduler/page_header_component.html.erb +16 -0
- data/app/components/moduler/page_header_component.rb +12 -0
- data/app/components/moduler/sidebar_action_component.html.erb +5 -0
- data/app/components/moduler/sidebar_action_component.rb +15 -0
- data/app/controllers/concerns/moduler/authentication.rb +62 -0
- data/app/controllers/moduler/administration/application_controller.rb +7 -0
- data/app/controllers/moduler/administration/home_controller.rb +9 -0
- data/app/controllers/moduler/administration/sessions_controller.rb +34 -0
- data/app/controllers/moduler/administration/users_controller.rb +56 -0
- data/app/controllers/moduler/application_controller.rb +18 -0
- data/app/controllers/moduler/home_controller.rb +5 -0
- data/app/controllers/moduler/profile/sessions/revocation_controller.rb +14 -0
- data/app/controllers/moduler/profile/sessions_controller.rb +21 -0
- data/app/controllers/moduler/profile_controller.rb +27 -0
- data/app/controllers/moduler/sessions_controller.rb +39 -0
- data/app/controllers/moduler/users_controller.rb +36 -0
- data/app/filters/application_filter.rb +41 -0
- data/app/filters/query_filter.rb +18 -0
- data/app/helpers/moduler/application_helper.rb +4 -0
- data/app/lib/moduler/form_builder.rb +23 -0
- data/app/models/moduler/current.rb +6 -0
- data/app/models/moduler/session.rb +17 -0
- data/app/models/moduler/user.rb +18 -0
- data/app/policies/moduler/administration/base_policy.rb +11 -0
- data/app/policies/moduler/administration/home_policy.rb +9 -0
- data/app/policies/moduler/administration/session_policy.rb +13 -0
- data/app/policies/moduler/administration/user_policy.rb +25 -0
- data/app/policies/moduler/application_policy.rb +38 -0
- data/app/views/kaminari/_gap.html.erb +1 -0
- data/app/views/kaminari/_next_page.html.erb +1 -0
- data/app/views/kaminari/_page.html.erb +3 -0
- data/app/views/kaminari/_paginator.html.erb +13 -0
- data/app/views/kaminari/_prev_page.html.erb +1 -0
- data/app/views/layouts/moduler/application.html.erb +16 -10
- data/app/views/moduler/administration/home/index.html.erb +6 -0
- data/app/views/moduler/administration/sessions/_session.html.erb +30 -0
- data/app/views/moduler/administration/sessions/index.html.erb +9 -0
- data/app/views/moduler/administration/users/_user.html.erb +13 -0
- data/app/views/moduler/administration/users/edit.html.erb +14 -0
- data/app/views/moduler/administration/users/index.html.erb +13 -0
- data/app/views/moduler/administration/users/new.html.erb +18 -0
- data/app/views/moduler/common/_flash.html.erb +16 -0
- data/app/views/moduler/common/_header.html.erb +18 -0
- data/app/views/moduler/common/_sidebar.html.erb +6 -0
- data/app/views/moduler/common/administration/_sidebar.html.erb +6 -0
- data/app/views/moduler/home/index.html.erb +0 -0
- data/app/views/moduler/profile/edit.html.erb +28 -0
- data/app/views/moduler/profile/sessions/_session.html.erb +52 -0
- data/app/views/moduler/profile/sessions/index.html.erb +26 -0
- data/app/views/moduler/sessions/new.html.erb +31 -0
- data/app/views/moduler/users/new.html.erb +26 -0
- data/config/importmap.rb +5 -0
- data/config/locales/moduler/administration/sessions/en.yml +24 -0
- data/config/locales/moduler/administration/users/en.yml +43 -0
- data/config/locales/moduler/common/en.yml +13 -0
- data/config/locales/moduler/components/en.yml +11 -0
- data/config/locales/moduler/en.yml +4 -0
- data/config/locales/moduler/profile/en.yml +26 -0
- data/config/locales/moduler/profile/sessions/en.yml +25 -0
- data/config/locales/moduler/sessions/en.yml +18 -0
- data/config/locales/moduler/users/en.yml +21 -0
- data/config/routes.rb +19 -0
- data/db/migrate/20260322152259_create_moduler_users.rb +12 -0
- data/db/migrate/20260322153744_create_moduler_sessions.rb +13 -0
- data/db/migrate/20261015171504_add_role_to_moduler_users.rb +5 -0
- data/db/migrate/20261020191709_enable_pg_trgm.rb +5 -0
- data/lib/moduler/configuration.rb +12 -0
- data/lib/moduler/engine.rb +20 -0
- data/lib/moduler/router.rb +10 -0
- data/lib/moduler/routing.rb +11 -0
- data/lib/moduler/version.rb +1 -1
- data/lib/moduler.rb +12 -1
- data/lib/tasks/moduler_tasks.rake +42 -4
- metadata +250 -2
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<main class="mx-auto flex w-full flex-1 max-w-[120rem] flex-col gap-4 px-4 md:flex-row md:gap-8 md:pl-0 md:pr-6">
|
|
2
|
+
<%= render "moduler/common/sidebar" %>
|
|
3
|
+
|
|
4
|
+
<div class="flex min-w-0 flex-1 flex-col pb-4 md:mt-8">
|
|
5
|
+
<div class="mb-4 flex min-h-9 items-center justify-between gap-4">
|
|
6
|
+
<h1 class="text-base font-semibold text-gray-900"><%= t(".title") %></h1>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<%= form_with model: @user, url: moduler.profile_path, method: :patch, data: { turbo: false }, class: "w-full max-w-lg space-y-6" do |form| %>
|
|
10
|
+
<%= render(::Moduler::Form::BaseErrorComponent.new(form)) %>
|
|
11
|
+
|
|
12
|
+
<%= render(::Moduler::Form::SectionComponent.new(t(".section.data"))) do %>
|
|
13
|
+
<%= render(::Moduler::Form::EmailComponent.new(form, :email_address, label: t(".label.email_address"), disabled: :disabled)) %>
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
16
|
+
<%= render(::Moduler::Form::SectionComponent.new(t(".section.password"), divider: true)) do %>
|
|
17
|
+
<%= render(::Moduler::Form::PasswordComponent.new(form, :password, label: t(".label.password"))) %>
|
|
18
|
+
<%= render(::Moduler::Form::PasswordComponent.new(form, :password_confirmation, label: t(".label.password_confirmation"), description: t(".description.password_confirmation"))) %>
|
|
19
|
+
<% end %>
|
|
20
|
+
|
|
21
|
+
<%= render(::Moduler::Form::SectionComponent.new(t(".section.current_password"), divider: true)) do %>
|
|
22
|
+
<%= render(::Moduler::Form::PasswordComponent.new(form, :password_challenge, label: t(".label.password_challenge"), description: t(".description.password_challenge"))) %>
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
<%= render(::Moduler::Form::SubmitComponent.new(form, text: t(".action.save"))) %>
|
|
26
|
+
<% end %>
|
|
27
|
+
</div>
|
|
28
|
+
</main>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<% current = session == Moduler::Current.session %>
|
|
2
|
+
<% mobile = session.user_agent.to_s.match?(/Mobile|Android|iPhone|iPad/) %>
|
|
3
|
+
|
|
4
|
+
<li id="<%= dom_id(session) %>" class="flex items-center justify-between gap-4 px-4 py-4 sm:px-5 <%= "bg-slate-50/60" unless session.active? %>">
|
|
5
|
+
<div class="flex min-w-0 items-start gap-4">
|
|
6
|
+
<div class="flex size-10 shrink-0 items-center justify-center rounded-full <%= session.active? ? "bg-slate-100 text-slate-600" : "bg-slate-100/60 text-slate-400" %>">
|
|
7
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="size-5" aria-hidden="true">
|
|
8
|
+
<% if mobile %>
|
|
9
|
+
<rect x="7" y="2.5" width="10" height="19" rx="2" />
|
|
10
|
+
<path d="M11 18.5h2" />
|
|
11
|
+
<% else %>
|
|
12
|
+
<rect x="3" y="4" width="18" height="12" rx="1.5" />
|
|
13
|
+
<path d="M8 20h8M12 16v4" />
|
|
14
|
+
<% end %>
|
|
15
|
+
</svg>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="min-w-0">
|
|
19
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
20
|
+
<p class="text-sm font-semibold <%= session.active? ? "text-gray-900" : "text-slate-500" %>">
|
|
21
|
+
<%= session.ip_address.presence || t(".unknown_ip") %>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<% if current %>
|
|
25
|
+
<span class="inline-flex items-center rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700 ring-1 ring-inset ring-emerald-600/20"><%= t(".status.current") %></span>
|
|
26
|
+
<% elsif session.active? %>
|
|
27
|
+
<span class="inline-flex items-center rounded-full bg-slate-50 px-2 py-0.5 text-xs font-medium text-slate-600 ring-1 ring-inset ring-slate-500/20"><%= t(".status.active") %></span>
|
|
28
|
+
<% else %>
|
|
29
|
+
<span class="inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium text-slate-400 ring-1 ring-inset ring-slate-200"><%= t(".status.signed_out") %></span>
|
|
30
|
+
<% end %>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<p class="mt-1 truncate text-xs text-slate-500" title="<%= session.user_agent %>">
|
|
34
|
+
<%= session.user_agent.presence || t(".unknown_device") %>
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
<p class="mt-1 text-xs text-slate-400">
|
|
38
|
+
<%= time_tag session.signed_in_at, t(".signed_in", time: time_ago_in_words(session.signed_in_at)), title: l(session.signed_in_at, format: :long) %>
|
|
39
|
+
<% if session.signed_out_at %>
|
|
40
|
+
<span aria-hidden="true">·</span>
|
|
41
|
+
<%= time_tag session.signed_out_at, t(".signed_out", time: time_ago_in_words(session.signed_out_at)), title: l(session.signed_out_at, format: :long) %>
|
|
42
|
+
<% end %>
|
|
43
|
+
</p>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<% if session.active? %>
|
|
48
|
+
<%= button_to t(".action.destroy"), moduler.profile_session_path(session), method: :delete,
|
|
49
|
+
class: "cursor-pointer rounded-md bg-white px-2.5 py-1 text-sm font-medium text-slate-600 shadow-sm ring-1 ring-inset ring-slate-300 hover:bg-slate-50 hover:text-red-600",
|
|
50
|
+
form: { class: "shrink-0", data: { turbo_confirm: t(current ? ".confirmation.destroy_current" : ".confirmation.destroy") } } %>
|
|
51
|
+
<% end %>
|
|
52
|
+
</li>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<main class="mx-auto flex w-full flex-1 max-w-[120rem] flex-col gap-4 px-4 md:flex-row md:gap-8 md:pl-0 md:pr-6">
|
|
2
|
+
<%= render "moduler/common/sidebar" %>
|
|
3
|
+
|
|
4
|
+
<div class="flex min-w-0 flex-1 flex-col pb-4 md:mt-8">
|
|
5
|
+
<div class="mb-6 flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
|
6
|
+
<div>
|
|
7
|
+
<h1 class="text-base font-semibold text-gray-900"><%= t(".title") %></h1>
|
|
8
|
+
<p class="mt-1 text-sm text-slate-500"><%= t(".description") %></p>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<%= button_to t(".action.revoke_all"), moduler.profile_sessions_revocation_path, method: :delete,
|
|
12
|
+
class: "cursor-pointer whitespace-nowrap rounded-md bg-white px-3 py-1.5 text-sm/6 font-semibold text-red-600 shadow-sm ring-1 ring-inset ring-slate-300 hover:bg-red-50 hover:ring-red-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600",
|
|
13
|
+
form: { class: "shrink-0", data: { turbo_confirm: t(".confirmation.revoke") } } %>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="flex-1">
|
|
17
|
+
<ul id="sessions" role="list" class="divide-y divide-slate-100 rounded-lg border border-slate-200 bg-white">
|
|
18
|
+
<%= render partial: "moduler/profile/sessions/session", collection: @sessions %>
|
|
19
|
+
</ul>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="mt-4 flex justify-end">
|
|
23
|
+
<%= paginate @sessions %>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</main>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<div class="flex min-h-full">
|
|
2
|
+
<div class="flex flex-1 flex-col justify-center px-4 py-12 sm:px-6 lg:flex-none lg:px-20 xl:px-24">
|
|
3
|
+
<div class="mx-auto w-full max-w-sm lg:w-96">
|
|
4
|
+
<div>
|
|
5
|
+
<h2 class="mt-8 text-2xl/9 font-bold tracking-tight text-gray-900"><%= t(".title") %></h2>
|
|
6
|
+
<p class="mt-2 text-sm/6 text-gray-500">
|
|
7
|
+
<%= t(".subtitle") %>
|
|
8
|
+
<%= link_to t(".action.sign_up"), moduler.new_user_path, class: "font-semibold text-custom hover:text-custom" %>
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="mt-10">
|
|
13
|
+
<% flash.each do |type, message| %>
|
|
14
|
+
<div class="my-2 text-sm text-green-600">
|
|
15
|
+
<%= message %>
|
|
16
|
+
</div>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<div>
|
|
20
|
+
<%= form_for @user, url: moduler.session_path, class: "space-y-6", data: { turbo: false } do |form| %>
|
|
21
|
+
<%= render(::Moduler::Form::BaseErrorComponent.new(form)) %>
|
|
22
|
+
|
|
23
|
+
<%= render(::Moduler::Form::EmailComponent.new(form, :email_address, label: t(".label.email_address"), placeholder: t(".placeholder.email_address"))) %>
|
|
24
|
+
<%= render(::Moduler::Form::PasswordComponent.new(form, :password, label: t(".label.password"), placeholder: t(".placeholder.password"))) %>
|
|
25
|
+
<%= render(::Moduler::Form::SubmitComponent.new(form, text: t(".action.sign_in"))) %>
|
|
26
|
+
<% end %>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<div class="flex min-h-full">
|
|
2
|
+
<div class="flex flex-1 flex-col justify-center px-4 py-12 sm:px-6 lg:flex-none lg:px-20 xl:px-24">
|
|
3
|
+
<div class="mx-auto w-full max-w-sm lg:w-96">
|
|
4
|
+
<div>
|
|
5
|
+
<h2 class="mt-8 text-2xl/9 font-bold tracking-tight text-gray-900"><%= t(".title") %></h2>
|
|
6
|
+
<p class="mt-2 text-sm/6 text-gray-500">
|
|
7
|
+
<%= t(".subtitle") %>
|
|
8
|
+
<%= link_to t(".action.sign_in"), moduler.new_session_path, class: "font-semibold text-custom hover:text-custom" %>
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="mt-10">
|
|
13
|
+
<div>
|
|
14
|
+
<%= form_for @user, url: moduler.user_path, class: "space-y-6", data: { turbo: false } do |form| %>
|
|
15
|
+
<%= render(::Moduler::Form::BaseErrorComponent.new(form)) %>
|
|
16
|
+
|
|
17
|
+
<%= render(::Moduler::Form::EmailComponent.new(form, :email_address, label: t(".label.email_address"), placeholder: t(".placeholder.email_address"))) %>
|
|
18
|
+
<%= render(::Moduler::Form::PasswordComponent.new(form, :password, label: t(".label.password"), placeholder: t(".placeholder.password"), description: t(".description.password"))) %>
|
|
19
|
+
<%= render(::Moduler::Form::PasswordComponent.new(form, :password_confirmation, placeholder: t(".placeholder.password_confirmation"))) %>
|
|
20
|
+
<%= render(::Moduler::Form::SubmitComponent.new(form, text: t(".action.sign_up"))) %>
|
|
21
|
+
<% end %>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
pin "moduler/application"
|
|
2
|
+
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
|
3
|
+
pin "@hotwired/stimulus", to: "stimulus.min.js"
|
|
4
|
+
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
|
5
|
+
pin_all_from File.expand_path("../app/assets/javascripts/moduler/controllers", __dir__), under: "moduler/controllers"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
en:
|
|
2
|
+
moduler:
|
|
3
|
+
administration:
|
|
4
|
+
sessions:
|
|
5
|
+
index:
|
|
6
|
+
title: Sessions
|
|
7
|
+
description: Sessions of all users. Search sessions by email address, IP address or device and revoke any of them.
|
|
8
|
+
empty: No sessions found.
|
|
9
|
+
destroy:
|
|
10
|
+
notice: Session has been revoked
|
|
11
|
+
session:
|
|
12
|
+
action:
|
|
13
|
+
destroy: Revoke
|
|
14
|
+
status:
|
|
15
|
+
active: Active
|
|
16
|
+
signed_out: Signed out
|
|
17
|
+
current: This device
|
|
18
|
+
signed_in: Signed in %{time} ago
|
|
19
|
+
signed_out: Signed out %{time} ago
|
|
20
|
+
unknown_ip: Unknown IP address
|
|
21
|
+
unknown_device: Unknown device
|
|
22
|
+
confirmation:
|
|
23
|
+
destroy: The user will be signed out of this session. Are you sure?
|
|
24
|
+
destroy_current: You will be signed out of this device. Are you sure?
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
en:
|
|
2
|
+
moduler:
|
|
3
|
+
administration:
|
|
4
|
+
users:
|
|
5
|
+
roles:
|
|
6
|
+
member: Member
|
|
7
|
+
administrator: Administrator
|
|
8
|
+
index:
|
|
9
|
+
title: Users
|
|
10
|
+
description: Manage user accounts and their roles. Search users by email address.
|
|
11
|
+
empty: No users found.
|
|
12
|
+
action:
|
|
13
|
+
new: Create
|
|
14
|
+
new:
|
|
15
|
+
title: New user
|
|
16
|
+
label:
|
|
17
|
+
email_address: Email address
|
|
18
|
+
role: Role
|
|
19
|
+
password: Password
|
|
20
|
+
description:
|
|
21
|
+
password: Use at least 8 characters.
|
|
22
|
+
section:
|
|
23
|
+
data: User data
|
|
24
|
+
password: Password
|
|
25
|
+
action:
|
|
26
|
+
create: Create
|
|
27
|
+
create:
|
|
28
|
+
notice: User has been created
|
|
29
|
+
edit:
|
|
30
|
+
title: Edit user
|
|
31
|
+
label:
|
|
32
|
+
email_address: Email address
|
|
33
|
+
role: Role
|
|
34
|
+
section:
|
|
35
|
+
data: User data
|
|
36
|
+
action:
|
|
37
|
+
save: Save
|
|
38
|
+
update:
|
|
39
|
+
notice: User has been updated
|
|
40
|
+
user:
|
|
41
|
+
action:
|
|
42
|
+
edit: Edit
|
|
43
|
+
created: Created %{time} ago
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
en:
|
|
2
|
+
moduler:
|
|
3
|
+
profile:
|
|
4
|
+
edit:
|
|
5
|
+
title: Profile
|
|
6
|
+
label:
|
|
7
|
+
email_address: Email address
|
|
8
|
+
password: New password
|
|
9
|
+
password_confirmation: Confirm new password
|
|
10
|
+
password_challenge: Current password
|
|
11
|
+
description:
|
|
12
|
+
password_confirmation: Leave blank to keep your current password. Changing it signs out your other active sessions.
|
|
13
|
+
password_challenge: Required to save changes to password.
|
|
14
|
+
action:
|
|
15
|
+
save: Save
|
|
16
|
+
section:
|
|
17
|
+
data: Your data
|
|
18
|
+
password: Change password
|
|
19
|
+
current_password: Confirm changes
|
|
20
|
+
sidebar:
|
|
21
|
+
profile: Profile
|
|
22
|
+
notifications: Notifications
|
|
23
|
+
security: Security
|
|
24
|
+
billing: Billing
|
|
25
|
+
update:
|
|
26
|
+
notice: Your profile has been updated
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
en:
|
|
2
|
+
moduler:
|
|
3
|
+
profile:
|
|
4
|
+
sessions:
|
|
5
|
+
index:
|
|
6
|
+
title: Sessions
|
|
7
|
+
description: Devices where you are signed in to your account. Revoke any session you don't recognize.
|
|
8
|
+
action:
|
|
9
|
+
revoke_all: Revoke all
|
|
10
|
+
confirmation:
|
|
11
|
+
revoke: You will be signed out of all sessions, including this one. Are you sure?
|
|
12
|
+
session:
|
|
13
|
+
action:
|
|
14
|
+
destroy: Revoke
|
|
15
|
+
status:
|
|
16
|
+
active: Active
|
|
17
|
+
signed_out: Signed out
|
|
18
|
+
current: This device
|
|
19
|
+
signed_in: Signed in %{time} ago
|
|
20
|
+
signed_out: Signed out %{time} ago
|
|
21
|
+
unknown_ip: Unknown IP address
|
|
22
|
+
unknown_device: Unknown device
|
|
23
|
+
confirmation:
|
|
24
|
+
destroy: Are you sure?
|
|
25
|
+
destroy_current: You will be signed out of this device. Are you sure?
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
en:
|
|
2
|
+
moduler:
|
|
3
|
+
sessions:
|
|
4
|
+
new:
|
|
5
|
+
title: Sign in to your account
|
|
6
|
+
subtitle: Enter your email and password to sign in.
|
|
7
|
+
action:
|
|
8
|
+
sign_in: Sign in
|
|
9
|
+
sign_up: Sign up
|
|
10
|
+
label:
|
|
11
|
+
email_address: Email address
|
|
12
|
+
password: Password
|
|
13
|
+
placeholder:
|
|
14
|
+
email_address: Enter your email address
|
|
15
|
+
password: Enter your password
|
|
16
|
+
create:
|
|
17
|
+
invalid_credentials: Invalid credentials
|
|
18
|
+
rate_limited: Too many sign-in attempts. Please try again in a few minutes.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
en:
|
|
2
|
+
moduler:
|
|
3
|
+
users:
|
|
4
|
+
new:
|
|
5
|
+
title: "Create an account"
|
|
6
|
+
subtitle: "Already have an account?"
|
|
7
|
+
action:
|
|
8
|
+
sign_in: "Sign in"
|
|
9
|
+
sign_up: "Sign up"
|
|
10
|
+
label:
|
|
11
|
+
email_address: "Email address"
|
|
12
|
+
password: "Password"
|
|
13
|
+
placeholder:
|
|
14
|
+
email_address: "Email address"
|
|
15
|
+
password: "Password"
|
|
16
|
+
password_confirmation: "Confirm password"
|
|
17
|
+
description:
|
|
18
|
+
password: "Use at least 8 characters containing at least 1 number and 1 lowercase letter"
|
|
19
|
+
create:
|
|
20
|
+
success: "User created successfully."
|
|
21
|
+
rate_limited: "Too many sign-up attempts. Please try again in a few minutes."
|
data/config/routes.rb
CHANGED
|
@@ -1,2 +1,21 @@
|
|
|
1
1
|
Moduler::Engine.routes.draw do
|
|
2
|
+
resource :session, only: %i[new create destroy]
|
|
3
|
+
resource :user, only: %i[new create]
|
|
4
|
+
|
|
5
|
+
resource :profile, only: %i[ edit update ], controller: :profile
|
|
6
|
+
namespace :profile do
|
|
7
|
+
namespace :sessions do
|
|
8
|
+
resource :revocation, only: :destroy, controller: :revocation
|
|
9
|
+
end
|
|
10
|
+
resources :sessions, only: %i[ index destroy ]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
namespace :administration do
|
|
14
|
+
resources :users, only: %i[ index new create edit update ]
|
|
15
|
+
resources :sessions, only: %i[ index destroy ]
|
|
16
|
+
|
|
17
|
+
root "home#index"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
root "home#index"
|
|
2
21
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CreateModulerUsers < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :moduler_users, id: :uuid do |t|
|
|
4
|
+
t.string :email_address, null: false
|
|
5
|
+
t.string :password_digest
|
|
6
|
+
|
|
7
|
+
t.timestamps null: false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :moduler_users, :email_address, unique: true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateModulerSessions < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :moduler_sessions, id: :uuid do |t|
|
|
4
|
+
t.references :moduler_user, null: false, foreign_key: true, type: :uuid
|
|
5
|
+
t.string :ip_address
|
|
6
|
+
t.string :user_agent
|
|
7
|
+
t.datetime :signed_in_at
|
|
8
|
+
t.datetime :signed_out_at
|
|
9
|
+
|
|
10
|
+
t.timestamps null: false
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/moduler/engine.rb
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
+
require "importmap-rails"
|
|
2
|
+
require "stimulus-rails"
|
|
3
|
+
require "turbo-rails"
|
|
4
|
+
require "tailwindcss-rails"
|
|
5
|
+
require "kaminari"
|
|
6
|
+
|
|
1
7
|
module Moduler
|
|
2
8
|
class Engine < ::Rails::Engine
|
|
3
9
|
isolate_namespace Moduler
|
|
10
|
+
|
|
11
|
+
initializer "moduler.importmap", before: "importmap" do |app|
|
|
12
|
+
app.config.importmap.paths += [ Engine.root.join("config/importmap.rb") ]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
initializer "moduler.assets" do |app|
|
|
16
|
+
app.config.assets.precompile += %w[moduler/manifest.js]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
initializer "moduler.routes" do
|
|
20
|
+
Engine.routes.append do
|
|
21
|
+
instance_exec(&Moduler.router.routes) if Moduler.router&.routes
|
|
22
|
+
end
|
|
23
|
+
end
|
|
4
24
|
end
|
|
5
25
|
end
|
data/lib/moduler/version.rb
CHANGED
data/lib/moduler.rb
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
require "view_component"
|
|
2
|
+
require "pg_search"
|
|
3
|
+
require "pundit"
|
|
4
|
+
|
|
1
5
|
require "moduler/version"
|
|
2
6
|
require "moduler/engine"
|
|
7
|
+
require "moduler/configuration"
|
|
8
|
+
require "moduler/router"
|
|
9
|
+
require "moduler/routing"
|
|
3
10
|
|
|
4
11
|
module Moduler
|
|
5
|
-
|
|
12
|
+
def self.configuration
|
|
13
|
+
@configuration ||= Configuration.new
|
|
14
|
+
end
|
|
6
15
|
end
|
|
16
|
+
|
|
17
|
+
ActionDispatch::Routing::Mapper.include Moduler::Routing
|
|
@@ -1,4 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
require "tailwindcss-rails"
|
|
2
|
+
|
|
3
|
+
namespace :moduler do
|
|
4
|
+
namespace :tailwindcss do
|
|
5
|
+
desc "Build Tailwind CSS for Moduler Engine"
|
|
6
|
+
task :build do
|
|
7
|
+
input_path = "app/assets/stylesheets/moduler/tailwind.css"
|
|
8
|
+
output_path = "app/assets/builds/moduler/tailwind.css"
|
|
9
|
+
|
|
10
|
+
FileUtils.mkdir_p(Moduler::Engine.root.join("app/assets/builds/moduler"))
|
|
11
|
+
|
|
12
|
+
command = [
|
|
13
|
+
Tailwindcss::Ruby.executable,
|
|
14
|
+
"--input", input_path,
|
|
15
|
+
"--output", output_path
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
Dir.chdir(Moduler::Engine.root) do
|
|
19
|
+
system(*command, exception: true)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "Watch and build Tailwind CSS for Moduler Engine"
|
|
24
|
+
task :watch do
|
|
25
|
+
input_path = "app/assets/stylesheets/moduler/tailwind.css"
|
|
26
|
+
output_path = "app/assets/builds/moduler/tailwind.css"
|
|
27
|
+
|
|
28
|
+
FileUtils.mkdir_p(Moduler::Engine.root.join("app/assets/builds/moduler"))
|
|
29
|
+
|
|
30
|
+
command = [
|
|
31
|
+
Tailwindcss::Ruby.executable,
|
|
32
|
+
"--input", input_path,
|
|
33
|
+
"--output", output_path,
|
|
34
|
+
"--watch"
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
Dir.chdir(Moduler::Engine.root) do
|
|
38
|
+
system(*command, exception: true)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|