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.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +430 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/config/rails_nexus_manifest.js +2 -0
  6. data/app/assets/javascripts/rails_nexus/controllers.js +603 -0
  7. data/app/assets/stylesheets/rails_nexus/application.css +1964 -0
  8. data/app/controllers/rails_nexus/analytics_controller.rb +361 -0
  9. data/app/controllers/rails_nexus/application_controller.rb +15 -0
  10. data/app/controllers/rails_nexus/backup_controller.rb +91 -0
  11. data/app/controllers/rails_nexus/cron_jobs_controller.rb +64 -0
  12. data/app/controllers/rails_nexus/database_health_controller.rb +128 -0
  13. data/app/controllers/rails_nexus/logged_exceptions_controller.rb +227 -0
  14. data/app/controllers/rails_nexus/n1_patterns_controller.rb +10 -0
  15. data/app/controllers/rails_nexus/settings_controller.rb +100 -0
  16. data/app/controllers/rails_nexus/source_code_controller.rb +39 -0
  17. data/app/controllers/rails_nexus/stats_controller.rb +494 -0
  18. data/app/controllers/rails_nexus/workflow_controller.rb +96 -0
  19. data/app/helpers/rails_nexus/application_helper.rb +5 -0
  20. data/app/helpers/rails_nexus/logged_exceptions_helper.rb +254 -0
  21. data/app/helpers/rails_nexus/source_code_helper.rb +107 -0
  22. data/app/javascript/controllers/rails_nexus_controller.js +380 -0
  23. data/app/javascript/controllers/rails_nexus_detail_controller.js +165 -0
  24. data/app/javascript/controllers/rails_nexus_sidebar_controller.js +15 -0
  25. data/app/javascript/controllers/rails_nexus_theme_controller.js +31 -0
  26. data/app/javascript/rails_nexus/application.js +17 -0
  27. data/app/jobs/rails_nexus/application_job.rb +4 -0
  28. data/app/mailers/rails_nexus/application_mailer.rb +6 -0
  29. data/app/models/rails_nexus/application_record.rb +5 -0
  30. data/app/models/rails_nexus/backup.rb +107 -0
  31. data/app/models/rails_nexus/base_record.rb +26 -0
  32. data/app/models/rails_nexus/comment.rb +13 -0
  33. data/app/models/rails_nexus/cron_job.rb +72 -0
  34. data/app/models/rails_nexus/database_stat.rb +128 -0
  35. data/app/models/rails_nexus/event.rb +101 -0
  36. data/app/models/rails_nexus/logged_exception.rb +864 -0
  37. data/app/models/rails_nexus/logged_exception_n1.rb +87 -0
  38. data/app/models/rails_nexus/metric.rb +107 -0
  39. data/app/models/rails_nexus/nginx_metric.rb +112 -0
  40. data/app/models/rails_nexus/server_metric.rb +168 -0
  41. data/app/models/rails_nexus/webhook_delivery.rb +64 -0
  42. data/app/services/rails_nexus/backup_service.rb +275 -0
  43. data/app/views/layouts/rails_nexus/application.html.erb +185 -0
  44. data/app/views/rails_nexus/analytics/index.html.erb +363 -0
  45. data/app/views/rails_nexus/backup/files.html.erb +53 -0
  46. data/app/views/rails_nexus/backup/index.html.erb +211 -0
  47. data/app/views/rails_nexus/backup/settings.html.erb +158 -0
  48. data/app/views/rails_nexus/cron_jobs/index.html.erb +141 -0
  49. data/app/views/rails_nexus/database_health/index.html.erb +139 -0
  50. data/app/views/rails_nexus/logged_exceptions/_comments_list.html.erb +50 -0
  51. data/app/views/rails_nexus/logged_exceptions/_exceptions.html.erb +190 -0
  52. data/app/views/rails_nexus/logged_exceptions/_feed.html.erb +3 -0
  53. data/app/views/rails_nexus/logged_exceptions/_filter_group.html.erb +10 -0
  54. data/app/views/rails_nexus/logged_exceptions/_show.html.erb +554 -0
  55. data/app/views/rails_nexus/logged_exceptions/_workflow_panel.html.erb +118 -0
  56. data/app/views/rails_nexus/logged_exceptions/clear.turbo_stream.erb +6 -0
  57. data/app/views/rails_nexus/logged_exceptions/destroy.turbo_stream.erb +2 -0
  58. data/app/views/rails_nexus/logged_exceptions/destroy_all.turbo_stream.erb +6 -0
  59. data/app/views/rails_nexus/logged_exceptions/feed.rss.builder +20 -0
  60. data/app/views/rails_nexus/logged_exceptions/index.html.erb +323 -0
  61. data/app/views/rails_nexus/logged_exceptions/query.html.erb +3 -0
  62. data/app/views/rails_nexus/logged_exceptions/query.turbo_stream.erb +6 -0
  63. data/app/views/rails_nexus/logged_exceptions/show.html.erb +3 -0
  64. data/app/views/rails_nexus/logged_exceptions/show.turbo_stream.erb +5 -0
  65. data/app/views/rails_nexus/n1_patterns/index.html.erb +153 -0
  66. data/app/views/rails_nexus/settings/index.html.erb +140 -0
  67. data/app/views/rails_nexus/stats/index.html.erb +569 -0
  68. data/config/importmap.rb +15 -0
  69. data/config/initializers/date_formats.rb +5 -0
  70. data/config/initializers/rails_nexus.rb +18 -0
  71. data/config/locales/en.yml +47 -0
  72. data/config/routes.rb +58 -0
  73. data/db/migrate/20240330122311_create_rails_nexus_logged_exceptions.rb +22 -0
  74. data/lib/generators/rails_nexus/backup_generator.rb +70 -0
  75. data/lib/generators/rails_nexus/customize_generator.rb +159 -0
  76. data/lib/generators/rails_nexus/install_generator.rb +61 -0
  77. data/lib/generators/rails_nexus/templates/INSTALL.md +9 -0
  78. data/lib/generators/rails_nexus/templates/backup/backup_helper.sh +152 -0
  79. data/lib/generators/rails_nexus/templates/backup/config.rb +27 -0
  80. data/lib/generators/rails_nexus/templates/backup/models/daily_backup.rb +24 -0
  81. data/lib/generators/rails_nexus/templates/backup/models/full_backup.rb +35 -0
  82. data/lib/generators/rails_nexus/templates/backup/models/sync_backup.rb +43 -0
  83. data/lib/generators/rails_nexus/templates/backup/mysql-config/db_config.cnf +15 -0
  84. data/lib/generators/rails_nexus/templates/backup/schedule.rb +28 -0
  85. data/lib/generators/rails_nexus/templates/javascript/controllers/rails_nexus_controller.js +310 -0
  86. data/lib/generators/rails_nexus/templates/javascript/controllers/rails_nexus_detail_controller.js +98 -0
  87. data/lib/generators/rails_nexus/templates/javascript/controllers/rails_nexus_sidebar_controller.js +15 -0
  88. data/lib/generators/rails_nexus/templates/javascript/controllers/rails_nexus_theme_controller.js +30 -0
  89. data/lib/generators/rails_nexus/templates/migration.rb +24 -0
  90. data/lib/generators/rails_nexus/templates/migration_advanced_features.rb +30 -0
  91. data/lib/generators/rails_nexus/templates/migration_cron_jobs.rb +22 -0
  92. data/lib/generators/rails_nexus/templates/migration_new_schema.rb +120 -0
  93. data/lib/generators/rails_nexus/templates/migration_platform_detection.rb +11 -0
  94. data/lib/generators/rails_nexus/templates/migration_webhook_deliveries.rb +21 -0
  95. data/lib/generators/rails_nexus/templates/migration_workflow.rb +29 -0
  96. data/lib/generators/rails_nexus/templates/rails_nexus.rb +96 -0
  97. data/lib/generators/rails_nexus/templates/views/layouts/rails_nexus/application.html.erb +129 -0
  98. data/lib/generators/rails_nexus/templates/views/rails_nexus/logged_exceptions/_exceptions.html.erb +123 -0
  99. data/lib/generators/rails_nexus/templates/views/rails_nexus/logged_exceptions/_show.html.erb +297 -0
  100. data/lib/generators/rails_nexus/templates/views/rails_nexus/logged_exceptions/index.html.erb +113 -0
  101. data/lib/rails_nexus/breadcrumbs.rb +161 -0
  102. data/lib/rails_nexus/cleanup.rb +32 -0
  103. data/lib/rails_nexus/configuration.rb +319 -0
  104. data/lib/rails_nexus/engine.rb +135 -0
  105. data/lib/rails_nexus/logger.rb +188 -0
  106. data/lib/rails_nexus/notifications.rb +105 -0
  107. data/lib/rails_nexus/ransack_config.rb +4 -0
  108. data/lib/rails_nexus/storm_protection.rb +124 -0
  109. data/lib/rails_nexus/version.rb +3 -0
  110. data/lib/rails_nexus.rb +133 -0
  111. data/lib/tasks/rails_nexus.rake +230 -0
  112. data/lib/tasks/rails_nexus_tasks.rake +37 -0
  113. data/rails_nexus.gemspec +36 -0
  114. 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