rsb-admin 0.9.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 +7 -0
- data/LICENSE +15 -0
- data/README.md +83 -0
- data/Rakefile +25 -0
- data/app/assets/javascripts/rsb/admin/themes/modern.js +37 -0
- data/app/assets/stylesheets/rsb/admin/themes/default.css +1358 -0
- data/app/assets/stylesheets/rsb/admin/themes/modern.css +1370 -0
- data/app/controllers/concerns/rsb/admin/authorization.rb +21 -0
- data/app/controllers/rsb/admin/admin_controller.rb +138 -0
- data/app/controllers/rsb/admin/admin_users_controller.rb +110 -0
- data/app/controllers/rsb/admin/dashboard_controller.rb +76 -0
- data/app/controllers/rsb/admin/profile_controller.rb +146 -0
- data/app/controllers/rsb/admin/profile_sessions_controller.rb +45 -0
- data/app/controllers/rsb/admin/resources_controller.rb +386 -0
- data/app/controllers/rsb/admin/roles_controller.rb +99 -0
- data/app/controllers/rsb/admin/sessions_controller.rb +139 -0
- data/app/controllers/rsb/admin/settings_controller.rb +203 -0
- data/app/controllers/rsb/admin/two_factor_controller.rb +105 -0
- data/app/helpers/rsb/admin/authorization_helper.rb +49 -0
- data/app/helpers/rsb/admin/branding_helper.rb +38 -0
- data/app/helpers/rsb/admin/formatting_helper.rb +205 -0
- data/app/helpers/rsb/admin/i18n_helper.rb +148 -0
- data/app/helpers/rsb/admin/icons_helper.rb +55 -0
- data/app/helpers/rsb/admin/table_helper.rb +132 -0
- data/app/helpers/rsb/admin/theme_helper.rb +84 -0
- data/app/helpers/rsb/admin/url_helper.rb +109 -0
- data/app/mailers/rsb/admin/admin_mailer.rb +37 -0
- data/app/models/rsb/admin/admin_session.rb +109 -0
- data/app/models/rsb/admin/admin_user.rb +153 -0
- data/app/models/rsb/admin/application_record.rb +10 -0
- data/app/models/rsb/admin/role.rb +63 -0
- data/app/views/layouts/rsb/admin/application.html.erb +45 -0
- data/app/views/rsb/admin/admin_mailer/email_verification.html.erb +11 -0
- data/app/views/rsb/admin/admin_mailer/email_verification.text.erb +11 -0
- data/app/views/rsb/admin/admin_users/_form.html.erb +52 -0
- data/app/views/rsb/admin/admin_users/edit.html.erb +10 -0
- data/app/views/rsb/admin/admin_users/index.html.erb +77 -0
- data/app/views/rsb/admin/admin_users/new.html.erb +10 -0
- data/app/views/rsb/admin/admin_users/show.html.erb +85 -0
- data/app/views/rsb/admin/dashboard/index.html.erb +36 -0
- data/app/views/rsb/admin/profile/edit.html.erb +67 -0
- data/app/views/rsb/admin/profile/show.html.erb +155 -0
- data/app/views/rsb/admin/resources/_filters.html.erb +58 -0
- data/app/views/rsb/admin/resources/_form.html.erb +20 -0
- data/app/views/rsb/admin/resources/_pagination.html.erb +33 -0
- data/app/views/rsb/admin/resources/_table.html.erb +70 -0
- data/app/views/rsb/admin/resources/edit.html.erb +7 -0
- data/app/views/rsb/admin/resources/index.html.erb +49 -0
- data/app/views/rsb/admin/resources/new.html.erb +7 -0
- data/app/views/rsb/admin/resources/page.html.erb +9 -0
- data/app/views/rsb/admin/resources/show.html.erb +55 -0
- data/app/views/rsb/admin/roles/_form.html.erb +197 -0
- data/app/views/rsb/admin/roles/edit.html.erb +7 -0
- data/app/views/rsb/admin/roles/index.html.erb +71 -0
- data/app/views/rsb/admin/roles/new.html.erb +7 -0
- data/app/views/rsb/admin/roles/show.html.erb +99 -0
- data/app/views/rsb/admin/sessions/new.html.erb +31 -0
- data/app/views/rsb/admin/sessions/two_factor.html.erb +39 -0
- data/app/views/rsb/admin/settings/_field.html.erb +115 -0
- data/app/views/rsb/admin/settings/index.html.erb +61 -0
- data/app/views/rsb/admin/shared/_badge.html.erb +1 -0
- data/app/views/rsb/admin/shared/_breadcrumbs.html.erb +12 -0
- data/app/views/rsb/admin/shared/_empty_state.html.erb +4 -0
- data/app/views/rsb/admin/shared/_flash.html.erb +22 -0
- data/app/views/rsb/admin/shared/_header.html.erb +50 -0
- data/app/views/rsb/admin/shared/_page_tabs.html.erb +21 -0
- data/app/views/rsb/admin/shared/_sidebar.html.erb +99 -0
- data/app/views/rsb/admin/shared/disabled.html.erb +38 -0
- data/app/views/rsb/admin/shared/fields/_checkbox.html.erb +6 -0
- data/app/views/rsb/admin/shared/fields/_datetime.html.erb +10 -0
- data/app/views/rsb/admin/shared/fields/_email.html.erb +10 -0
- data/app/views/rsb/admin/shared/fields/_hidden.html.erb +1 -0
- data/app/views/rsb/admin/shared/fields/_json.html.erb +11 -0
- data/app/views/rsb/admin/shared/fields/_number.html.erb +10 -0
- data/app/views/rsb/admin/shared/fields/_password.html.erb +10 -0
- data/app/views/rsb/admin/shared/fields/_select.html.erb +12 -0
- data/app/views/rsb/admin/shared/fields/_text.html.erb +10 -0
- data/app/views/rsb/admin/shared/fields/_textarea.html.erb +10 -0
- data/app/views/rsb/admin/shared/forbidden.html.erb +22 -0
- data/app/views/rsb/admin/themes/modern/views/shared/_header.html.erb +77 -0
- data/app/views/rsb/admin/themes/modern/views/shared/_sidebar.html.erb +135 -0
- data/app/views/rsb/admin/two_factor/backup_codes.html.erb +48 -0
- data/app/views/rsb/admin/two_factor/new.html.erb +53 -0
- data/config/locales/en.yml +140 -0
- data/config/locales/seo.en.yml +21 -0
- data/config/routes.rb +59 -0
- data/db/migrate/20260208000003_create_rsb_admin_tables.rb +43 -0
- data/db/migrate/20260214000001_add_otp_fields_to_rsb_admin_admin_users.rb +9 -0
- data/lib/generators/rsb/admin/install/install_generator.rb +45 -0
- data/lib/generators/rsb/admin/install/templates/rsb_admin_seeds.rb +24 -0
- data/lib/generators/rsb/admin/theme/templates/theme.css.tt +66 -0
- data/lib/generators/rsb/admin/theme/theme_generator.rb +218 -0
- data/lib/generators/rsb/admin/views/views_generator.rb +262 -0
- data/lib/rsb/admin/breadcrumb_item.rb +26 -0
- data/lib/rsb/admin/category_registration.rb +177 -0
- data/lib/rsb/admin/column_definition.rb +89 -0
- data/lib/rsb/admin/configuration.rb +69 -0
- data/lib/rsb/admin/engine.rb +34 -0
- data/lib/rsb/admin/filter_definition.rb +129 -0
- data/lib/rsb/admin/form_field_definition.rb +96 -0
- data/lib/rsb/admin/icons.rb +95 -0
- data/lib/rsb/admin/page_registration.rb +140 -0
- data/lib/rsb/admin/registry.rb +109 -0
- data/lib/rsb/admin/resource_dsl_context.rb +139 -0
- data/lib/rsb/admin/resource_registration.rb +287 -0
- data/lib/rsb/admin/settings_schema.rb +60 -0
- data/lib/rsb/admin/test_kit/helpers.rb +316 -0
- data/lib/rsb/admin/test_kit/resource_test_case.rb +193 -0
- data/lib/rsb/admin/test_kit.rb +11 -0
- data/lib/rsb/admin/theme_definition.rb +46 -0
- data/lib/rsb/admin/themes/modern.rb +44 -0
- data/lib/rsb/admin/version.rb +9 -0
- data/lib/rsb/admin.rb +177 -0
- data/lib/tasks/rsb/admin_tasks.rake +23 -0
- metadata +227 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<div class="max-w-lg mx-auto">
|
|
2
|
+
<div class="bg-rsb-card border border-rsb-border rounded-rsb-lg shadow-rsb-sm p-6">
|
|
3
|
+
<h2 class="text-xl font-bold mb-4">Set Up Two-Factor Authentication</h2>
|
|
4
|
+
|
|
5
|
+
<p class="mb-4 text-rsb-muted text-sm">
|
|
6
|
+
Scan the QR code below with your authenticator app (Google Authenticator, Authy, 1Password, etc.),
|
|
7
|
+
then enter the 6-digit code to confirm.
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<% if flash[:alert] %>
|
|
11
|
+
<div class="mb-4 p-3 rounded-rsb bg-rsb-danger-bg border border-rsb-danger text-rsb-danger-text text-sm">
|
|
12
|
+
<%= flash[:alert] %>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
16
|
+
<!-- QR Code -->
|
|
17
|
+
<div class="flex justify-center mb-4">
|
|
18
|
+
<div class="bg-white p-4 rounded">
|
|
19
|
+
<%= @qr_svg.html_safe %>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<!-- Manual entry key -->
|
|
24
|
+
<div class="mb-6">
|
|
25
|
+
<p class="text-sm font-medium mb-1">Can't scan? Enter this key manually:</p>
|
|
26
|
+
<code class="block p-2 bg-rsb-bg border border-rsb-border rounded-rsb text-sm font-mono break-all select-all">
|
|
27
|
+
<%= @otp_secret %>
|
|
28
|
+
</code>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<!-- Confirmation form -->
|
|
32
|
+
<%= form_tag(rsb_admin.profile_two_factor_path, method: :post) do %>
|
|
33
|
+
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
|
|
34
|
+
|
|
35
|
+
<div class="mb-4">
|
|
36
|
+
<label for="otp_code" class="block text-sm font-medium mb-1">Verification Code</label>
|
|
37
|
+
<input type="text" id="otp_code" name="otp_code"
|
|
38
|
+
class="w-full px-3 py-2 border border-rsb-border rounded-rsb text-sm focus:outline-none focus:border-rsb-primary focus:ring-2 focus:ring-rsb-primary/10"
|
|
39
|
+
placeholder="Enter 6-digit code"
|
|
40
|
+
autocomplete="one-time-code" inputmode="numeric" autofocus required />
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<button type="submit"
|
|
44
|
+
class="w-full px-4 py-2 bg-rsb-primary text-rsb-primary-text rounded-rsb text-sm font-medium hover:bg-rsb-primary-hover">
|
|
45
|
+
Confirm & Enable 2FA
|
|
46
|
+
</button>
|
|
47
|
+
<% end %>
|
|
48
|
+
|
|
49
|
+
<div class="mt-4 text-center">
|
|
50
|
+
<a href="<%= rsb_admin.profile_path %>" class="text-sm text-rsb-muted hover:underline">Cancel</a>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
en:
|
|
2
|
+
rsb:
|
|
3
|
+
admin:
|
|
4
|
+
shared:
|
|
5
|
+
dashboard: "Dashboard"
|
|
6
|
+
sign_out: "Sign out"
|
|
7
|
+
switch_language: "Switch language"
|
|
8
|
+
save: "Save"
|
|
9
|
+
cancel: "Cancel"
|
|
10
|
+
delete: "Delete"
|
|
11
|
+
edit: "Edit"
|
|
12
|
+
view: "View"
|
|
13
|
+
new: "New %{resource}"
|
|
14
|
+
back: "Back"
|
|
15
|
+
confirm_delete: "Are you sure you want to delete this %{resource}? This action cannot be undone."
|
|
16
|
+
no_results: "No %{resource} found"
|
|
17
|
+
no_results_message: "%{resource} will appear here once created."
|
|
18
|
+
showing: "Showing %{from}-%{to} of %{total}"
|
|
19
|
+
previous: "Previous"
|
|
20
|
+
next: "Next"
|
|
21
|
+
page: "Page %{number}"
|
|
22
|
+
filters: "Filters"
|
|
23
|
+
clear_filters: "Clear"
|
|
24
|
+
apply_filters: "Apply"
|
|
25
|
+
all: "All"
|
|
26
|
+
system: "System"
|
|
27
|
+
danger_zone: "Danger Zone"
|
|
28
|
+
required: "required"
|
|
29
|
+
no_access: "No access"
|
|
30
|
+
access_denied: "Access Denied"
|
|
31
|
+
access_denied_message: "You don't have permission to access this page."
|
|
32
|
+
go_to_dashboard: "Go to Dashboard"
|
|
33
|
+
sign_out_and_try: "Sign out"
|
|
34
|
+
panel_disabled: "Admin Panel Disabled"
|
|
35
|
+
panel_disabled_message: "The admin panel is temporarily disabled. Contact your system administrator or set RSB_ADMIN_ENABLED=true to re-enable."
|
|
36
|
+
sessions:
|
|
37
|
+
sign_in: "Sign in"
|
|
38
|
+
email: "Email"
|
|
39
|
+
password: "Password"
|
|
40
|
+
sign_in_button: "Sign in"
|
|
41
|
+
signed_out: "Signed out successfully."
|
|
42
|
+
invalid_credentials: "Invalid email or password."
|
|
43
|
+
settings:
|
|
44
|
+
title: "Settings"
|
|
45
|
+
locked: "Locked"
|
|
46
|
+
save: "Save Settings"
|
|
47
|
+
saved: "Setting updated."
|
|
48
|
+
admin_users:
|
|
49
|
+
title: "Admin Users"
|
|
50
|
+
you: "You"
|
|
51
|
+
no_role: "No Role"
|
|
52
|
+
leave_blank: "Leave blank to keep current password"
|
|
53
|
+
min_password: "Minimum 8 characters"
|
|
54
|
+
confirm_password: "Confirm Password"
|
|
55
|
+
role: "Role"
|
|
56
|
+
no_role_hint: "Admins without a role have no access to any admin features."
|
|
57
|
+
last_sign_in: "Last Sign In"
|
|
58
|
+
last_ip: "Last IP"
|
|
59
|
+
never: "Never"
|
|
60
|
+
never_signed_in: "Never signed in"
|
|
61
|
+
confirm_delete: "Are you sure you want to delete this admin user? This action cannot be undone."
|
|
62
|
+
actions: "Actions"
|
|
63
|
+
roles:
|
|
64
|
+
title: "Roles"
|
|
65
|
+
superadmin: "Superadmin"
|
|
66
|
+
custom: "Custom"
|
|
67
|
+
built_in: "Built-in"
|
|
68
|
+
full_access: "Full access to all resources"
|
|
69
|
+
no_permissions: "No permissions assigned"
|
|
70
|
+
permissions: "Permissions"
|
|
71
|
+
superadmin_toggle: "Grant full access to all resources"
|
|
72
|
+
admin_users_count: "Admin Users"
|
|
73
|
+
actions: "Actions"
|
|
74
|
+
confirm_delete: "Are you sure you want to delete this role? Admin users assigned to it will lose their permissions."
|
|
75
|
+
reassign_warning: "This role is assigned to %{count} admin user(s). You must reassign them before deleting."
|
|
76
|
+
pages: "Pages"
|
|
77
|
+
dashboard: "Dashboard"
|
|
78
|
+
dashboard:
|
|
79
|
+
title: "Dashboard"
|
|
80
|
+
guide_title: "Customize Your Dashboard"
|
|
81
|
+
guide_description: "This is the default dashboard. Register a custom dashboard page to replace it with your own content."
|
|
82
|
+
guide_step1: "1. Register your dashboard controller in an initializer:"
|
|
83
|
+
guide_step2: "2. Create your controller with the actions you need:"
|
|
84
|
+
resources:
|
|
85
|
+
created: "%{resource} created."
|
|
86
|
+
updated: "%{resource} updated."
|
|
87
|
+
deleted: "%{resource} deleted."
|
|
88
|
+
page_placeholder: "This page is provided by an extension. Implement the controller at %{controller} to customize it."
|
|
89
|
+
columns:
|
|
90
|
+
id: "ID"
|
|
91
|
+
email: "Email"
|
|
92
|
+
status: "Status"
|
|
93
|
+
created_at: "Created"
|
|
94
|
+
updated_at: "Updated"
|
|
95
|
+
name: "Name"
|
|
96
|
+
errors:
|
|
97
|
+
prohibited: "%{count} error(s) prohibited this record from being saved:"
|
|
98
|
+
theme:
|
|
99
|
+
toggle_dark: "Dark mode"
|
|
100
|
+
toggle_light: "Light mode"
|
|
101
|
+
footer:
|
|
102
|
+
powered_by: "Powered by RSB"
|
|
103
|
+
profile:
|
|
104
|
+
title: "Profile"
|
|
105
|
+
edit_title: "Edit Profile"
|
|
106
|
+
current_email: "Email"
|
|
107
|
+
current_role: "Role"
|
|
108
|
+
last_sign_in: "Last Sign In"
|
|
109
|
+
last_ip: "Last IP"
|
|
110
|
+
edit_button: "Edit Profile"
|
|
111
|
+
current_password: "Current Password"
|
|
112
|
+
current_password_hint: "Required to confirm changes"
|
|
113
|
+
new_password: "New Password"
|
|
114
|
+
new_password_hint: "Leave blank to keep current password"
|
|
115
|
+
confirm_password: "Confirm New Password"
|
|
116
|
+
password_incorrect: "Current password is incorrect"
|
|
117
|
+
updated: "Profile updated successfully."
|
|
118
|
+
verification_sent: "A verification email has been sent to your new email address."
|
|
119
|
+
verification_resent: "Verification email resent."
|
|
120
|
+
verification_invalid: "Verification link is invalid or has expired."
|
|
121
|
+
verification_expired: "Verification link has expired. Please request a new one from your profile."
|
|
122
|
+
email_verified: "Email verified and updated successfully."
|
|
123
|
+
pending_email: "Pending Email"
|
|
124
|
+
verification_sent_ago: "Verification sent %{time} ago"
|
|
125
|
+
resend_verification: "Resend"
|
|
126
|
+
pending_email_hint: "A verification email was sent to %{email}. Changing the email above will send a new verification."
|
|
127
|
+
sessions_title: "Active Sessions"
|
|
128
|
+
current_session: "Current"
|
|
129
|
+
"on": "on"
|
|
130
|
+
active_now: "Active now"
|
|
131
|
+
last_active: "Active %{time} ago"
|
|
132
|
+
revoke_session: "Revoke"
|
|
133
|
+
revoke_all_sessions: "Revoke all other sessions"
|
|
134
|
+
confirm_revoke_all: "Are you sure you want to revoke all other sessions? You will remain signed in on this device only."
|
|
135
|
+
session_revoked: "Session revoked."
|
|
136
|
+
all_sessions_revoked:
|
|
137
|
+
one: "1 session revoked."
|
|
138
|
+
other: "%{count} sessions revoked."
|
|
139
|
+
session_not_found: "Session not found."
|
|
140
|
+
cannot_revoke_current: "Cannot revoke your current session."
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
en:
|
|
2
|
+
rsb:
|
|
3
|
+
admin:
|
|
4
|
+
sessions:
|
|
5
|
+
new:
|
|
6
|
+
page_title: "Admin Sign In"
|
|
7
|
+
dashboard:
|
|
8
|
+
page_title: "Dashboard"
|
|
9
|
+
settings:
|
|
10
|
+
page_title: "Settings"
|
|
11
|
+
profile:
|
|
12
|
+
show:
|
|
13
|
+
page_title: "Profile"
|
|
14
|
+
edit:
|
|
15
|
+
page_title: "Edit Profile"
|
|
16
|
+
roles:
|
|
17
|
+
index:
|
|
18
|
+
page_title: "Roles"
|
|
19
|
+
admin_users:
|
|
20
|
+
index:
|
|
21
|
+
page_title: "Admin Users"
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSB::Admin::Engine.routes.draw do
|
|
4
|
+
get 'login', to: 'sessions#new', as: :login
|
|
5
|
+
post 'login', to: 'sessions#create'
|
|
6
|
+
get 'login/two_factor', to: 'sessions#two_factor', as: :two_factor_login
|
|
7
|
+
post 'login/two_factor', to: 'sessions#verify_two_factor', as: :verify_two_factor_login
|
|
8
|
+
delete 'logout', to: 'sessions#destroy', as: :logout
|
|
9
|
+
|
|
10
|
+
root to: 'dashboard#index', as: :dashboard
|
|
11
|
+
|
|
12
|
+
# Dashboard sub-actions (tab navigation for custom dashboard pages)
|
|
13
|
+
get 'dashboard/:action_key', to: 'dashboard#dashboard_action', as: :dashboard_action,
|
|
14
|
+
constraints: { action_key: /[a-z_]+/ }
|
|
15
|
+
post 'dashboard/:action_key', to: 'dashboard#dashboard_action', constraints: { action_key: /[a-z_]+/ }
|
|
16
|
+
patch 'dashboard/:action_key', to: 'dashboard#dashboard_action', constraints: { action_key: /[a-z_]+/ }
|
|
17
|
+
delete 'dashboard/:action_key', to: 'dashboard#dashboard_action', constraints: { action_key: /[a-z_]+/ }
|
|
18
|
+
|
|
19
|
+
get 'settings', to: 'settings#index', as: :settings
|
|
20
|
+
patch 'settings', to: 'settings#batch_update'
|
|
21
|
+
patch 'settings/:category/:key', to: 'settings#update', as: :setting
|
|
22
|
+
|
|
23
|
+
get 'profile', to: 'profile#show', as: :profile
|
|
24
|
+
get 'profile/edit', to: 'profile#edit', as: :edit_profile
|
|
25
|
+
patch 'profile', to: 'profile#update'
|
|
26
|
+
get 'profile/verify_email', to: 'profile#verify_email', as: :verify_email_profile
|
|
27
|
+
post 'profile/resend_verification', to: 'profile#resend_verification', as: :resend_verification_profile
|
|
28
|
+
get 'profile/two_factor/new', to: 'two_factor#new', as: :new_profile_two_factor
|
|
29
|
+
post 'profile/two_factor', to: 'two_factor#create', as: :profile_two_factor
|
|
30
|
+
get 'profile/two_factor/backup_codes', to: 'two_factor#backup_codes', as: :profile_two_factor_backup_codes
|
|
31
|
+
delete 'profile/two_factor', to: 'two_factor#destroy'
|
|
32
|
+
delete 'profile/sessions', to: 'profile_sessions#destroy_all', as: :profile_sessions
|
|
33
|
+
delete 'profile/sessions/:id', to: 'profile_sessions#destroy', as: :profile_session
|
|
34
|
+
|
|
35
|
+
resources :roles
|
|
36
|
+
resources :admin_users
|
|
37
|
+
|
|
38
|
+
# Dynamic resource routes for registered resources.
|
|
39
|
+
# These catch-all routes must be LAST so they don't override static routes above.
|
|
40
|
+
# Order matters: specific patterns before generic ':id' patterns.
|
|
41
|
+
get ':resource_key/new', to: 'resources#new'
|
|
42
|
+
post ':resource_key', to: 'resources#create'
|
|
43
|
+
|
|
44
|
+
# Static page sub-actions (must be before catch-all :id routes)
|
|
45
|
+
get ':resource_key/:action_key', to: 'resources#page_action', constraints: { action_key: /[a-z_]+/ }
|
|
46
|
+
post ':resource_key/:action_key', to: 'resources#page_action', constraints: { action_key: /[a-z_]+/ }
|
|
47
|
+
delete ':resource_key/:action_key', to: 'resources#page_action', constraints: { action_key: /[a-z_]+/ }
|
|
48
|
+
patch ':resource_key/:action_key', to: 'resources#page_action', constraints: { action_key: /[a-z_]+/ }
|
|
49
|
+
|
|
50
|
+
get ':resource_key/:id/edit', to: 'resources#edit'
|
|
51
|
+
patch ':resource_key/:id', to: 'resources#update'
|
|
52
|
+
put ':resource_key/:id', to: 'resources#update'
|
|
53
|
+
delete ':resource_key/:id', to: 'resources#destroy'
|
|
54
|
+
get ':resource_key/:id/:custom_action', to: 'resources#custom_action'
|
|
55
|
+
post ':resource_key/:id/:custom_action', to: 'resources#custom_action'
|
|
56
|
+
patch ':resource_key/:id/:custom_action', to: 'resources#custom_action'
|
|
57
|
+
get ':resource_key/:id', to: 'resources#show'
|
|
58
|
+
get ':resource_key', to: 'resources#index'
|
|
59
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateRSBAdminTables < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
create_table :rsb_admin_roles do |t|
|
|
6
|
+
t.string :name, null: false
|
|
7
|
+
t.json :permissions, null: false, default: {}
|
|
8
|
+
t.boolean :built_in, default: false
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
add_index :rsb_admin_roles, :name, unique: true
|
|
13
|
+
|
|
14
|
+
create_table :rsb_admin_admin_users do |t|
|
|
15
|
+
t.string :email, null: false
|
|
16
|
+
t.string :password_digest, null: false
|
|
17
|
+
t.references :role, foreign_key: { to_table: :rsb_admin_roles }
|
|
18
|
+
t.datetime :last_sign_in_at
|
|
19
|
+
t.string :last_sign_in_ip
|
|
20
|
+
t.string :pending_email
|
|
21
|
+
t.string :email_verification_token
|
|
22
|
+
t.datetime :email_verification_sent_at
|
|
23
|
+
t.timestamps
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
add_index :rsb_admin_admin_users, :email, unique: true
|
|
27
|
+
add_index :rsb_admin_admin_users, :email_verification_token, unique: true
|
|
28
|
+
|
|
29
|
+
create_table :rsb_admin_admin_sessions do |t|
|
|
30
|
+
t.references :admin_user, null: false, foreign_key: { to_table: :rsb_admin_admin_users }
|
|
31
|
+
t.string :session_token, null: false
|
|
32
|
+
t.string :ip_address
|
|
33
|
+
t.text :user_agent
|
|
34
|
+
t.string :browser
|
|
35
|
+
t.string :os
|
|
36
|
+
t.string :device_type
|
|
37
|
+
t.datetime :last_active_at, null: false
|
|
38
|
+
t.timestamps
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
add_index :rsb_admin_admin_sessions, :session_token, unique: true
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddOtpFieldsToRSBAdminAdminUsers < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
add_column :rsb_admin_admin_users, :otp_secret, :string
|
|
6
|
+
add_column :rsb_admin_admin_users, :otp_required, :boolean, null: false, default: false
|
|
7
|
+
add_column :rsb_admin_admin_users, :otp_backup_codes, :text
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RSB
|
|
4
|
+
module Admin
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
namespace 'rsb:admin:install'
|
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
|
8
|
+
|
|
9
|
+
desc 'Install rsb-admin: copy migrations, mount routes, and create seed file.'
|
|
10
|
+
|
|
11
|
+
# Copies rsb-admin migrations to the host application.
|
|
12
|
+
# @return [void]
|
|
13
|
+
def copy_migrations
|
|
14
|
+
rake 'rsb_admin:install:migrations'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Mounts the rsb-admin engine at /admin in the host application's routes.
|
|
18
|
+
# @return [void]
|
|
19
|
+
def mount_routes
|
|
20
|
+
route 'mount RSB::Admin::Engine => "/admin"'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Copies the seed file template to db/seeds/rsb_admin.rb.
|
|
24
|
+
# Uses skip: true to avoid overwriting existing seed files.
|
|
25
|
+
# @return [void]
|
|
26
|
+
def copy_seed_file
|
|
27
|
+
copy_file 'rsb_admin_seeds.rb', 'db/seeds/rsb_admin.rb', skip: true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Prints post-installation instructions.
|
|
31
|
+
# @return [void]
|
|
32
|
+
def print_post_install
|
|
33
|
+
say ''
|
|
34
|
+
say 'rsb-admin installed successfully!', :green
|
|
35
|
+
say ''
|
|
36
|
+
say 'Next steps:'
|
|
37
|
+
say ' 1. rails db:migrate'
|
|
38
|
+
say ' 2. rails rsb:create_admin EMAIL=admin@example.com PASSWORD=changeme'
|
|
39
|
+
say ' OR edit db/seeds/rsb_admin.rb and run: rails db:seed'
|
|
40
|
+
say ' 3. Visit /admin/login'
|
|
41
|
+
say ''
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# RSB Admin Seed Data
|
|
4
|
+
#
|
|
5
|
+
# This file creates a default Superadmin role and initial admin user.
|
|
6
|
+
# Uncomment and customize the values below, then run:
|
|
7
|
+
#
|
|
8
|
+
# rails db:seed
|
|
9
|
+
#
|
|
10
|
+
# Alternatively, use the rake task for a quick one-off setup:
|
|
11
|
+
#
|
|
12
|
+
# rails rsb:create_admin EMAIL=admin@example.com PASSWORD=changeme
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
# role = RSB::Admin::Role.find_or_create_by!(name: "Superadmin") do |r|
|
|
16
|
+
# r.permissions = { "*" => ["*"] }
|
|
17
|
+
# r.built_in = true
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# RSB::Admin::AdminUser.find_or_create_by!(email: "admin@example.com") do |u|
|
|
21
|
+
# u.password = "changeme123"
|
|
22
|
+
# u.password_confirmation = "changeme123"
|
|
23
|
+
# u.role = role
|
|
24
|
+
# end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* <%= theme_name.titleize %> Theme for RSB Admin
|
|
2
|
+
*
|
|
3
|
+
* This file defines all CSS variables required by RSB Admin.
|
|
4
|
+
* Every --rsb-admin-* variable MUST be defined — there is no
|
|
5
|
+
* inheritance from the default theme. Missing variables will
|
|
6
|
+
* produce empty values, which may break layout.
|
|
7
|
+
*
|
|
8
|
+
* Customize the values below to create your theme.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
:root {
|
|
12
|
+
/* ── Background ─────────────────────────────────── */
|
|
13
|
+
--rsb-admin-bg: #f9fafb; /* Page background */
|
|
14
|
+
--rsb-admin-bg-secondary: #f3f4f6; /* Secondary/alternate background */
|
|
15
|
+
--rsb-admin-card-bg: #ffffff; /* Card/panel background */
|
|
16
|
+
|
|
17
|
+
/* ── Text ───────────────────────────────────────── */
|
|
18
|
+
--rsb-admin-text: #111827; /* Primary text color */
|
|
19
|
+
--rsb-admin-text-muted: #6b7280; /* Muted/secondary text */
|
|
20
|
+
|
|
21
|
+
/* ── Borders ────────────────────────────────────── */
|
|
22
|
+
--rsb-admin-border: #e5e7eb; /* Default border color */
|
|
23
|
+
|
|
24
|
+
/* ── Primary action ─────────────────────────────── */
|
|
25
|
+
--rsb-admin-primary: #4f46e5; /* Primary buttons, links */
|
|
26
|
+
--rsb-admin-primary-hover: #4338ca; /* Primary hover state */
|
|
27
|
+
--rsb-admin-primary-text: #ffffff; /* Text on primary backgrounds */
|
|
28
|
+
|
|
29
|
+
/* ── Status: Success ────────────────────────────── */
|
|
30
|
+
--rsb-admin-success: #16a34a; /* Success accent */
|
|
31
|
+
--rsb-admin-success-bg: #ecfdf5; /* Success background */
|
|
32
|
+
--rsb-admin-success-text: #065f46; /* Success text */
|
|
33
|
+
|
|
34
|
+
/* ── Status: Warning ────────────────────────────── */
|
|
35
|
+
--rsb-admin-warning: #d97706; /* Warning accent */
|
|
36
|
+
--rsb-admin-warning-bg: #fffbeb; /* Warning background */
|
|
37
|
+
--rsb-admin-warning-text: #92400e; /* Warning text */
|
|
38
|
+
|
|
39
|
+
/* ── Status: Danger ─────────────────────────────── */
|
|
40
|
+
--rsb-admin-danger: #dc2626; /* Danger accent */
|
|
41
|
+
--rsb-admin-danger-bg: #fef2f2; /* Danger background */
|
|
42
|
+
--rsb-admin-danger-text: #991b1b; /* Danger text */
|
|
43
|
+
|
|
44
|
+
/* ── Status: Info ───────────────────────────────── */
|
|
45
|
+
--rsb-admin-info: #2563eb; /* Info accent */
|
|
46
|
+
--rsb-admin-info-bg: #eff6ff; /* Info background */
|
|
47
|
+
--rsb-admin-info-text: #1e40af; /* Info text */
|
|
48
|
+
|
|
49
|
+
/* ── Sidebar ────────────────────────────────────── */
|
|
50
|
+
--rsb-admin-sidebar-bg: #1f2937; /* Sidebar background */
|
|
51
|
+
--rsb-admin-sidebar-text: #d1d5db; /* Sidebar text */
|
|
52
|
+
--rsb-admin-sidebar-active: #4f46e5; /* Sidebar active indicator */
|
|
53
|
+
--rsb-admin-sidebar-hover-bg: rgba(255,255,255,0.05); /* Sidebar hover */
|
|
54
|
+
|
|
55
|
+
/* ── Shape ──────────────────────────────────────── */
|
|
56
|
+
--rsb-admin-radius-sm: 0.25rem; /* Small border radius */
|
|
57
|
+
--rsb-admin-radius: 0.375rem; /* Default border radius */
|
|
58
|
+
--rsb-admin-radius-lg: 0.5rem; /* Large border radius */
|
|
59
|
+
|
|
60
|
+
/* ── Shadows ────────────────────────────────────── */
|
|
61
|
+
--rsb-admin-shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
|
|
62
|
+
--rsb-admin-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
|
|
63
|
+
|
|
64
|
+
/* ── Transitions ────────────────────────────────── */
|
|
65
|
+
--rsb-admin-transition: 0.15s; /* Default transition duration */
|
|
66
|
+
}
|