rails_nexus 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/rails_nexus/backup_controller.rb +78 -24
- data/app/models/rails_nexus/backup_config.rb +90 -0
- data/app/services/rails_nexus/backup_runner.rb +62 -0
- data/app/services/rails_nexus/backup_service.rb +231 -131
- data/app/views/rails_nexus/backup/_form.html.erb +185 -0
- data/app/views/rails_nexus/backup/config_history.html.erb +67 -0
- data/app/views/rails_nexus/backup/edit.html.erb +15 -0
- data/app/views/rails_nexus/backup/{files.html.erb → history.html.erb} +8 -15
- data/app/views/rails_nexus/backup/index.html.erb +99 -79
- data/app/views/rails_nexus/backup/new.html.erb +15 -0
- data/config/routes.rb +8 -4
- data/lib/generators/rails_nexus/templates/rails_nexus.rb +1 -1
- data/lib/rails_nexus/version.rb +1 -1
- data/lib/tasks/rails_nexus.rake +66 -3
- metadata +8 -3
- data/config/initializers/rails_nexus.rb +0 -18
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<% page_title "Edit #{@config.name}" %>
|
|
2
|
+
|
|
3
|
+
<div class="space-y-6">
|
|
4
|
+
<nav class="rn-breadcrumbs" aria-label="Breadcrumb">
|
|
5
|
+
<a href="<%= root_path %>" class="rn-breadcrumb">Dashboard</a>
|
|
6
|
+
<span class="rn-breadcrumb-sep">/</span>
|
|
7
|
+
<a href="<%= backup_path %>" class="rn-breadcrumb">Backups</a>
|
|
8
|
+
<span class="rn-breadcrumb-sep">/</span>
|
|
9
|
+
<span class="rn-breadcrumb-current">Edit <%= @config.name %></span>
|
|
10
|
+
</nav>
|
|
11
|
+
|
|
12
|
+
<h1 class="text-2xl font-bold" style="color: var(--rn-text)">Edit: <%= @config.name %></h1>
|
|
13
|
+
|
|
14
|
+
<%= render "form" %>
|
|
15
|
+
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<% page_title
|
|
1
|
+
<% page_title "Backup History" %>
|
|
2
2
|
|
|
3
3
|
<div class="space-y-6">
|
|
4
4
|
<nav class="rn-breadcrumbs" aria-label="Breadcrumb">
|
|
@@ -6,44 +6,37 @@
|
|
|
6
6
|
<span class="rn-breadcrumb-sep">/</span>
|
|
7
7
|
<a href="<%= backup_path %>" class="rn-breadcrumb">Backups</a>
|
|
8
8
|
<span class="rn-breadcrumb-sep">/</span>
|
|
9
|
-
<span class="rn-breadcrumb-current">
|
|
9
|
+
<span class="rn-breadcrumb-current">History</span>
|
|
10
10
|
</nav>
|
|
11
11
|
|
|
12
|
-
<
|
|
13
|
-
<div class="p-4" style="border-bottom: 1px solid var(--rn-border-light)">
|
|
14
|
-
<h2 class="text-sm font-semibold" style="color: var(--rn-text)">All Backup Records</h2>
|
|
15
|
-
<p class="text-xs mt-1" style="color: var(--rn-text-muted)">Complete history of backup runs</p>
|
|
16
|
-
</div>
|
|
12
|
+
<h1 class="text-2xl font-bold" style="color: var(--rn-text)">Backup History</h1>
|
|
17
13
|
|
|
14
|
+
<div class="rn-card">
|
|
18
15
|
<% if @records.any? %>
|
|
19
16
|
<div class="rn-table-wrap">
|
|
20
17
|
<table class="rn-table">
|
|
21
18
|
<thead>
|
|
22
19
|
<tr>
|
|
23
|
-
<th>
|
|
24
|
-
<th>Model</th>
|
|
20
|
+
<th>Config</th>
|
|
25
21
|
<th>Status</th>
|
|
26
22
|
<th>File</th>
|
|
27
23
|
<th>Size</th>
|
|
28
24
|
<th>Duration</th>
|
|
29
25
|
<th>Triggered By</th>
|
|
30
|
-
<th>
|
|
31
|
-
<th
|
|
26
|
+
<th>Time</th>
|
|
27
|
+
<th>Error</th>
|
|
32
28
|
</tr>
|
|
33
29
|
</thead>
|
|
34
30
|
<tbody>
|
|
35
31
|
<% @records.each do |record| %>
|
|
36
32
|
<tr>
|
|
37
|
-
<td><span class="text-xs rn-mono" style="color: var(--rn-text-muted)">#<%= record.id %></span></td>
|
|
38
33
|
<td><span class="font-medium" style="color: var(--rn-text)"><%= record.model_name %></span></td>
|
|
39
34
|
<td>
|
|
40
35
|
<span class="rn-badge rn-badge-<%= record.status == 'success' ? 'success' : record.status == 'failed' ? 'danger' : 'warning' %>">
|
|
41
36
|
<%= record.status.capitalize %>
|
|
42
37
|
</span>
|
|
43
38
|
</td>
|
|
44
|
-
<td>
|
|
45
|
-
<span class="text-xs rn-mono" style="color: var(--rn-text-secondary)"><%= record.file_path ? File.basename(record.file_path) : "—" %></span>
|
|
46
|
-
</td>
|
|
39
|
+
<td><span class="text-xs rn-mono" style="color: var(--rn-text-secondary)"><%= record.file_path ? File.basename(record.file_path) : "—" %></span></td>
|
|
47
40
|
<td><span class="text-xs" style="color: var(--rn-text-secondary)"><%= record.file_size ? number_to_human_size(record.file_size) : "—" %></span></td>
|
|
48
41
|
<td><span class="text-xs" style="color: var(--rn-text-secondary)"><%= record.duration ? "#{record.duration.round(1)}s" : "—" %></span></td>
|
|
49
42
|
<td><span class="text-xs" style="color: var(--rn-text-muted)"><%= record.triggered_by || "—" %></span></td>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
<% page_title
|
|
1
|
+
<% page_title "Backup Management" %>
|
|
2
2
|
|
|
3
3
|
<div class="space-y-6">
|
|
4
|
-
<%# ─── Header ──────────────────────────────────────────────── %>
|
|
5
4
|
<nav class="rn-breadcrumbs" aria-label="Breadcrumb">
|
|
6
5
|
<a href="<%= root_path %>" class="rn-breadcrumb">Dashboard</a>
|
|
7
6
|
<span class="rn-breadcrumb-sep">/</span>
|
|
@@ -9,117 +8,95 @@
|
|
|
9
8
|
</nav>
|
|
10
9
|
|
|
11
10
|
<div class="flex items-center justify-between">
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
<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" />
|
|
15
|
-
</svg>
|
|
16
|
-
<h1 class="text-2xl font-bold" style="color: var(--rn-text)">Backup Management</h1>
|
|
17
|
-
</div>
|
|
18
|
-
</div>
|
|
19
|
-
|
|
20
|
-
<%# ─── Health Status ───────────────────────────────────────── %>
|
|
21
|
-
<div class="rn-card" style="border-left: 3px solid <%= @health[:status] == 'healthy' ? 'var(--rn-success)' : @health[:status] == 'warning' ? 'var(--rn-warning)' : 'var(--rn-danger)' %>">
|
|
22
|
-
<div class="p-4 flex items-center gap-3">
|
|
23
|
-
<span class="w-3 h-3 rounded-full" style="background: <%= @health[:status] == 'healthy' ? 'var(--rn-success)' : @health[:status] == 'warning' ? 'var(--rn-warning)' : 'var(--rn-danger)' %>"></span>
|
|
24
|
-
<div>
|
|
25
|
-
<span class="text-sm font-semibold" style="color: var(--rn-text)"><%= @health[:message] %></span>
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
11
|
+
<h1 class="text-2xl font-bold" style="color: var(--rn-text)">Backup Management</h1>
|
|
12
|
+
<%= link_to "New Config", new_backup_path, class: "rn-btn rn-btn-primary" %>
|
|
28
13
|
</div>
|
|
29
14
|
|
|
30
|
-
<%# ───
|
|
15
|
+
<%# ─── Stats ──────────────────────────────────────────────── %>
|
|
31
16
|
<div class="rn-stats">
|
|
17
|
+
<div class="rn-stat">
|
|
18
|
+
<span class="rn-stat-label">Configs</span>
|
|
19
|
+
<span class="rn-stat-value"><%= @stats[:total_configs] %></span>
|
|
20
|
+
<span class="rn-stat-change"><%= @stats[:enabled_configs] %> enabled</span>
|
|
21
|
+
</div>
|
|
32
22
|
<div class="rn-stat">
|
|
33
23
|
<span class="rn-stat-label">Total Backups</span>
|
|
34
|
-
<span class="rn-stat-value"><%= @
|
|
35
|
-
<span class="rn-stat-change"
|
|
24
|
+
<span class="rn-stat-value"><%= @stats[:total_backups] %></span>
|
|
25
|
+
<span class="rn-stat-change">All time</span>
|
|
36
26
|
</div>
|
|
37
27
|
<div class="rn-stat">
|
|
38
28
|
<span class="rn-stat-label">Successful</span>
|
|
39
|
-
<span class="rn-stat-value" style="color: var(--rn-success)"><%= @
|
|
29
|
+
<span class="rn-stat-value" style="color: var(--rn-success)"><%= @stats[:successful] %></span>
|
|
40
30
|
<span class="rn-stat-change">All time</span>
|
|
41
31
|
</div>
|
|
42
32
|
<div class="rn-stat">
|
|
43
33
|
<span class="rn-stat-label">Failed</span>
|
|
44
|
-
<span class="rn-stat-value" style="color: var(--rn-danger)"><%= @
|
|
34
|
+
<span class="rn-stat-value" style="color: var(--rn-danger)"><%= @stats[:failed] %></span>
|
|
45
35
|
<span class="rn-stat-change">All time</span>
|
|
46
36
|
</div>
|
|
47
|
-
<div class="rn-stat">
|
|
48
|
-
<span class="rn-stat-label">Last 7 Days</span>
|
|
49
|
-
<span class="rn-stat-value"><%= @summary[:recent_count] %></span>
|
|
50
|
-
<span class="rn-stat-change"><%= @summary[:recent_success] %> ok, <%= @summary[:recent_failed] %> failed</span>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
<%# ─── Trigger Backup ──────────────────────────────────────── %>
|
|
55
|
-
<div class="rn-card">
|
|
56
|
-
<div class="p-4" style="border-bottom: 1px solid var(--rn-border-light)">
|
|
57
|
-
<h2 class="text-sm font-semibold" style="color: var(--rn-text)">Run Backup</h2>
|
|
58
|
-
<p class="text-xs mt-1" style="color: var(--rn-text-muted)">Trigger a database dump. The record will be saved to the table.</p>
|
|
59
|
-
</div>
|
|
60
|
-
<div class="p-4 flex gap-3">
|
|
61
|
-
<% %w[primary full sync].each do |model| %>
|
|
62
|
-
<%= button_to model.titleize, backup_trigger_path(model: model),
|
|
63
|
-
method: :post,
|
|
64
|
-
class: "rn-btn rn-btn-primary rn-btn-sm",
|
|
65
|
-
data: { turbo_confirm: "Run #{model} backup now?" } %>
|
|
66
|
-
<% end %>
|
|
67
|
-
</div>
|
|
68
37
|
</div>
|
|
69
38
|
|
|
70
|
-
<%# ───
|
|
39
|
+
<%# ─── Backup Configs ─────────────────────────────────────── %>
|
|
71
40
|
<div class="rn-card">
|
|
72
41
|
<div class="p-4" style="border-bottom: 1px solid var(--rn-border-light)">
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
76
|
-
</svg>
|
|
77
|
-
<h2 class="text-sm font-semibold" style="color: var(--rn-text)">Backup History</h2>
|
|
78
|
-
</div>
|
|
79
|
-
<p class="text-xs mt-1" style="color: var(--rn-text-muted)">All backup runs recorded in the database</p>
|
|
42
|
+
<h2 class="text-sm font-semibold" style="color: var(--rn-text)">Backup Configurations</h2>
|
|
43
|
+
<p class="text-xs mt-1" style="color: var(--rn-text-muted)">Configure and run database backups</p>
|
|
80
44
|
</div>
|
|
81
45
|
|
|
82
|
-
<% if @
|
|
46
|
+
<% if @configs.any? %>
|
|
83
47
|
<div class="rn-table-wrap">
|
|
84
48
|
<table class="rn-table">
|
|
85
49
|
<thead>
|
|
86
50
|
<tr>
|
|
87
|
-
<th>
|
|
51
|
+
<th>Name</th>
|
|
52
|
+
<th>Adapter</th>
|
|
53
|
+
<th>Database</th>
|
|
54
|
+
<th>Storage</th>
|
|
55
|
+
<th>Schedule</th>
|
|
88
56
|
<th>Status</th>
|
|
89
|
-
<th>
|
|
90
|
-
<th>Duration</th>
|
|
91
|
-
<th>Triggered By</th>
|
|
92
|
-
<th>Time</th>
|
|
93
|
-
<th></th>
|
|
57
|
+
<th style="text-align: right">Actions</th>
|
|
94
58
|
</tr>
|
|
95
59
|
</thead>
|
|
96
60
|
<tbody>
|
|
97
|
-
<% @
|
|
61
|
+
<% @configs.each do |config| %>
|
|
98
62
|
<tr>
|
|
99
63
|
<td>
|
|
100
|
-
<span class="font-medium" style="color: var(--rn-text)"><%=
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
<%= record.status.capitalize %>
|
|
105
|
-
</span>
|
|
106
|
-
</td>
|
|
107
|
-
<td>
|
|
108
|
-
<span class="text-xs" style="color: var(--rn-text-secondary)"><%= record.file_size ? number_to_human_size(record.file_size) : "—" %></span>
|
|
109
|
-
</td>
|
|
110
|
-
<td>
|
|
111
|
-
<span class="text-xs" style="color: var(--rn-text-secondary)"><%= record.duration ? "#{record.duration.round(1)}s" : "—" %></span>
|
|
64
|
+
<span class="font-medium" style="color: var(--rn-text)"><%= config.name %></span>
|
|
65
|
+
<% if config.description.present? %>
|
|
66
|
+
<br><span class="text-xs" style="color: var(--rn-text-muted)"><%= config.description %></span>
|
|
67
|
+
<% end %>
|
|
112
68
|
</td>
|
|
69
|
+
<td><span class="rn-badge rn-badge-info"><%= config.adapter.upcase %></span></td>
|
|
70
|
+
<td><span class="text-xs rn-mono" style="color: var(--rn-text-secondary)"><%= config.database_name %></span></td>
|
|
71
|
+
<td><span class="text-xs" style="color: var(--rn-text-muted)"><%= config.storage_path %> (keep <%= config.keep_count %>)</span></td>
|
|
113
72
|
<td>
|
|
114
|
-
|
|
73
|
+
<% if config.schedule_cron.present? %>
|
|
74
|
+
<span class="rn-badge rn-badge-warning"><%= config.schedule_cron %></span>
|
|
75
|
+
<% else %>
|
|
76
|
+
<span class="text-xs" style="color: var(--rn-text-muted)">Manual</span>
|
|
77
|
+
<% end %>
|
|
115
78
|
</td>
|
|
116
79
|
<td>
|
|
117
|
-
<span class="
|
|
80
|
+
<span class="rn-badge <%= config.enabled? ? 'rn-badge-success' : 'rn-badge-muted' %>">
|
|
81
|
+
<%= config.enabled? ? 'Enabled' : 'Disabled' %>
|
|
82
|
+
</span>
|
|
83
|
+
<% if config.encrypt? %><span class="rn-badge rn-badge-warning">Encrypted</span><% end %>
|
|
84
|
+
<% if config.rsync_enabled? %><span class="rn-badge rn-badge-info">RSync</span><% end %>
|
|
118
85
|
</td>
|
|
119
|
-
<td>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
86
|
+
<td style="text-align: right">
|
|
87
|
+
<div class="flex items-center justify-end gap-1">
|
|
88
|
+
<%= button_to "Run", backup_trigger_path(config),
|
|
89
|
+
method: :post,
|
|
90
|
+
class: "rn-btn rn-btn-primary rn-btn-xs",
|
|
91
|
+
data: { turbo_confirm: "Run #{config.name} backup now?" } %>
|
|
92
|
+
<%= link_to "History", backup_config_history_path(config), class: "rn-btn rn-btn-ghost rn-btn-xs" %>
|
|
93
|
+
<%= link_to "Edit", edit_backup_path(config), class: "rn-btn rn-btn-ghost rn-btn-xs" %>
|
|
94
|
+
<%= button_to "Delete", backup_destroy_path(config),
|
|
95
|
+
method: :delete,
|
|
96
|
+
class: "rn-btn rn-btn-ghost rn-btn-xs",
|
|
97
|
+
style: "color: var(--rn-danger)",
|
|
98
|
+
data: { turbo_confirm: "Delete config '#{config.name}'?" } %>
|
|
99
|
+
</div>
|
|
123
100
|
</td>
|
|
124
101
|
</tr>
|
|
125
102
|
<% end %>
|
|
@@ -128,8 +105,51 @@
|
|
|
128
105
|
</div>
|
|
129
106
|
<% else %>
|
|
130
107
|
<div class="p-8 text-center">
|
|
131
|
-
<p class="text-sm" style="color: var(--rn-text-muted)">No
|
|
108
|
+
<p class="text-sm" style="color: var(--rn-text-muted)">No backup configurations yet.</p>
|
|
109
|
+
<%= link_to "Create your first config", new_backup_path, class: "mt-3 inline-block text-xs font-medium", style: "color: var(--rn-primary)" %>
|
|
132
110
|
</div>
|
|
133
111
|
<% end %>
|
|
134
112
|
</div>
|
|
113
|
+
|
|
114
|
+
<%# ─── Recent Backups ─────────────────────────────────────── %>
|
|
115
|
+
<% if @records.any? %>
|
|
116
|
+
<div class="rn-card">
|
|
117
|
+
<div class="p-4" style="border-bottom: 1px solid var(--rn-border-light)">
|
|
118
|
+
<div class="flex items-center justify-between">
|
|
119
|
+
<h2 class="text-sm font-semibold" style="color: var(--rn-text)">Recent Backups</h2>
|
|
120
|
+
<%= link_to "View All", backup_history_path, class: "text-xs font-medium", style: "color: var(--rn-primary)" %>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="rn-table-wrap">
|
|
124
|
+
<table class="rn-table">
|
|
125
|
+
<thead>
|
|
126
|
+
<tr>
|
|
127
|
+
<th>Config</th>
|
|
128
|
+
<th>Status</th>
|
|
129
|
+
<th>Size</th>
|
|
130
|
+
<th>Duration</th>
|
|
131
|
+
<th>Triggered By</th>
|
|
132
|
+
<th>Time</th>
|
|
133
|
+
</tr>
|
|
134
|
+
</thead>
|
|
135
|
+
<tbody>
|
|
136
|
+
<% @records.each do |record| %>
|
|
137
|
+
<tr>
|
|
138
|
+
<td><span class="font-medium" style="color: var(--rn-text)"><%= record.model_name %></span></td>
|
|
139
|
+
<td>
|
|
140
|
+
<span class="rn-badge rn-badge-<%= record.status == 'success' ? 'success' : record.status == 'failed' ? 'danger' : 'warning' %>">
|
|
141
|
+
<%= record.status.capitalize %>
|
|
142
|
+
</span>
|
|
143
|
+
</td>
|
|
144
|
+
<td><span class="text-xs" style="color: var(--rn-text-secondary)"><%= record.file_size ? number_to_human_size(record.file_size) : "—" %></span></td>
|
|
145
|
+
<td><span class="text-xs" style="color: var(--rn-text-secondary)"><%= record.duration ? "#{record.duration.round(1)}s" : "—" %></span></td>
|
|
146
|
+
<td><span class="text-xs" style="color: var(--rn-text-muted)"><%= record.triggered_by || "—" %></span></td>
|
|
147
|
+
<td><span class="text-xs" style="color: var(--rn-text-muted)"><%= record.started_at ? time_ago_in_words(record.started_at) + " ago" : "—" %></span></td>
|
|
148
|
+
</tr>
|
|
149
|
+
<% end %>
|
|
150
|
+
</tbody>
|
|
151
|
+
</table>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
<% end %>
|
|
135
155
|
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<% page_title "New Backup Config" %>
|
|
2
|
+
|
|
3
|
+
<div class="space-y-6">
|
|
4
|
+
<nav class="rn-breadcrumbs" aria-label="Breadcrumb">
|
|
5
|
+
<a href="<%= root_path %>" class="rn-breadcrumb">Dashboard</a>
|
|
6
|
+
<span class="rn-breadcrumb-sep">/</span>
|
|
7
|
+
<a href="<%= backup_path %>" class="rn-breadcrumb">Backups</a>
|
|
8
|
+
<span class="rn-breadcrumb-sep">/</span>
|
|
9
|
+
<span class="rn-breadcrumb-current">New Config</span>
|
|
10
|
+
</nav>
|
|
11
|
+
|
|
12
|
+
<h1 class="text-2xl font-bold" style="color: var(--rn-text)">New Backup Configuration</h1>
|
|
13
|
+
|
|
14
|
+
<%= render "form" %>
|
|
15
|
+
</div>
|
data/config/routes.rb
CHANGED
|
@@ -42,11 +42,15 @@ RailsNexus::Engine.routes.draw do
|
|
|
42
42
|
get "n1_patterns", to: "n1_patterns#index", as: :n1_patterns
|
|
43
43
|
|
|
44
44
|
# Backup management
|
|
45
|
-
get
|
|
46
|
-
get
|
|
47
|
-
post "backup
|
|
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"
|
|
48
51
|
delete "backup/:id", to: "backup#destroy", as: :backup_destroy
|
|
49
|
-
|
|
52
|
+
post "backup/:id/trigger", to: "backup#trigger", as: :backup_trigger
|
|
53
|
+
get "backup/:id/history", to: "backup#history", as: :backup_config_history
|
|
50
54
|
|
|
51
55
|
# Settings and webhook management
|
|
52
56
|
get "settings", to: "settings#index", as: :settings
|
|
@@ -12,7 +12,7 @@ Rails.application.config.to_prepare do
|
|
|
12
12
|
# ─── Authentication ──────────────────────────────────────
|
|
13
13
|
# Protect the dashboard — only authorized users can access
|
|
14
14
|
# config.auth_block = lambda do |controller|
|
|
15
|
-
# controller.current_user&.admin?
|
|
15
|
+
# controller.current_user&.admin? # adjust to your auth model
|
|
16
16
|
# end
|
|
17
17
|
|
|
18
18
|
# ─── Exception Data ──────────────────────────────────────
|
data/lib/rails_nexus/version.rb
CHANGED
data/lib/tasks/rails_nexus.rake
CHANGED
|
@@ -228,11 +228,74 @@ namespace :rails_nexus do
|
|
|
228
228
|
puts "✓ Deleted #{count} webhook delivery records older than #{days} days"
|
|
229
229
|
end
|
|
230
230
|
|
|
231
|
+
# ─── Backup Tasks ──────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
desc "Run a specific backup by name: rails_nexus:backup[daily_backup]"
|
|
234
|
+
task :backup, [:name] => :environment do |_t, args|
|
|
235
|
+
name = args[:name]
|
|
236
|
+
if name.blank?
|
|
237
|
+
puts "Usage: rake rails_nexus:backup[config_name]"
|
|
238
|
+
puts "Available configs:"
|
|
239
|
+
RailsNexus::BackupConfig.enabled.pluck(:name).each { |n| puts " - #{n}" }
|
|
240
|
+
next
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
result = RailsNexus::BackupRunner.run(name)
|
|
244
|
+
if result[:success]
|
|
245
|
+
puts "✓ Backup "#{name}" completed successfully (#{result[:record].duration_human})"
|
|
246
|
+
else
|
|
247
|
+
puts "✗ Backup "#{name}" failed: #{result[:error]}"
|
|
248
|
+
exit 1
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
desc "Run all enabled backup configs"
|
|
253
|
+
task backup_all: :environment do
|
|
254
|
+
results = RailsNexus::BackupRunner.run_all
|
|
255
|
+
results.each do |r|
|
|
256
|
+
if r[:success]
|
|
257
|
+
puts "✓ #{r[:record].model_name} completed"
|
|
258
|
+
else
|
|
259
|
+
puts "✗ #{r[:record]&.model_name || '?'} failed: #{r[:error]}"
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
desc "Run backups matching current cron schedule"
|
|
265
|
+
task backup_scheduled: :environment do
|
|
266
|
+
results = RailsNexus::BackupRunner.run_scheduled
|
|
267
|
+
if results.empty?
|
|
268
|
+
puts "No backups scheduled for this time"
|
|
269
|
+
else
|
|
270
|
+
results.each do |r|
|
|
271
|
+
if r[:success]
|
|
272
|
+
puts "✓ #{r[:record].model_name} completed"
|
|
273
|
+
else
|
|
274
|
+
puts "✗ #{r[:record]&.model_name || '?'} failed: #{r[:error]}"
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
231
280
|
desc "Clean up old backup records and files (default: 30 days)"
|
|
232
281
|
task :backup_cleanup, [:days] => :environment do |_t, args|
|
|
233
282
|
days = (args[:days] || 30).to_i
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
283
|
+
count = RailsNexus::Backup.where("started_at < ?", days.days.ago).delete_all
|
|
284
|
+
puts "✓ Deleted #{count} backup records older than #{days} days"
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
desc "List all backup configs and their status"
|
|
288
|
+
task backup_list: :environment do
|
|
289
|
+
configs = RailsNexus::BackupConfig.order(:name)
|
|
290
|
+
if configs.empty?
|
|
291
|
+
puts "No backup configs found"
|
|
292
|
+
else
|
|
293
|
+
configs.each do |c|
|
|
294
|
+
status = c.enabled? ? "✓" : "✗"
|
|
295
|
+
last = c.recent_backups(limit: 1).first
|
|
296
|
+
last_str = last ? "#{last.status} #{last.started_at&.strftime('%b %d %H:%M')}" : "never"
|
|
297
|
+
puts "#{status} #{c.name.ljust(20)} #{c.adapter.ljust(12)} #{c.database_name.ljust(20)} last: #{last_str}"
|
|
298
|
+
end
|
|
299
|
+
end
|
|
237
300
|
end
|
|
238
301
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_nexus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tamiru Hailu
|
|
@@ -139,17 +139,23 @@ files:
|
|
|
139
139
|
- app/mailers/rails_nexus/application_mailer.rb
|
|
140
140
|
- app/models/rails_nexus/application_record.rb
|
|
141
141
|
- app/models/rails_nexus/backup.rb
|
|
142
|
+
- app/models/rails_nexus/backup_config.rb
|
|
142
143
|
- app/models/rails_nexus/base_record.rb
|
|
143
144
|
- app/models/rails_nexus/comment.rb
|
|
144
145
|
- app/models/rails_nexus/cron_job.rb
|
|
145
146
|
- app/models/rails_nexus/logged_exception.rb
|
|
146
147
|
- app/models/rails_nexus/logged_exception_n1.rb
|
|
147
148
|
- app/models/rails_nexus/webhook_delivery.rb
|
|
149
|
+
- app/services/rails_nexus/backup_runner.rb
|
|
148
150
|
- app/services/rails_nexus/backup_service.rb
|
|
149
151
|
- app/views/layouts/rails_nexus/application.html.erb
|
|
150
152
|
- app/views/rails_nexus/analytics/index.html.erb
|
|
151
|
-
- app/views/rails_nexus/backup/
|
|
153
|
+
- app/views/rails_nexus/backup/_form.html.erb
|
|
154
|
+
- app/views/rails_nexus/backup/config_history.html.erb
|
|
155
|
+
- app/views/rails_nexus/backup/edit.html.erb
|
|
156
|
+
- app/views/rails_nexus/backup/history.html.erb
|
|
152
157
|
- app/views/rails_nexus/backup/index.html.erb
|
|
158
|
+
- app/views/rails_nexus/backup/new.html.erb
|
|
153
159
|
- app/views/rails_nexus/cron_jobs/index.html.erb
|
|
154
160
|
- app/views/rails_nexus/database_health/index.html.erb
|
|
155
161
|
- app/views/rails_nexus/logged_exceptions/_comments_list.html.erb
|
|
@@ -172,7 +178,6 @@ files:
|
|
|
172
178
|
- app/views/rails_nexus/stats/index.html.erb
|
|
173
179
|
- config/importmap.rb
|
|
174
180
|
- config/initializers/date_formats.rb
|
|
175
|
-
- config/initializers/rails_nexus.rb
|
|
176
181
|
- config/locales/en.yml
|
|
177
182
|
- config/routes.rb
|
|
178
183
|
- lib/generators/rails_nexus/backup_generator.rb
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Rails.application.config.to_prepare do
|
|
4
|
-
RailsNexus.configure do |config|
|
|
5
|
-
# Protect the dashboard - only admin users can access
|
|
6
|
-
config.auth_block = lambda do |controller|
|
|
7
|
-
controller.current_user&.admin?
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
# Attach additional data to each exception record
|
|
11
|
-
config.exception_data = lambda do |controller|
|
|
12
|
-
{
|
|
13
|
-
user_id: controller.current_user&.id,
|
|
14
|
-
request_id: controller.request.request_id
|
|
15
|
-
}
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|