rails_nexus 2.1.2 → 2.1.4

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +41 -10
  4. data/app/controllers/rails_nexus/stats_controller.rb +0 -126
  5. data/app/views/layouts/rails_nexus/_sidebar_navigation.html.erb +0 -11
  6. data/app/views/layouts/rails_nexus/application.html.erb +1 -12
  7. data/app/views/rails_nexus/stats/index.html.erb +0 -101
  8. data/config/routes.rb +0 -11
  9. data/lib/generators/rails_nexus/templates/migration.rb +0 -82
  10. data/lib/rails_nexus/configuration.rb +0 -45
  11. data/lib/rails_nexus/engine.rb +6 -4
  12. data/lib/rails_nexus/version.rb +1 -1
  13. data/lib/tasks/rails_nexus.rake +0 -80
  14. data/rails_nexus.gemspec +1 -3
  15. metadata +6 -27
  16. data/app/controllers/rails_nexus/backup_controller.rb +0 -119
  17. data/app/jobs/rails_nexus/backup_job.rb +0 -18
  18. data/app/models/rails_nexus/backup.rb +0 -107
  19. data/app/models/rails_nexus/backup_config.rb +0 -189
  20. data/app/services/rails_nexus/backup_runner.rb +0 -62
  21. data/app/services/rails_nexus/backup_service.rb +0 -817
  22. data/app/views/rails_nexus/backup/_form.html.erb +0 -334
  23. data/app/views/rails_nexus/backup/config_history.html.erb +0 -67
  24. data/app/views/rails_nexus/backup/edit.html.erb +0 -15
  25. data/app/views/rails_nexus/backup/history.html.erb +0 -75
  26. data/app/views/rails_nexus/backup/index.html.erb +0 -112
  27. data/app/views/rails_nexus/backup/new.html.erb +0 -15
  28. data/db/seeds/backup_configs.rb +0 -140
  29. data/lib/generators/rails_nexus/backup_generator.rb +0 -70
  30. data/lib/generators/rails_nexus/templates/backup/backup_helper.sh +0 -152
  31. data/lib/generators/rails_nexus/templates/backup/config.rb +0 -27
  32. data/lib/generators/rails_nexus/templates/backup/models/daily_backup.rb +0 -24
  33. data/lib/generators/rails_nexus/templates/backup/models/full_backup.rb +0 -35
  34. data/lib/generators/rails_nexus/templates/backup/models/sync_backup.rb +0 -43
  35. data/lib/generators/rails_nexus/templates/backup/mysql-config/db_config.cnf +0 -15
  36. data/lib/generators/rails_nexus/templates/backup/schedule.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b0d4fb3a131d01ac778f166aff0bdda1c0247293e7f6ce7441b477fc9dbd47b8
4
- data.tar.gz: 073effb7c2dbd08a49ef1ea2d534c9ccf422d0f509c79138f15616e8510c2969
3
+ metadata.gz: 68f6936b492e804cacc457cb32a3c211f46a1200b57efe2c478fa3c6769fa7e2
4
+ data.tar.gz: 85fcf9d42acb0def8427800684cffffccfbe03b63791bbe4c3d1938c9cef33da
5
5
  SHA512:
6
- metadata.gz: 25ca7d4c31b7c5762aaf4c606f8f3c9b16a24ca2be69176cc8b1ad31578fcf697b71d5d2bdbcab2827e22b2f5103f9300be9c6417d73d86d28d5269fbc018e93
7
- data.tar.gz: 7e10dfb542e5f6a0a69d559c878bd3cbf024e409e7d703c2c5d5a24516ae37572a5c8b9795a14a41c41bef67109408b6688775300b7cd19df3a2528ac165aac5
6
+ metadata.gz: 0fe9502389240d9c4f486613ca0730da70fa0f4d932b0b86359f6d8587d5c456e16ac90f8caaeea9c9bb204e77a359c3fbf493ebeb5244119a1071d228ccac01
7
+ data.tar.gz: 7c6bab5a7baafe7dbc87fc89d2c6cf2de10137906cc910b4b2fcbb5f935f4ec89923ee6d97fa592ffc3ebbd6eca4e9e38a1e8ac07d1b96bcac1212f680a3f276
data/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ All notable changes to RailsNexus will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.1.4] - 2026-08-24
9
+
10
+ ### Changed
11
+ - **Backup extraction** — Backup management now lives in the standalone `backup_nexus` gem while RailsNexus retains the shared layout and stylesheet.
12
+ - **Release automation** — GitHub Actions builds, validates, and publishes the gem non-interactively with a scoped RubyGems API key.
13
+
14
+ ### Security
15
+ - **Safe backup examples** — Removed the credential-bearing backup seed and replaced it with environment-based configuration owned by BackupNexus.
16
+
8
17
  ## [2.1.1] - 2026-08-23
9
18
 
10
19
  ### Added
data/README.md CHANGED
@@ -33,13 +33,6 @@ RailsNexus is an extensible operations and administration console for Rails appl
33
33
  - **Execution Times** — Monitor job duration and detect slow jobs
34
34
  - **Cleanup** — Automatic old job record removal
35
35
 
36
- ### 💾 Backup Management
37
- - **Backup Dashboard** — Health status, model listing, recent backups
38
- - **File Browser** — View all backup files with size, age, and format
39
- - **Trigger from UI** — Run backups directly from the dashboard
40
- - **Settings Editor** — Configure paths, thresholds, and notifications
41
- - **Health Monitoring** — Alerts when backups are stale or missing
42
- - **Cron Schedule** — View and manage backup schedules
43
36
 
44
37
  ### 🖥️ Server Statistics
45
38
  - **Memory Usage** — RAM and swap monitoring
@@ -112,6 +105,16 @@ This will:
112
105
 
113
106
  The dashboard is now available at `/rails_nexus`.
114
107
 
108
+ Backup management is provided separately by
109
+ [backup_nexus](https://github.com/tamiru/backup_nexus). Add it when your
110
+ application needs the backup dashboard and runner:
111
+
112
+ ```ruby
113
+ gem "backup_nexus"
114
+ ```
115
+
116
+ Then run bin/rails generate backup_nexus:install.
117
+
115
118
  ### Optional UI customization
116
119
 
117
120
  The mounted dashboard already includes its CSS, Turbo, and Stimulus controllers.
@@ -405,9 +408,6 @@ RailsNexus applies Rails' configured parameter filters plus built-in password, t
405
408
 
406
409
  Analytics time grouping works on SQLite, PostgreSQL, and MySQL/MariaDB. Row counts work on all three; physical table-size and server uptime/connection metrics are displayed only where the adapter exposes them safely, and otherwise show as unavailable.
407
410
 
408
- ## Backup safety and compatibility
409
-
410
- Backup commands are executed without a shell and validate database identifiers, ports, hosts, paths, remote destinations, and MySQL options. Database and encryption passwords are passed through protected environment variables or mode-`0600` temporary files rather than command-line arguments. Free-form notification shell commands are intentionally unsupported; configure a direct executable and arguments instead.
411
411
 
412
412
  ## Security
413
413
 
@@ -441,6 +441,37 @@ gem build rails_nexus.gemspec
441
441
 
442
442
  The repository includes a Rails dummy application under `test/dummy` for engine integration testing.
443
443
 
444
+ ## CI/CD
445
+
446
+ GitHub Actions runs the test matrix, database compatibility checks, asset builds,
447
+ and security checks on pushes and pull requests. Releases are published to
448
+ RubyGems when a version tag is pushed.
449
+
450
+ Before the first release, set the RubyGems account's MFA level to **UI and gem
451
+ signin**, then create an API key with only the **Push rubygems** scope, limited
452
+ to the `rails_nexus` gem. Leave MFA disabled for this CI key so RubyGems does
453
+ not request a one-time password during automated releases. Add the key to the
454
+ GitHub repository (or its `release` environment) as an Actions secret named
455
+ `RUBYGEMS_API_KEY`. Never commit the key to the repository.
456
+
457
+ The publish command has no interactive input. A missing key or an account/key
458
+ that requires API MFA therefore fails the release instead of waiting for an
459
+ OTP prompt.
460
+
461
+ Create the `release` environment in GitHub and add required reviewers if you
462
+ want a manual approval before publishing.
463
+
464
+ To release a version, update `lib/rails_nexus/version.rb` and `CHANGELOG.md`,
465
+ merge the changes to `main`, then push a matching tag:
466
+
467
+ ```bash
468
+ git tag v2.1.4
469
+ git push origin v2.1.4
470
+ ```
471
+
472
+ The workflow verifies that the tag matches the gem version, runs the tests,
473
+ builds and validates the gem, then publishes it and creates the GitHub release.
474
+
444
475
  ## Support
445
476
 
446
477
  If you find RailsNexus useful, consider supporting the project:
@@ -11,7 +11,6 @@ module RailsNexus
11
11
  @exception_stats = collect_exception_stats
12
12
  @platform_stats = collect_platform_stats
13
13
  @database_stats = collect_database_stats
14
- @backup_stats = collect_backup_stats
15
14
  @storm_stats = collect_storm_stats if defined?(RailsNexus::StormProtection)
16
15
  end
17
16
 
@@ -243,131 +242,6 @@ module RailsNexus
243
242
  { error: e.message }
244
243
  end
245
244
 
246
- def collect_backup_stats
247
- stats = { detected: false, gem: nil, status: "unknown", last_backup: nil, details: {} }
248
-
249
- # Check for backup gem
250
- if defined?(Backup)
251
- stats[:detected] = true
252
- stats[:gem] = "backup"
253
- stats[:version] = Backup::VERSION if Backup.const_defined?(:VERSION)
254
-
255
- begin
256
- # Check for backup configuration
257
- config_path = Rails.root.join("config", "backup")
258
- if config_path.exist?
259
- stats[:details][:config_path] = config_path.to_s
260
- stats[:status] = "configured"
261
- end
262
-
263
- # Check for backup logs
264
- log_path = Rails.root.join("log", "backup")
265
- if log_path.exist?
266
- log_files = log_path.glob("*.log").sort_by(&:mtime).reverse
267
- if log_files.any?
268
- last_log = log_files.first
269
- stats[:details][:log_file] = last_log.to_s
270
- stats[:details][:log_size] = (last_log.size / 1024.0).round(1)
271
-
272
- # Parse last backup time from log
273
- log_content = last_log.read(1000) # Read first 1KB
274
- if log_content.match(/started at (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/)
275
- stats[:last_backup] = Time.parse($1)
276
- end
277
- end
278
- end
279
-
280
- # Check for backup models
281
- models = Backup::Model.all rescue []
282
- if models.any?
283
- stats[:details][:models] = models.map do |m|
284
- {
285
- name: m.name,
286
- schedule: m.schedule&.to_s,
287
- triggers: m.triggers.map(&:name)
288
- }
289
- end
290
- stats[:status] = "configured"
291
- end
292
- rescue StandardError => e
293
- stats[:details][:error] = e.message
294
- end
295
- end
296
-
297
- # Check for pg_dump (PostgreSQL)
298
- if (version = executable_version("pg_dump"))
299
- stats[:detected] = true
300
- stats[:gem] ||= "pg_dump"
301
- stats[:details][:pg_dump] = version
302
- end
303
-
304
- # Check for mysqldump (MySQL)
305
- if (version = executable_version("mysqldump"))
306
- stats[:detected] = true
307
- stats[:gem] ||= "mysqldump"
308
- stats[:details][:mysqldump] = version
309
- end
310
-
311
- # Check for AWS CLI (RDS backups)
312
- if (version = executable_version("aws"))
313
- stats[:detected] = true
314
- stats[:gem] ||= "aws-cli"
315
- stats[:details][:aws_cli] = version
316
-
317
- # Check for RDS snapshots if configured
318
- begin
319
- rds_client = Aws::RDS::Client.new
320
- snapshots = rds_client.describe_db_snapshots(db_snapshot_identifier: "automated-*")
321
- stats[:details][:rds_snapshots] = snapshots.db_snapshots.count
322
- stats[:status] = "configured" if snapshots.db_snapshots.any?
323
- rescue StandardError
324
- # AWS not configured or no access
325
- end
326
- end
327
-
328
- # Check for docker backup solutions
329
- if File.exist?("/var/lib/docker") && (version = executable_version("docker"))
330
- stats[:detected] = true
331
- stats[:gem] ||= "docker"
332
- stats[:details][:docker] = version
333
- end
334
-
335
- # Check for crontab entries related to backups
336
- begin
337
- crontab, _stderr, status = Open3.capture3("crontab", "-l")
338
- if status.success? && (crontab.include?("backup") || crontab.include?("dump"))
339
- stats[:details][:crontab_backup] = true
340
- stats[:status] = "scheduled" if stats[:status] == "unknown"
341
- end
342
- rescue StandardError
343
- # Cannot read crontab
344
- end
345
-
346
- # Check for backup-related environment variables
347
- backup_env_vars = %w[BACKUP_DIR BACKUP_S3_BUCKET BACKUP_GCS_BUCKET DATABASE_BACKUP_URL]
348
- found_env = backup_env_vars.select { |var| ENV[var] }
349
- if found_env.any?
350
- stats[:detected] = true
351
- stats[:details][:env_vars] = found_env
352
- end
353
-
354
- # Determine overall status
355
- if stats[:last_backup]
356
- hours_since = (Time.now - stats[:last_backup]) / 3600
357
- if hours_since < 24
358
- stats[:status] = "healthy"
359
- elsif hours_since < 168 # 7 days
360
- stats[:status] = "warning"
361
- else
362
- stats[:status] = "stale"
363
- end
364
- elsif stats[:status] == "unknown"
365
- stats[:status] = "not_configured"
366
- end
367
-
368
- stats
369
- end
370
-
371
245
  def collect_table_stats
372
246
  tables = []
373
247
 
@@ -60,17 +60,6 @@
60
60
  Cron Jobs
61
61
  <% end %>
62
62
 
63
- <%= link_to backup_path,
64
- class: "rn-nav-item #{controller_path == 'rails_nexus/backup' && !%w[all_history history].include?(action_name) ? 'active' : ''}" do %>
65
- <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" /></svg>
66
- Backups
67
- <% end %>
68
-
69
- <%= link_to backup_history_path,
70
- class: "rn-nav-item #{controller_path == 'rails_nexus/backup' && %w[all_history history].include?(action_name) ? 'active' : ''}" do %>
71
- <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
72
- Backup History
73
- <% end %>
74
63
 
75
64
  <%= link_to settings_path,
76
65
  class: "rn-nav-item #{request.path == rails_nexus.settings_path ? 'active' : ''}" do %>
@@ -24,7 +24,7 @@
24
24
  rails_nexus/analytics rails_nexus/stats
25
25
  ].include?(controller_path)
26
26
  operations_menu_active = %w[
27
- rails_nexus/cron_jobs rails_nexus/backup rails_nexus/settings
27
+ rails_nexus/cron_jobs rails_nexus/settings backup_nexus/backup
28
28
  ].include?(controller_path)
29
29
  resources_menu_active = RailsNexus.configuration.sidebar_links.any? do |link|
30
30
  link[:url].to_s == request.path
@@ -109,17 +109,6 @@
109
109
  Cron Jobs
110
110
  <% end %>
111
111
 
112
- <%= link_to backup_path,
113
- class: "rn-nav-item #{controller_path == 'rails_nexus/backup' && !%w[all_history history].include?(action_name) ? 'active' : ''}" do %>
114
- <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" /></svg>
115
- Backups
116
- <% end %>
117
-
118
- <%= link_to backup_history_path,
119
- class: "rn-nav-item #{controller_path == 'rails_nexus/backup' && %w[all_history history].include?(action_name) ? 'active' : ''}" do %>
120
- <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
121
- Backup History
122
- <% end %>
123
112
 
124
113
  <%= link_to settings_path,
125
114
  class: "rn-nav-item #{request.path == rails_nexus.settings_path ? 'active' : ''}" do %>
@@ -367,107 +367,6 @@
367
367
  </div>
368
368
  <% end %>
369
369
 
370
- <%# ─── Backup Health ──────────────────────────────────────── %>
371
- <% if @backup_stats && @backup_stats[:detected] %>
372
- <div class="rn-card">
373
- <div class="p-4" style="border-bottom: 1px solid var(--rn-border-light)">
374
- <div class="flex items-center gap-2">
375
- <div class="w-8 h-8 rounded-lg flex items-center justify-center" style="background: var(--rn-info-light)">
376
- <svg class="w-4 h-4" style="color: var(--rn-info)" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
377
- <path stroke-linecap="round" stroke-linejoin="round" d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
378
- </svg>
379
- </div>
380
- <div>
381
- <h2 class="text-sm font-semibold" style="color: var(--rn-text)">Backup Status</h2>
382
- <p class="text-xs" style="color: var(--rn-text-muted)"><%= @backup_stats[:gem] %> <%= @backup_stats[:version] ? "v#{@backup_stats[:version]}" : '' %></p>
383
- </div>
384
- </div>
385
- </div>
386
- <div class="p-4">
387
- <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
388
- <div>
389
- <div class="text-xs" style="color: var(--rn-text-muted)">Status</div>
390
- <div class="flex items-center gap-2 mt-1">
391
- <span class="w-2 h-2 rounded-full" style="background: <%= case @backup_stats[:status]
392
- when 'healthy' then 'var(--rn-success)'
393
- when 'warning' then 'var(--rn-warning)'
394
- when 'stale' then 'var(--rn-danger)'
395
- when 'configured' then 'var(--rn-info)'
396
- else 'var(--rn-text-muted)'
397
- end %>"></span>
398
- <span class="text-sm font-medium" style="color: var(--rn-text)"><%= @backup_stats[:status].capitalize %></span>
399
- </div>
400
- </div>
401
- <div>
402
- <div class="text-xs" style="color: var(--rn-text-muted)">Last Backup</div>
403
- <div class="text-sm font-medium mt-1" style="color: var(--rn-text)">
404
- <% if @backup_stats[:last_backup] %>
405
- <%= @backup_stats[:last_backup].strftime("%b %d, %Y %I:%M %p") %>
406
- <% else %>
407
- <span style="color: var(--rn-text-muted)">Unknown</span>
408
- <% end %>
409
- </div>
410
- </div>
411
- <div>
412
- <div class="text-xs" style="color: var(--rn-text-muted)">Tool</div>
413
- <div class="text-sm font-medium mt-1" style="color: var(--rn-text)"><%= @backup_stats[:gem] %></div>
414
- </div>
415
- <div>
416
- <div class="text-xs" style="color: var(--rn-text-muted)">Config</div>
417
- <div class="text-sm font-medium mt-1" style="color: var(--rn-text)">
418
- <%= @backup_stats[:status] == 'configured' || @backup_stats[:status] == 'scheduled' ? 'Yes' : 'No' %>
419
- </div>
420
- </div>
421
- </div>
422
-
423
- <%# Backup Details %>
424
- <% if @backup_stats[:details].any? %>
425
- <div class="border-t pt-4" style="border-color: var(--rn-border-light)">
426
- <h3 class="text-xs font-semibold uppercase tracking-wider mb-3" style="color: var(--rn-text-muted)">Details</h3>
427
- <div class="space-y-2">
428
- <% @backup_stats[:details].each do |key, value| %>
429
- <% next if value.is_a?(Hash) && value.empty? %>
430
- <div class="flex items-start gap-3 text-sm">
431
- <span class="rn-mono font-medium" style="color: var(--rn-text-secondary); min-width: 120px"><%= key.to_s.humanize %>:</span>
432
- <% if value.is_a?(Array) %>
433
- <div class="flex-1">
434
- <% value.each do |item| %>
435
- <div class="text-xs" style="color: var(--rn-text-muted)">
436
- <% if item.is_a?(Hash) %>
437
- <%= item[:name] %> <%= item[:schedule] ? "(#{item[:schedule]})" : '' %>
438
- <% else %>
439
- <%= item %>
440
- <% end %>
441
- </div>
442
- <% end %>
443
- </div>
444
- <% elsif value.is_a?(Hash) %>
445
- <div class="flex-1">
446
- <% value.each do |k, v| %>
447
- <div class="text-xs" style="color: var(--rn-text-muted)"><%= k %>: <%= v %></div>
448
- <% end %>
449
- </div>
450
- <% else %>
451
- <span class="flex-1 text-xs" style="color: var(--rn-text-muted)"><%= value %></span>
452
- <% end %>
453
- </div>
454
- <% end %>
455
- </div>
456
- </div>
457
- <% end %>
458
- </div>
459
- </div>
460
- <% elsif @backup_stats && !@backup_stats[:detected] %>
461
- <div class="rn-card">
462
- <div class="p-4" style="border-bottom: 1px solid var(--rn-border-light)">
463
- <h2 class="text-sm font-semibold" style="color: var(--rn-text)">Backup Status</h2>
464
- </div>
465
- <div class="p-4">
466
- <p class="text-sm" style="color: var(--rn-text-muted)">No backup solution detected. Consider adding a backup gem like <code class="rn-mono text-xs" style="background: var(--rn-bg-secondary); padding: 2px 6px; border-radius: 4px">backup</code> or configuring <code class="rn-mono text-xs" style="background: var(--rn-bg-secondary); padding: 2px 6px; border-radius: 4px">pg_dump</code>/<code class="rn-mono text-xs" style="background: var(--rn-bg-secondary); padding: 2px 6px; border-radius: 4px">mysqldump</code>.</p>
467
- </div>
468
- </div>
469
- <% end %>
470
-
471
370
  <%# ─── Platform Breakdown ──────────────────────────────────── %>
472
371
  <% if @platform_stats && @platform_stats[:breakdown]&.any? %>
473
372
  <div class="rn-card">
data/config/routes.rb CHANGED
@@ -41,17 +41,6 @@ RailsNexus::Engine.routes.draw do
41
41
  # N+1 query patterns
42
42
  get "n1_patterns", to: "n1_patterns#index", as: :n1_patterns
43
43
 
44
- # Backup management
45
- get "backup", to: "backup#index", as: :backup
46
- get "backup/new", to: "backup#new", as: :new_backup
47
- post "backup", to: "backup#create"
48
- get "backup/history", to: "backup#all_history", as: :backup_history
49
- get "backup/:id/edit", to: "backup#edit", as: :edit_backup
50
- patch "backup/:id", to: "backup#update"
51
- delete "backup/:id", to: "backup#destroy", as: :backup_destroy
52
- post "backup/:id/trigger", to: "backup#trigger", as: :backup_trigger
53
- get "backup/:id/history", to: "backup#history", as: :backup_config_history
54
-
55
44
  # Settings and webhook management
56
45
  get "settings", to: "settings#index", as: :settings
57
46
  post "settings/test_webhook", to: "settings#test_webhook", as: :test_webhook
@@ -111,87 +111,5 @@ class CreateRailsNexusTables < ActiveRecord::Migration[<%= "[#{ActiveRecord::Mig
111
111
  add_index :rails_nexus_webhook_deliveries, :status
112
112
  add_index :rails_nexus_webhook_deliveries, :created_at
113
113
  add_index :rails_nexus_webhook_deliveries, :url
114
-
115
- # ─── rails_nexus_backups ─────────────────────────────────────────
116
- create_table :rails_nexus_backups do |t|
117
- t.string :config_name, null: false
118
- t.string :status, null: false
119
- t.text :file_path
120
- t.bigint :file_size
121
- t.float :duration
122
- t.text :error_message
123
- t.string :triggered_by
124
- t.datetime :started_at, null: false
125
- t.datetime :completed_at
126
- end
127
-
128
- add_index :rails_nexus_backups, [:config_name, :started_at]
129
- add_index :rails_nexus_backups, :status
130
- add_index :rails_nexus_backups, :started_at
131
-
132
- # ─── rails_nexus_backup_configs ────────────────────────────────
133
- create_table :rails_nexus_backup_configs do |t|
134
- t.string :name, null: false
135
- t.string :description
136
- t.string :database_name, null: false
137
- t.string :adapter, null: false, default: "mysql"
138
- t.string :host, default: "localhost"
139
- t.integer :port
140
- t.string :username
141
- t.string :password
142
- t.string :skip_tables
143
-
144
- t.string :storage_path, null: false
145
- t.integer :keep_count, default: 30
146
-
147
- t.boolean :compress, default: true
148
- t.boolean :bzip2_compress, default: false
149
- t.boolean :encrypted, default: false
150
- t.string :encryption_password
151
- t.boolean :encrypt_base64, default: false
152
- t.boolean :gpg_enabled, default: false
153
- t.string :gpg_password
154
-
155
- t.boolean :rsync_enabled, default: false
156
- t.string :rsync_host
157
- t.integer :rsync_port, default: 22
158
- t.string :rsync_user
159
- t.string :rsync_path
160
- t.boolean :rsync_mirror, default: false
161
- t.boolean :rsync_archive, default: true
162
- t.string :rsync_directories
163
- t.string :rsync_excludes
164
-
165
- t.string :mysql_additional_options
166
-
167
- # S3 storage
168
- t.boolean :s3_enabled, default: false
169
- t.string :s3_access_key
170
- t.string :s3_secret_key
171
- t.string :s3_bucket
172
- t.string :s3_region, default: "us-east-1"
173
- t.string :s3_prefix
174
-
175
- # Archives (file/dir backup)
176
- t.boolean :archive_enabled, default: false
177
- t.string :archive_paths
178
- t.string :archive_excludes
179
- t.boolean :split_chunks, default: false
180
-
181
- # Notifications
182
- t.string :notify_command
183
- t.boolean :notify_on_success, default: true
184
- t.boolean :notify_on_failure, default: true
185
- t.boolean :email_notify, default: false
186
- t.string :email_to
187
-
188
- t.string :schedule_cron
189
- t.boolean :enabled, default: true
190
-
191
- t.timestamps
192
- end
193
-
194
- add_index :rails_nexus_backup_configs, :name, unique: true
195
- add_index :rails_nexus_backup_configs, :enabled
196
114
  end
197
115
  end
@@ -183,15 +183,6 @@ module RailsNexus
183
183
 
184
184
  # Source Code Viewing
185
185
  attr_accessor :source_code_viewing
186
-
187
- # Backup Management
188
- attr_accessor :backup_enabled
189
- attr_accessor :backup_config_path
190
- attr_accessor :backup_models_path
191
- attr_accessor :backup_dump_path
192
- attr_accessor :backup_notify_command
193
- attr_accessor :backup_alert_threshold_hours
194
-
195
186
  # ─── Database ───────────────────────────────────────────────
196
187
  # Use a separate database for RailsNexus tables.
197
188
  # Options:
@@ -282,15 +273,6 @@ module RailsNexus
282
273
 
283
274
  # Source Code Viewing
284
275
  @source_code_viewing = true
285
-
286
- # Backup Management
287
- @backup_enabled = false
288
- @backup_config_path = default_backup_config_path
289
- @backup_models_path = default_backup_models_path
290
- @backup_dump_path = default_backup_dump_path
291
- @backup_notify_command = nil
292
- @backup_alert_threshold_hours = 24
293
-
294
276
  # Database
295
277
  @database_url = nil
296
278
  @database_name = nil
@@ -298,32 +280,5 @@ module RailsNexus
298
280
  @table_prefix = "rails_nexus_"
299
281
  end
300
282
 
301
- private
302
-
303
- def default_backup_config_path
304
- if defined?(Rails) && Rails.respond_to?(:root)
305
- path = Rails.root.join("config/backup/config.rb")
306
- path.exist? ? path.to_s : nil
307
- else
308
- nil
309
- end
310
- end
311
-
312
- def default_backup_models_path
313
- if defined?(Rails) && Rails.respond_to?(:root)
314
- path = Rails.root.join("config/backup/models")
315
- path.exist? ? path.to_s : nil
316
- else
317
- nil
318
- end
319
- end
320
-
321
- def default_backup_dump_path
322
- if defined?(Rails) && Rails.respond_to?(:root)
323
- Rails.root.join("storage/rails_nexus/backups").to_s
324
- else
325
- "storage/rails_nexus/backups"
326
- end
327
- end
328
283
  end
329
284
  end
@@ -92,10 +92,12 @@ module RailsNexus
92
92
 
93
93
  initializer "rails_nexus.after_create" do
94
94
  ActiveSupport.on_load(:active_record) do
95
- RailsNexus::LoggedException.after_create do |exception|
96
- RailsNexus::Notifications.notify(exception)
97
- rescue StandardError => e
98
- Rails.logger.error("[RailsNexus] Notification error: #{e.message}")
95
+ if defined?(RailsNexus::LoggedException)
96
+ RailsNexus::LoggedException.after_create do |exception|
97
+ RailsNexus::Notifications.notify(exception)
98
+ rescue StandardError => e
99
+ Rails.logger.error("[RailsNexus] Notification error: #{e.message}")
100
+ end
99
101
  end
100
102
  end
101
103
  end
@@ -1,3 +1,3 @@
1
1
  module RailsNexus
2
- VERSION = "2.1.2"
2
+ VERSION = "2.1.4"
3
3
  end