rails_nexus 2.0.2 → 2.0.6
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/assets/javascripts/rails_nexus/controllers.js +21 -1
- data/app/assets/stylesheets/rails_nexus/application.css +295 -29
- data/app/controllers/rails_nexus/backup_controller.rb +6 -2
- data/app/javascript/controllers/rails_nexus_controller.js +2 -0
- data/app/javascript/controllers/rails_nexus_sidebar_controller.js +13 -0
- data/app/models/rails_nexus/backup_config.rb +77 -14
- data/app/services/rails_nexus/backup_service.rb +416 -97
- data/app/views/layouts/rails_nexus/_sidebar_navigation.html.erb +83 -0
- data/app/views/layouts/rails_nexus/application.html.erb +70 -22
- data/app/views/rails_nexus/backup/_form.html.erb +183 -37
- data/app/views/rails_nexus/logged_exceptions/_exceptions.html.erb +16 -15
- data/app/views/rails_nexus/logged_exceptions/index.html.erb +3 -3
- data/db/seeds/backup_configs.rb +139 -0
- data/lib/generators/rails_nexus/templates/migration.rb +59 -0
- data/lib/rails_nexus/version.rb +1 -1
- data/lib/tasks/rails_nexus.rake +10 -0
- metadata +3 -1
|
@@ -4,50 +4,106 @@ module RailsNexus
|
|
|
4
4
|
class BackupConfig < BaseRecord
|
|
5
5
|
self.table_name = "rails_nexus_backup_configs"
|
|
6
6
|
|
|
7
|
-
ADAPTERS = %w[mysql postgresql sqlite].freeze
|
|
7
|
+
ADAPTERS = %w[mysql postgresql sqlite mongodb redis].freeze
|
|
8
|
+
|
|
9
|
+
# ─── Boolean defaults ──────────────────────────────────────────
|
|
10
|
+
# Rails doesn't apply DB defaults to new in-memory records, so
|
|
11
|
+
# check_box helpers in form_for render unchecked. These declarations
|
|
12
|
+
# ensure the form reflects the intended defaults.
|
|
13
|
+
attribute :enabled, :boolean, default: true
|
|
14
|
+
attribute :compress, :boolean, default: true
|
|
15
|
+
attribute :encrypted, :boolean, default: false
|
|
16
|
+
attribute :rsync_enabled, :boolean, default: false
|
|
17
|
+
attribute :rsync_mirror, :boolean, default: false
|
|
18
|
+
attribute :notify_on_success, :boolean, default: true
|
|
19
|
+
attribute :notify_on_failure, :boolean, default: true
|
|
20
|
+
attribute :s3_enabled, :boolean, default: false
|
|
21
|
+
attribute :gpg_enabled, :boolean, default: false
|
|
22
|
+
attribute :email_notify, :boolean, default: false
|
|
23
|
+
attribute :archive_enabled, :boolean, default: false
|
|
24
|
+
attribute :bzip2_compress, :boolean, default: false
|
|
25
|
+
attribute :split_chunks, :boolean, default: false
|
|
8
26
|
|
|
9
27
|
validates :name, presence: true, uniqueness: true
|
|
10
|
-
validates :database_name, presence: true
|
|
28
|
+
validates :database_name, presence: true, if: -> { adapter != "redis" }
|
|
11
29
|
validates :adapter, presence: true, inclusion: { in: ADAPTERS }
|
|
12
30
|
validates :storage_path, presence: true
|
|
13
31
|
validates :keep_count, numericality: { greater_than: 0 }
|
|
14
|
-
validates :
|
|
32
|
+
validates :encryption_password, presence: true, if: :encrypted?
|
|
15
33
|
validates :rsync_host, presence: true, if: :rsync_enabled?
|
|
16
34
|
validates :rsync_user, presence: true, if: :rsync_enabled?
|
|
35
|
+
validates :s3_bucket, presence: true, if: :s3_enabled?
|
|
36
|
+
validates :s3_access_key, presence: true, if: :s3_enabled?
|
|
37
|
+
validates :s3_secret_key, presence: true, if: :s3_enabled?
|
|
38
|
+
validates :gpg_password, presence: true, if: :gpg_enabled?
|
|
39
|
+
validates :email_to, presence: true, if: :email_notify?
|
|
17
40
|
|
|
18
41
|
scope :enabled, -> { where(enabled: true) }
|
|
19
42
|
scope :disabled, -> { where(enabled: false) }
|
|
20
43
|
|
|
21
44
|
serialize :skip_tables, coder: JSON
|
|
45
|
+
serialize :archive_paths, coder: JSON
|
|
46
|
+
serialize :archive_excludes, coder: JSON
|
|
22
47
|
|
|
48
|
+
# ─── Skip Tables ───────────────────────────────────────────────
|
|
23
49
|
def skip_tables_list
|
|
24
50
|
return [] if skip_tables.blank?
|
|
25
51
|
skip_tables.is_a?(Array) ? skip_tables : JSON.parse(skip_tables.to_s) rescue []
|
|
26
52
|
end
|
|
27
53
|
|
|
28
|
-
def skip_tables_list=(
|
|
29
|
-
|
|
54
|
+
def skip_tables_list=(value)
|
|
55
|
+
return if value.blank?
|
|
56
|
+
self.skip_tables = if value.is_a?(String)
|
|
57
|
+
value.split(",").map(&:strip).reject(&:blank?)
|
|
58
|
+
else
|
|
59
|
+
value
|
|
60
|
+
end
|
|
30
61
|
end
|
|
31
62
|
|
|
32
|
-
|
|
33
|
-
|
|
63
|
+
# ─── Archive Paths ─────────────────────────────────────────────
|
|
64
|
+
def archive_paths_list
|
|
65
|
+
return [] if archive_paths.blank?
|
|
66
|
+
archive_paths.is_a?(Array) ? archive_paths : JSON.parse(archive_paths.to_s) rescue []
|
|
34
67
|
end
|
|
35
68
|
|
|
36
|
-
def
|
|
37
|
-
|
|
69
|
+
def archive_paths_list=(value)
|
|
70
|
+
return if value.blank?
|
|
71
|
+
self.archive_paths = if value.is_a?(String)
|
|
72
|
+
value.split(",").map(&:strip).reject(&:blank?)
|
|
73
|
+
else
|
|
74
|
+
value
|
|
75
|
+
end
|
|
38
76
|
end
|
|
39
77
|
|
|
40
|
-
def
|
|
41
|
-
|
|
78
|
+
def archive_excludes_list
|
|
79
|
+
return [] if archive_excludes.blank?
|
|
80
|
+
archive_excludes.is_a?(Array) ? archive_excludes : JSON.parse(archive_excludes.to_s) rescue []
|
|
42
81
|
end
|
|
43
82
|
|
|
83
|
+
def archive_excludes_list=(value)
|
|
84
|
+
return if value.blank?
|
|
85
|
+
self.archive_excludes = if value.is_a?(String)
|
|
86
|
+
value.split(",").map(&:strip).reject(&:blank?)
|
|
87
|
+
else
|
|
88
|
+
value
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# ─── Adapter checks ────────────────────────────────────────────
|
|
93
|
+
def mysql?; adapter == "mysql"; end
|
|
94
|
+
def postgresql?; adapter == "postgresql"; end
|
|
95
|
+
def sqlite?; adapter == "sqlite"; end
|
|
96
|
+
def mongodb?; adapter == "mongodb"; end
|
|
97
|
+
def redis?; adapter == "redis"; end
|
|
98
|
+
|
|
99
|
+
# ─── Path helpers ──────────────────────────────────────────────
|
|
44
100
|
def storage_path_expanded
|
|
45
101
|
storage_path.gsub("~", Dir.home)
|
|
46
102
|
end
|
|
47
103
|
|
|
48
104
|
def dump_filename
|
|
49
105
|
timestamp = Time.current.strftime("%Y%m%d_%H%M%S")
|
|
50
|
-
ext =
|
|
106
|
+
ext = dump_extension
|
|
51
107
|
"#{name}_#{timestamp}.#{ext}"
|
|
52
108
|
end
|
|
53
109
|
|
|
@@ -55,12 +111,11 @@ module RailsNexus
|
|
|
55
111
|
File.join(storage_path_expanded, dump_filename)
|
|
56
112
|
end
|
|
57
113
|
|
|
58
|
-
#
|
|
114
|
+
# ─── Stats ─────────────────────────────────────────────────────
|
|
59
115
|
def recent_backups(limit: 10)
|
|
60
116
|
RailsNexus::Backup.where(model_name: name).order(started_at: :desc).limit(limit)
|
|
61
117
|
end
|
|
62
118
|
|
|
63
|
-
# Summary stats
|
|
64
119
|
def stats
|
|
65
120
|
backups = RailsNexus::Backup.where(model_name: name)
|
|
66
121
|
recent = backups.where("started_at >= ?", 7.days.ago)
|
|
@@ -79,6 +134,14 @@ module RailsNexus
|
|
|
79
134
|
|
|
80
135
|
private
|
|
81
136
|
|
|
137
|
+
def dump_extension
|
|
138
|
+
parts = ["sql"]
|
|
139
|
+
parts << "gz" if compress? && !bzip2_compress?
|
|
140
|
+
parts << "bz2" if bzip2_compress?
|
|
141
|
+
parts << "enc" if encrypted?
|
|
142
|
+
parts.join(".")
|
|
143
|
+
end
|
|
144
|
+
|
|
82
145
|
def human_size(bytes)
|
|
83
146
|
return "0 B" if bytes.zero?
|
|
84
147
|
units = %w[B KB MB GB TB]
|