rails_nexus 2.0.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +430 -0
- data/Rakefile +6 -0
- data/app/assets/config/rails_nexus_manifest.js +2 -0
- data/app/assets/javascripts/rails_nexus/controllers.js +603 -0
- data/app/assets/stylesheets/rails_nexus/application.css +1964 -0
- data/app/controllers/rails_nexus/analytics_controller.rb +361 -0
- data/app/controllers/rails_nexus/application_controller.rb +15 -0
- data/app/controllers/rails_nexus/backup_controller.rb +91 -0
- data/app/controllers/rails_nexus/cron_jobs_controller.rb +64 -0
- data/app/controllers/rails_nexus/database_health_controller.rb +128 -0
- data/app/controllers/rails_nexus/logged_exceptions_controller.rb +227 -0
- data/app/controllers/rails_nexus/n1_patterns_controller.rb +10 -0
- data/app/controllers/rails_nexus/settings_controller.rb +100 -0
- data/app/controllers/rails_nexus/source_code_controller.rb +39 -0
- data/app/controllers/rails_nexus/stats_controller.rb +494 -0
- data/app/controllers/rails_nexus/workflow_controller.rb +96 -0
- data/app/helpers/rails_nexus/application_helper.rb +5 -0
- data/app/helpers/rails_nexus/logged_exceptions_helper.rb +254 -0
- data/app/helpers/rails_nexus/source_code_helper.rb +107 -0
- data/app/javascript/controllers/rails_nexus_controller.js +380 -0
- data/app/javascript/controllers/rails_nexus_detail_controller.js +165 -0
- data/app/javascript/controllers/rails_nexus_sidebar_controller.js +15 -0
- data/app/javascript/controllers/rails_nexus_theme_controller.js +31 -0
- data/app/javascript/rails_nexus/application.js +17 -0
- data/app/jobs/rails_nexus/application_job.rb +4 -0
- data/app/mailers/rails_nexus/application_mailer.rb +6 -0
- data/app/models/rails_nexus/application_record.rb +5 -0
- data/app/models/rails_nexus/backup.rb +107 -0
- data/app/models/rails_nexus/base_record.rb +26 -0
- data/app/models/rails_nexus/comment.rb +13 -0
- data/app/models/rails_nexus/cron_job.rb +72 -0
- data/app/models/rails_nexus/database_stat.rb +128 -0
- data/app/models/rails_nexus/event.rb +101 -0
- data/app/models/rails_nexus/logged_exception.rb +864 -0
- data/app/models/rails_nexus/logged_exception_n1.rb +87 -0
- data/app/models/rails_nexus/metric.rb +107 -0
- data/app/models/rails_nexus/nginx_metric.rb +112 -0
- data/app/models/rails_nexus/server_metric.rb +168 -0
- data/app/models/rails_nexus/webhook_delivery.rb +64 -0
- data/app/services/rails_nexus/backup_service.rb +275 -0
- data/app/views/layouts/rails_nexus/application.html.erb +185 -0
- data/app/views/rails_nexus/analytics/index.html.erb +363 -0
- data/app/views/rails_nexus/backup/files.html.erb +53 -0
- data/app/views/rails_nexus/backup/index.html.erb +211 -0
- data/app/views/rails_nexus/backup/settings.html.erb +158 -0
- data/app/views/rails_nexus/cron_jobs/index.html.erb +141 -0
- data/app/views/rails_nexus/database_health/index.html.erb +139 -0
- data/app/views/rails_nexus/logged_exceptions/_comments_list.html.erb +50 -0
- data/app/views/rails_nexus/logged_exceptions/_exceptions.html.erb +190 -0
- data/app/views/rails_nexus/logged_exceptions/_feed.html.erb +3 -0
- data/app/views/rails_nexus/logged_exceptions/_filter_group.html.erb +10 -0
- data/app/views/rails_nexus/logged_exceptions/_show.html.erb +554 -0
- data/app/views/rails_nexus/logged_exceptions/_workflow_panel.html.erb +118 -0
- data/app/views/rails_nexus/logged_exceptions/clear.turbo_stream.erb +6 -0
- data/app/views/rails_nexus/logged_exceptions/destroy.turbo_stream.erb +2 -0
- data/app/views/rails_nexus/logged_exceptions/destroy_all.turbo_stream.erb +6 -0
- data/app/views/rails_nexus/logged_exceptions/feed.rss.builder +20 -0
- data/app/views/rails_nexus/logged_exceptions/index.html.erb +323 -0
- data/app/views/rails_nexus/logged_exceptions/query.html.erb +3 -0
- data/app/views/rails_nexus/logged_exceptions/query.turbo_stream.erb +6 -0
- data/app/views/rails_nexus/logged_exceptions/show.html.erb +3 -0
- data/app/views/rails_nexus/logged_exceptions/show.turbo_stream.erb +5 -0
- data/app/views/rails_nexus/n1_patterns/index.html.erb +153 -0
- data/app/views/rails_nexus/settings/index.html.erb +140 -0
- data/app/views/rails_nexus/stats/index.html.erb +569 -0
- data/config/importmap.rb +15 -0
- data/config/initializers/date_formats.rb +5 -0
- data/config/initializers/rails_nexus.rb +18 -0
- data/config/locales/en.yml +47 -0
- data/config/routes.rb +58 -0
- data/db/migrate/20240330122311_create_rails_nexus_logged_exceptions.rb +22 -0
- data/lib/generators/rails_nexus/backup_generator.rb +70 -0
- data/lib/generators/rails_nexus/customize_generator.rb +159 -0
- data/lib/generators/rails_nexus/install_generator.rb +61 -0
- data/lib/generators/rails_nexus/templates/INSTALL.md +9 -0
- data/lib/generators/rails_nexus/templates/backup/backup_helper.sh +152 -0
- data/lib/generators/rails_nexus/templates/backup/config.rb +27 -0
- data/lib/generators/rails_nexus/templates/backup/models/daily_backup.rb +24 -0
- data/lib/generators/rails_nexus/templates/backup/models/full_backup.rb +35 -0
- data/lib/generators/rails_nexus/templates/backup/models/sync_backup.rb +43 -0
- data/lib/generators/rails_nexus/templates/backup/mysql-config/db_config.cnf +15 -0
- data/lib/generators/rails_nexus/templates/backup/schedule.rb +28 -0
- data/lib/generators/rails_nexus/templates/javascript/controllers/rails_nexus_controller.js +310 -0
- data/lib/generators/rails_nexus/templates/javascript/controllers/rails_nexus_detail_controller.js +98 -0
- data/lib/generators/rails_nexus/templates/javascript/controllers/rails_nexus_sidebar_controller.js +15 -0
- data/lib/generators/rails_nexus/templates/javascript/controllers/rails_nexus_theme_controller.js +30 -0
- data/lib/generators/rails_nexus/templates/migration.rb +24 -0
- data/lib/generators/rails_nexus/templates/migration_advanced_features.rb +30 -0
- data/lib/generators/rails_nexus/templates/migration_cron_jobs.rb +22 -0
- data/lib/generators/rails_nexus/templates/migration_new_schema.rb +120 -0
- data/lib/generators/rails_nexus/templates/migration_platform_detection.rb +11 -0
- data/lib/generators/rails_nexus/templates/migration_webhook_deliveries.rb +21 -0
- data/lib/generators/rails_nexus/templates/migration_workflow.rb +29 -0
- data/lib/generators/rails_nexus/templates/rails_nexus.rb +96 -0
- data/lib/generators/rails_nexus/templates/views/layouts/rails_nexus/application.html.erb +129 -0
- data/lib/generators/rails_nexus/templates/views/rails_nexus/logged_exceptions/_exceptions.html.erb +123 -0
- data/lib/generators/rails_nexus/templates/views/rails_nexus/logged_exceptions/_show.html.erb +297 -0
- data/lib/generators/rails_nexus/templates/views/rails_nexus/logged_exceptions/index.html.erb +113 -0
- data/lib/rails_nexus/breadcrumbs.rb +161 -0
- data/lib/rails_nexus/cleanup.rb +32 -0
- data/lib/rails_nexus/configuration.rb +319 -0
- data/lib/rails_nexus/engine.rb +135 -0
- data/lib/rails_nexus/logger.rb +188 -0
- data/lib/rails_nexus/notifications.rb +105 -0
- data/lib/rails_nexus/ransack_config.rb +4 -0
- data/lib/rails_nexus/storm_protection.rb +124 -0
- data/lib/rails_nexus/version.rb +3 -0
- data/lib/rails_nexus.rb +133 -0
- data/lib/tasks/rails_nexus.rake +230 -0
- data/lib/tasks/rails_nexus_tasks.rake +37 -0
- data/rails_nexus.gemspec +36 -0
- metadata +252 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
en:
|
|
2
|
+
rails_nexus:
|
|
3
|
+
logged_exceptions:
|
|
4
|
+
index:
|
|
5
|
+
title: Logged Exceptions
|
|
6
|
+
subtitle: Review, filter, and remove application exceptions.
|
|
7
|
+
filters: Filters
|
|
8
|
+
latest_exceptions: Latest Exceptions
|
|
9
|
+
exception: Exception
|
|
10
|
+
controller_action: Controller / Action
|
|
11
|
+
dates: Dates
|
|
12
|
+
today: Today
|
|
13
|
+
last_few_days: Last few days
|
|
14
|
+
last_7_days: Last 7 days
|
|
15
|
+
last_30_days: Last 30 days
|
|
16
|
+
clear_history: Clear history
|
|
17
|
+
confirm_clear: Clear the complete exception history?
|
|
18
|
+
search: Search
|
|
19
|
+
search_placeholder: Search messages
|
|
20
|
+
find: Find
|
|
21
|
+
loading: Loading exceptions…
|
|
22
|
+
select_exception: Select an exception to inspect its details.
|
|
23
|
+
_exceptions:
|
|
24
|
+
heading: Exceptions
|
|
25
|
+
filtered: filtered
|
|
26
|
+
count: "%{count} total"
|
|
27
|
+
empty: No exceptions match these filters.
|
|
28
|
+
exception: Exception
|
|
29
|
+
date: Date
|
|
30
|
+
actions: Actions
|
|
31
|
+
delete: Delete
|
|
32
|
+
delete_visible: Delete visible
|
|
33
|
+
confirm_delete: Delete this exception?
|
|
34
|
+
confirm_delete_visible: Delete all visible exceptions?
|
|
35
|
+
_show:
|
|
36
|
+
close: Close
|
|
37
|
+
request: Request
|
|
38
|
+
message: Message
|
|
39
|
+
backtrace: Backtrace
|
|
40
|
+
environment: Environment
|
|
41
|
+
delete: Delete exception
|
|
42
|
+
confirm_delete: Delete this exception?
|
|
43
|
+
_feed:
|
|
44
|
+
rss_feed: Subscribe to the RSS feed
|
|
45
|
+
show:
|
|
46
|
+
title: Logged Exception
|
|
47
|
+
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
RailsNexus::Engine.routes.draw do
|
|
2
|
+
resources :logged_exceptions do
|
|
3
|
+
collection do
|
|
4
|
+
post :clear
|
|
5
|
+
match :query, via: %i[get post]
|
|
6
|
+
post :destroy_all
|
|
7
|
+
get :feed
|
|
8
|
+
end
|
|
9
|
+
member do
|
|
10
|
+
post :assign, to: "workflow#assign"
|
|
11
|
+
post :set_priority, to: "workflow#set_priority"
|
|
12
|
+
post :snooze, to: "workflow#snooze"
|
|
13
|
+
post :mute, to: "workflow#mute"
|
|
14
|
+
post :unmute, to: "workflow#unmute"
|
|
15
|
+
post :add_comment, to: "workflow#add_comment"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Server statistics
|
|
20
|
+
get "stats", to: "stats#index", as: :stats
|
|
21
|
+
|
|
22
|
+
# Database health
|
|
23
|
+
get "database_health", to: "database_health#index", as: :database_health
|
|
24
|
+
|
|
25
|
+
# Real-time analytics
|
|
26
|
+
get "analytics", to: "analytics#index", as: :analytics
|
|
27
|
+
|
|
28
|
+
# Cron job monitoring
|
|
29
|
+
resources :cron_jobs, only: [:index, :show, :destroy] do
|
|
30
|
+
collection do
|
|
31
|
+
delete :clear_old
|
|
32
|
+
end
|
|
33
|
+
member do
|
|
34
|
+
post :retry_job
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Source code viewing
|
|
39
|
+
get "source_code", to: "source_code#show", as: :source_code
|
|
40
|
+
|
|
41
|
+
# N+1 query patterns
|
|
42
|
+
get "n1_patterns", to: "n1_patterns#index", as: :n1_patterns
|
|
43
|
+
|
|
44
|
+
# Backup management
|
|
45
|
+
get "backup", to: "backup#index", as: :backup
|
|
46
|
+
get "backup/files", to: "backup#files", as: :backup_files
|
|
47
|
+
post "backup/trigger/:model", to: "backup#trigger", as: :backup_trigger
|
|
48
|
+
get "backup/health", to: "backup#health", as: :backup_health
|
|
49
|
+
get "backup/settings", to: "backup#settings", as: :backup_settings
|
|
50
|
+
patch "backup/settings", to: "backup#update_settings", as: :backup_update_settings
|
|
51
|
+
|
|
52
|
+
# Settings and webhook management
|
|
53
|
+
get "settings", to: "settings#index", as: :settings
|
|
54
|
+
post "settings/test_webhook", to: "settings#test_webhook", as: :test_webhook
|
|
55
|
+
get "settings/deliveries", to: "settings#deliveries", as: :webhook_deliveries
|
|
56
|
+
|
|
57
|
+
root :to => 'logged_exceptions#index'
|
|
58
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class CreateRailsNexusLoggedExceptions < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :rails_nexus_exceptions do |t|
|
|
4
|
+
t.string :exception_class
|
|
5
|
+
t.string :controller_name
|
|
6
|
+
t.string :action_name
|
|
7
|
+
t.text :message
|
|
8
|
+
t.text :backtrace
|
|
9
|
+
t.text :environment
|
|
10
|
+
t.text :request
|
|
11
|
+
t.string :user_info
|
|
12
|
+
t.string :user_agent
|
|
13
|
+
t.string :remote_ip
|
|
14
|
+
|
|
15
|
+
t.timestamps
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
add_index :rails_nexus_exceptions, :created_at
|
|
19
|
+
add_index :rails_nexus_exceptions, :exception_class
|
|
20
|
+
add_index :rails_nexus_exceptions, [:controller_name, :action_name]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/erb"
|
|
5
|
+
|
|
6
|
+
module RailsNexus
|
|
7
|
+
module Generators
|
|
8
|
+
class BackupGenerator < Rails::Generators::Base
|
|
9
|
+
desc "Set up rails_nexus backup directory structure and models"
|
|
10
|
+
source_root File.expand_path("templates/backup", __dir__)
|
|
11
|
+
|
|
12
|
+
def create_backup_directories
|
|
13
|
+
empty_directory "config/backup"
|
|
14
|
+
empty_directory "config/backup/models"
|
|
15
|
+
empty_directory "config/backup/mysql-config"
|
|
16
|
+
empty_directory "storage/rails_nexus/backups"
|
|
17
|
+
empty_directory "storage/rails_nexus/backups/sync"
|
|
18
|
+
empty_directory "storage/rails_nexus/backups/logs"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create_config_template
|
|
22
|
+
template "config.rb", "config/backup/config.rb"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def create_model_templates
|
|
26
|
+
template "models/daily_backup.rb", "config/backup/models/daily_backup.rb"
|
|
27
|
+
template "models/full_backup.rb", "config/backup/models/full_backup.rb"
|
|
28
|
+
template "models/sync_backup.rb", "config/backup/models/sync_backup.rb"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_schedule_template
|
|
32
|
+
template "schedule.rb", "config/backup/schedule.rb"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create_helper_script
|
|
36
|
+
template "backup_helper.sh", "bin/rails_nexus-backup"
|
|
37
|
+
chmod "bin/rails_nexus-backup", 0o755
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def create_mysql_config
|
|
41
|
+
template "mysql-config/db_config.cnf", "config/backup/mysql-config/db_config.cnf"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def add_to_gitignore
|
|
45
|
+
append_to_file ".gitignore", <<~GITIGNORE unless File.exist?(".gitignore") && File.read(".gitignore").include?("rails_nexus/backups")
|
|
46
|
+
\n# RailsNexus backups
|
|
47
|
+
storage/rails_nexus/backups/
|
|
48
|
+
config/backup/mysql-config/db_config.cnf
|
|
49
|
+
GITIGNORE
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def show_readme
|
|
53
|
+
say ""
|
|
54
|
+
say "✅ Backup directory structure created!", :green
|
|
55
|
+
say ""
|
|
56
|
+
say "Next steps:"
|
|
57
|
+
say " 1. Edit config/backup/config.rb with your database credentials"
|
|
58
|
+
say " 2. Customize models in config/backup/models/"
|
|
59
|
+
say " 3. Install the schedule: whenever --write-crontab"
|
|
60
|
+
say " 4. Test: bin/rails_nexus-backup status"
|
|
61
|
+
say ""
|
|
62
|
+
say "Backup models created:"
|
|
63
|
+
say " - daily_backup: Incremental (skips large tables), local + remote"
|
|
64
|
+
say " - full_backup: All tables, local only"
|
|
65
|
+
say " - sync_backup: Remote sync to backup server"
|
|
66
|
+
say ""
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module RailsNexus
|
|
6
|
+
module Generators
|
|
7
|
+
class CustomizeGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
desc <<~DESC
|
|
11
|
+
Generate customizable RailsNexus dashboard files.
|
|
12
|
+
|
|
13
|
+
Creates modern views, host-app-integrated layout, Stimulus controller,
|
|
14
|
+
and initializer patches for your Rails application.
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
rails generate rails_nexus:customize
|
|
18
|
+
rails generate rails_nexus:customize --layout-only
|
|
19
|
+
rails generate rails_nexus:customize --views-only
|
|
20
|
+
rails generate rails_nexus:customize --stimulus-only
|
|
21
|
+
rails generate rails_nexus:customize --initializer-only
|
|
22
|
+
DESC
|
|
23
|
+
|
|
24
|
+
class_option :layout_only, type: :boolean, default: false, desc: "Only generate the layout"
|
|
25
|
+
class_option :views_only, type: :boolean, default: false, desc: "Only generate the views"
|
|
26
|
+
class_option :stimulus_only, type: :boolean, default: false, desc: "Only generate the Stimulus controller"
|
|
27
|
+
class_option :initializer_only, type: :boolean, default: false, desc: "Only generate the initializer patches"
|
|
28
|
+
class_option :force, type: :boolean, default: false, desc: "Overwrite existing files"
|
|
29
|
+
|
|
30
|
+
def generate_layout
|
|
31
|
+
return unless generate_all? || options[:layout_only]
|
|
32
|
+
|
|
33
|
+
template "views/layouts/rails_nexus/application.html.erb",
|
|
34
|
+
"app/views/layouts/rails_nexus/application.html.erb"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def generate_views
|
|
38
|
+
return unless generate_all? || options[:views_only]
|
|
39
|
+
|
|
40
|
+
template "views/rails_nexus/logged_exceptions/index.html.erb",
|
|
41
|
+
"app/views/rails_nexus/logged_exceptions/index.html.erb"
|
|
42
|
+
|
|
43
|
+
template "views/rails_nexus/logged_exceptions/_exceptions.html.erb",
|
|
44
|
+
"app/views/rails_nexus/logged_exceptions/_exceptions.html.erb"
|
|
45
|
+
|
|
46
|
+
template "views/rails_nexus/logged_exceptions/_show.html.erb",
|
|
47
|
+
"app/views/rails_nexus/logged_exceptions/_show.html.erb"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def generate_stimulus
|
|
51
|
+
return unless generate_all? || options[:stimulus_only]
|
|
52
|
+
|
|
53
|
+
target_dir = "app/javascript/rails_nexus/controllers"
|
|
54
|
+
|
|
55
|
+
template "javascript/controllers/rails_nexus_controller.js",
|
|
56
|
+
"#{target_dir}/rails_nexus_controller.js"
|
|
57
|
+
template "javascript/controllers/rails_nexus_detail_controller.js",
|
|
58
|
+
"#{target_dir}/rails_nexus_detail_controller.js"
|
|
59
|
+
template "javascript/controllers/rails_nexus_theme_controller.js",
|
|
60
|
+
"#{target_dir}/rails_nexus_theme_controller.js"
|
|
61
|
+
template "javascript/controllers/rails_nexus_sidebar_controller.js",
|
|
62
|
+
"#{target_dir}/rails_nexus_sidebar_controller.js"
|
|
63
|
+
|
|
64
|
+
# Create a standalone manifest under rails_nexus/ so it works
|
|
65
|
+
# with both esbuild (jsbundling) and importmap.
|
|
66
|
+
# This avoids conflict with stimulus:manifest:update which
|
|
67
|
+
# only scans app/javascript/controllers/.
|
|
68
|
+
create_file "app/javascript/rails_nexus/index.js" do
|
|
69
|
+
<<~JS
|
|
70
|
+
import { application } from "../controllers/application"
|
|
71
|
+
|
|
72
|
+
import RailsNexusController from "./controllers/rails_nexus_controller"
|
|
73
|
+
application.register("rails_nexus", RailsNexusController)
|
|
74
|
+
|
|
75
|
+
import RailsNexusDetailController from "./controllers/rails_nexus_detail_controller"
|
|
76
|
+
application.register("rails_nexus-detail", RailsNexusDetailController)
|
|
77
|
+
|
|
78
|
+
import RailsNexusThemeController from "./controllers/rails_nexus_theme_controller"
|
|
79
|
+
application.register("rails_nexus-theme", RailsNexusThemeController)
|
|
80
|
+
|
|
81
|
+
import RailsNexusSidebarController from "./controllers/rails_nexus_sidebar_controller"
|
|
82
|
+
application.register("rails_nexus-sidebar", RailsNexusSidebarController)
|
|
83
|
+
JS
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Inject import into application.js if not already present
|
|
87
|
+
app_js = "app/javascript/application.js"
|
|
88
|
+
if File.exist?(app_js)
|
|
89
|
+
content = File.read(app_js)
|
|
90
|
+
unless content.include?("rails_nexus")
|
|
91
|
+
inject_into_file app_js,
|
|
92
|
+
'import "./rails_nexus"
|
|
93
|
+
',
|
|
94
|
+
after: /import.*controllers.*\n/
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def generate_initializer_patches
|
|
100
|
+
return unless generate_all? || options[:initializer_only]
|
|
101
|
+
|
|
102
|
+
append_to_file "config/initializers/rails_nexus.rb" do
|
|
103
|
+
"\n# --- RailsNexus Customize Generator Patches ---\n" \
|
|
104
|
+
"\n" \
|
|
105
|
+
"# Make host app helpers available in rails_nexus views.\n" \
|
|
106
|
+
"# Uncomment the helpers your app defines:\n" \
|
|
107
|
+
"# RailsNexus::LoggedExceptionsController.helper HeroiconHelper\n" \
|
|
108
|
+
"# RailsNexus::LoggedExceptionsController.helper ApplicationHelper\n" \
|
|
109
|
+
"\n" \
|
|
110
|
+
"# Fix for Rails 8.1+: params_filters must return plain hashes.\n" \
|
|
111
|
+
"# This is applied automatically by the gem (v0.5.1+).\n" \
|
|
112
|
+
"# Uncomment below if needed:\n" \
|
|
113
|
+
"# RailsNexus::LoggedExceptionsController.class_eval do\n" \
|
|
114
|
+
"# private\n" \
|
|
115
|
+
"# def params_filters\n" \
|
|
116
|
+
"# result = {}\n" \
|
|
117
|
+
"# result[:q] = params[:q].to_unsafe_h if params[:q].present?\n" \
|
|
118
|
+
"# result[:query] = params[:query] if params[:query].present?\n" \
|
|
119
|
+
"# result[:date_ranges_filter] = params[:date_ranges_filter] if params[:date_ranges_filter].present?\n" \
|
|
120
|
+
"# result[:exception_names_filter] = params[:exception_names_filter] if params[:exception_names_filter].present?\n" \
|
|
121
|
+
"# result[:controller_actions_filter] = params[:controller_actions_filter] if params[:controller_actions_filter].present?\n" \
|
|
122
|
+
"# result\n" \
|
|
123
|
+
"# end\n" \
|
|
124
|
+
"# end\n"
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def display_post_install
|
|
129
|
+
return unless generate_all?
|
|
130
|
+
|
|
131
|
+
say ""
|
|
132
|
+
say "RailsNexus customization files generated!", :green
|
|
133
|
+
say ""
|
|
134
|
+
say "Generated files:"
|
|
135
|
+
say " - app/views/layouts/rails_nexus/application.html.erb (host-app layout)"
|
|
136
|
+
say " - app/views/rails_nexus/logged_exceptions/index.html.erb"
|
|
137
|
+
say " - app/views/rails_nexus/logged_exceptions/_exceptions.html.erb"
|
|
138
|
+
say " - app/views/rails_nexus/logged_exceptions/_show.html.erb"
|
|
139
|
+
say " - app/javascript/rails_nexus/controllers/rails_nexus_controller.js"
|
|
140
|
+
say " - app/javascript/rails_nexus/controllers/rails_nexus_detail_controller.js"
|
|
141
|
+
say " - app/javascript/rails_nexus/controllers/rails_nexus_theme_controller.js"
|
|
142
|
+
say " - app/javascript/rails_nexus/controllers/rails_nexus_sidebar_controller.js"
|
|
143
|
+
say ""
|
|
144
|
+
say "Next steps:"
|
|
145
|
+
say " 1. Edit the layout to match your host app's navigation"
|
|
146
|
+
say " 2. If using Tailwind, add to your CSS source paths:"
|
|
147
|
+
say ' @source "path/to/rails_nexus/app/views/**/*.erb";'
|
|
148
|
+
say ""
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def generate_all?
|
|
154
|
+
!options[:layout_only] && !options[:views_only] &&
|
|
155
|
+
!options[:stimulus_only] && !options[:initializer_only]
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module RailsNexus
|
|
7
|
+
module Generators
|
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
|
9
|
+
include Rails::Generators::Migration
|
|
10
|
+
|
|
11
|
+
MIGRATIONS = {
|
|
12
|
+
"migration.rb" => "create_rails_nexus_exceptions.rb",
|
|
13
|
+
"migration_advanced_features.rb" => "add_advanced_features_to_rails_nexus_exceptions.rb",
|
|
14
|
+
"migration_platform_detection.rb" => "add_platform_to_rails_nexus_exceptions.rb",
|
|
15
|
+
"migration_workflow.rb" => "add_workflow_to_rails_nexus.rb",
|
|
16
|
+
"migration_cron_jobs.rb" => "create_rails_nexus_cron_jobs.rb",
|
|
17
|
+
"migration_webhook_deliveries.rb" => "create_rails_nexus_webhook_deliveries.rb",
|
|
18
|
+
"migration_new_schema.rb" => "create_rails_nexus_metrics_tables.rb"
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
source_root File.expand_path("templates", __dir__)
|
|
22
|
+
desc "Install RailsNexus into your Rails application"
|
|
23
|
+
|
|
24
|
+
def self.next_migration_number(dirname)
|
|
25
|
+
ActiveRecord::Generators::Base.next_migration_number(dirname)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def create_migration_files
|
|
29
|
+
MIGRATIONS.each do |source, destination|
|
|
30
|
+
migration_template source, "db/migrate/#{destination}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_initializer
|
|
35
|
+
template "rails_nexus.rb", "config/initializers/rails_nexus.rb"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def mount_routes
|
|
39
|
+
route 'mount RailsNexus::Engine, at: "/rails_nexus"'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def add_rescue_from
|
|
43
|
+
app_controller = "app/controllers/application_controller.rb"
|
|
44
|
+
destination_controller = File.join(destination_root, app_controller)
|
|
45
|
+
|
|
46
|
+
if File.exist?(destination_controller)
|
|
47
|
+
content = File.read(destination_controller)
|
|
48
|
+
unless content.include?("log_exception_handler")
|
|
49
|
+
inject_into_class app_controller, "ApplicationController" do
|
|
50
|
+
"\n rescue_from Exception, with: :log_exception_handler\n"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def show_readme
|
|
57
|
+
readme "INSTALL.md" if behavior == :invoke
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
RailsNexus has been installed.
|
|
2
|
+
|
|
3
|
+
Next steps:
|
|
4
|
+
|
|
5
|
+
1. Review `config/initializers/rails_nexus.rb` and configure `auth_block`.
|
|
6
|
+
2. Run `bin/rails db:migrate`.
|
|
7
|
+
3. Visit `/rails_nexus` in the host application.
|
|
8
|
+
|
|
9
|
+
The dashboard denies access until authentication is configured.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# RailsNexus Backup Helper Script
|
|
3
|
+
# Generated by: rails generate rails_nexus:backup
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./backup_helper.sh run <model> Run a backup model
|
|
7
|
+
# ./backup_helper.sh list List backup files
|
|
8
|
+
# ./backup_helper.sh status Show backup health status
|
|
9
|
+
# ./backup_helper.sh clean <days> Remove backups older than N days
|
|
10
|
+
|
|
11
|
+
set -e
|
|
12
|
+
|
|
13
|
+
# Configuration
|
|
14
|
+
BACKUP_DIR="${BACKUP_DUMP_PATH:-storage/rails_nexus/backups}"
|
|
15
|
+
BUNDLE_PATH="${BUNDLE_PATH:-.bundle}"
|
|
16
|
+
RUBY_ENV="${RUBY_ENV:-production}"
|
|
17
|
+
|
|
18
|
+
# Colors
|
|
19
|
+
RED='\033[0;31m'
|
|
20
|
+
GREEN='\033[0;32m'
|
|
21
|
+
YELLOW='\033[1;33m'
|
|
22
|
+
NC='\033[0m'
|
|
23
|
+
|
|
24
|
+
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
25
|
+
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
26
|
+
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
27
|
+
|
|
28
|
+
# Run a backup
|
|
29
|
+
run_backup() {
|
|
30
|
+
local model="$1"
|
|
31
|
+
if [ -z "$model" ]; then
|
|
32
|
+
log_error "Usage: $0 run <model_name>"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Lock to prevent concurrent runs
|
|
37
|
+
LOCKFILE="/tmp/rails_nexus_backup_${model}.lock"
|
|
38
|
+
exec 200>"$LOCKFILE"
|
|
39
|
+
if ! flock -n 200; then
|
|
40
|
+
log_warn "Backup '$model' is already running. Skipping."
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
log_info "Running backup: $model"
|
|
45
|
+
cd "$(dirname "$0")/../../.."
|
|
46
|
+
|
|
47
|
+
if bundle exec backup perform -t "$model" --triggered_by rails_nexus; then
|
|
48
|
+
log_info "Backup '$model' completed successfully."
|
|
49
|
+
else
|
|
50
|
+
log_error "Backup '$model' FAILED."
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# List backup files
|
|
56
|
+
list_backups() {
|
|
57
|
+
local dir="$BACKUP_DIR"
|
|
58
|
+
if [ ! -d "$dir" ]; then
|
|
59
|
+
log_warn "No backup directory found at: $dir"
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
echo "Backup files in: $dir"
|
|
64
|
+
echo "─────────────────────────────────────────────"
|
|
65
|
+
printf "%-40s %10s %s\n" "FILE" "SIZE" "MODIFIED"
|
|
66
|
+
echo "─────────────────────────────────────────────"
|
|
67
|
+
|
|
68
|
+
find "$dir" -maxdepth 1 -type f -name "*.tar.gz*" -o -name "*.sql.gz*" | sort -r | head -20 | while read -r f; do
|
|
69
|
+
size=$(du -h "$f" | cut -f1)
|
|
70
|
+
modified=$(stat -c "%y" "$f" 2>/dev/null | cut -d'.' -f1 || date -r "$f" "+%Y-%m-%d %H:%M")
|
|
71
|
+
printf "%-40s %10s %s\n" "$(basename "$f")" "$size" "$modified"
|
|
72
|
+
done
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Show backup status
|
|
76
|
+
show_status() {
|
|
77
|
+
local dir="$BACKUP_DIR"
|
|
78
|
+
if [ ! -d "$dir" ]; then
|
|
79
|
+
echo "Status: NOT_CONFIGURED"
|
|
80
|
+
echo "No backup directory at: $dir"
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
local total_files
|
|
85
|
+
total_files=$(find "$dir" -maxdepth 1 -type f | wc -l)
|
|
86
|
+
local total_size
|
|
87
|
+
total_size=$(du -sh "$dir" 2>/dev/null | cut -f1 || echo "0B")
|
|
88
|
+
|
|
89
|
+
local latest_file
|
|
90
|
+
latest_file=$(find "$dir" -maxdepth 1 -type f -name "*.tar.gz*" -o -name "*.sql.gz*" 2>/dev/null | sort -r | head -1)
|
|
91
|
+
|
|
92
|
+
if [ -z "$latest_file" ]; then
|
|
93
|
+
echo "Status: WARNING - No backup files found"
|
|
94
|
+
else
|
|
95
|
+
local age_hours
|
|
96
|
+
age_hours=$(( ($(date +%s) - $(stat -c "%Y" "$latest_file" 2>/dev/null || stat -f "%m" "$latest_file")) / 3600 ))
|
|
97
|
+
|
|
98
|
+
if [ "$age_hours" -lt 24 ]; then
|
|
99
|
+
echo "Status: HEALTHY"
|
|
100
|
+
elif [ "$age_hours" -lt 72 ]; then
|
|
101
|
+
echo "Status: WARNING - Last backup ${age_hours}h ago"
|
|
102
|
+
else
|
|
103
|
+
echo "Status: CRITICAL - Last backup ${age_hours}h ago"
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
echo "Files: $total_files"
|
|
108
|
+
echo "Size: $total_size"
|
|
109
|
+
[ -n "$latest_file" ] && echo "Latest: $(basename "$latest_file")"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Clean old backups
|
|
113
|
+
clean_backups() {
|
|
114
|
+
local days="${1:-30}"
|
|
115
|
+
local dir="$BACKUP_DIR"
|
|
116
|
+
|
|
117
|
+
if [ ! -d "$dir" ]; then
|
|
118
|
+
log_warn "No backup directory found at: $dir"
|
|
119
|
+
exit 0
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
log_info "Removing backups older than $days days..."
|
|
123
|
+
find "$dir" -maxdepth 1 -type f -mtime +"$days" -delete
|
|
124
|
+
log_info "Cleanup complete."
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Main
|
|
128
|
+
case "${1:-help}" in
|
|
129
|
+
run)
|
|
130
|
+
run_backup "$2"
|
|
131
|
+
;;
|
|
132
|
+
list)
|
|
133
|
+
list_backups
|
|
134
|
+
;;
|
|
135
|
+
status)
|
|
136
|
+
show_status
|
|
137
|
+
;;
|
|
138
|
+
clean)
|
|
139
|
+
clean_backups "$2"
|
|
140
|
+
;;
|
|
141
|
+
*)
|
|
142
|
+
echo "RailsNexus Backup Helper"
|
|
143
|
+
echo ""
|
|
144
|
+
echo "Usage: $0 <command> [args]"
|
|
145
|
+
echo ""
|
|
146
|
+
echo "Commands:"
|
|
147
|
+
echo " run <model> Run a backup model (e.g., daily_backup)"
|
|
148
|
+
echo " list List recent backup files"
|
|
149
|
+
echo " status Show backup health status"
|
|
150
|
+
echo " clean [days] Remove backups older than N days (default: 30)"
|
|
151
|
+
;;
|
|
152
|
+
esac
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Backup v5.x Configuration
|
|
5
|
+
# Generated by rails_nexus
|
|
6
|
+
#
|
|
7
|
+
# Documentation: http://backup.github.io/backup
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
# Logging
|
|
11
|
+
# Logger.configure do
|
|
12
|
+
# console.quiet = true
|
|
13
|
+
# logfile.max_bytes = 2_000_000
|
|
14
|
+
# syslog.enabled = true
|
|
15
|
+
# syslog.ident = '<%= Rails.application.class.module_parent_name.underscore %>_backup'
|
|
16
|
+
# end
|
|
17
|
+
|
|
18
|
+
# Storage defaults
|
|
19
|
+
# Storage::Local.defaults do |local|
|
|
20
|
+
# local.path = 'storage/rails_nexus/backups'
|
|
21
|
+
# end
|
|
22
|
+
|
|
23
|
+
# Notifier defaults
|
|
24
|
+
# Notifier::Mail.defaults do |mail|
|
|
25
|
+
# mail.from = 'backup@<%= Rails.application.class.module_parent_name.underscore %>.com'
|
|
26
|
+
# mail.to = 'admin@<%= Rails.application.class.module_parent_name.underscore %>.com'
|
|
27
|
+
# end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
# Daily Backup - Generated by rails_nexus
|
|
4
|
+
# Skips large/temporary tables for faster backups
|
|
5
|
+
|
|
6
|
+
Model.new(:daily_backup, '<%= Rails.application.class.module_parent_name %> Daily Backup') do
|
|
7
|
+
|
|
8
|
+
## MySQL [Database]
|
|
9
|
+
database MySQL do |db|
|
|
10
|
+
db.name = "<%= Rails.configuration.database_configuration[Rails.env]['database'] %>"
|
|
11
|
+
db.port = <%= Rails.configuration.database_configuration[Rails.env]['port'] || 3306 %>
|
|
12
|
+
db.skip_tables = []
|
|
13
|
+
db.additional_options = ["--quick"]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
## Local [Storage]
|
|
17
|
+
store_with Local do |local|
|
|
18
|
+
local.path = 'storage/rails_nexus/backups'
|
|
19
|
+
local.keep = 30
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
## Gzip [Compressor]
|
|
23
|
+
compress_with Gzip
|
|
24
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Full backup template — all tables, local storage only
|
|
3
|
+
|
|
4
|
+
model :full_backup do
|
|
5
|
+
# Database
|
|
6
|
+
database Mysql do |db|
|
|
7
|
+
db.name = ENV["DATABASE_NAME"] || "myapp_production"
|
|
8
|
+
db.username = ENV["DATABASE_USER"] || "root"
|
|
9
|
+
db.password = ENV["DATABASE_PASSWORD"] || ""
|
|
10
|
+
db.host = ENV["DATABASE_HOST"] || "localhost"
|
|
11
|
+
db.port = (ENV["DATABASE_PORT"] || 3306).to_i
|
|
12
|
+
db.dump_options = {
|
|
13
|
+
single_transaction: true,
|
|
14
|
+
add_lock_tables: false
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Compress with Gzip
|
|
19
|
+
compress_with Gzip
|
|
20
|
+
|
|
21
|
+
# Store locally
|
|
22
|
+
store_with Local do |local|
|
|
23
|
+
local.path = ENV["BACKUP_DUMP_PATH"] || File.expand_path("storage/rails_nexus/backups", Rails.root)
|
|
24
|
+
local.keep = 10
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Notify on failure (optional)
|
|
28
|
+
notify_by Mail do |mail|
|
|
29
|
+
mail.on_success = false
|
|
30
|
+
mail.on_failure = true
|
|
31
|
+
mail.from = ENV["BACKUP_MAIL_FROM"] || "backup@example.com"
|
|
32
|
+
mail.to = ENV["BACKUP_MAIL_TO"] || "admin@example.com"
|
|
33
|
+
mail.smtp = { address: ENV["SMTP_HOST"] || "localhost", port: ENV["SMTP_PORT"] || 587 }
|
|
34
|
+
end
|
|
35
|
+
end
|