ops_backups 0.1.12 → 0.1.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/ops_backups/application_controller.rb +13 -0
- data/app/controllers/ops_backups/backups_controller.rb +29 -1
- data/app/jobs/ops_backups/backup_db_job.rb +1 -0
- data/app/models/ops_backups/backup.rb +5 -4
- data/app/views/ops_backups/backups/index.html.erb +86 -13
- data/config/routes.rb +4 -2
- data/lib/generators/ops_backups/activeadmin/templates/backup.rb +10 -0
- data/lib/generators/ops_backups/install/install_generator.rb +1 -1
- data/lib/ops_backups/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 676b08b7df0898e0bc8fbc2572b8aa7801f09c74cba43f5544836d6111a27cfe
|
4
|
+
data.tar.gz: 6377a44547eb4a34e97d13ccdf16db48a3739ca2c61ed43c5f8cf371be6fbfe1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d03c15b798b0fe5cef8ced86cbb27ff14cf657ceb9052883e88e6e24d132cf28e712797ac87c3f436a2e04da5fc4c1dfc65540877648615c9c9fade12658da8
|
7
|
+
data.tar.gz: f3cbf7c12f6f54feaeca3a05fed9c667fbcc559936b36eb2b81408f02bd802c1e6354a9b7574e42c236427de6700acb47965180d57b26809e9262cd5fa38e33f
|
@@ -1,4 +1,17 @@
|
|
1
1
|
module OpsBackups
|
2
2
|
class ApplicationController < ActionController::Base
|
3
|
+
before_action :set_locale
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def set_locale
|
8
|
+
# set the locale only if the locale is available
|
9
|
+
locale = extract_locale
|
10
|
+
I18n.locale = locale if I18n.available_locales.map(&:to_s).include?(locale)
|
11
|
+
end
|
12
|
+
|
13
|
+
def extract_locale
|
14
|
+
request.env["HTTP_ACCEPT_LANGUAGE"].split(",")&.first&.split(";")&.first.to_s
|
15
|
+
end
|
3
16
|
end
|
4
17
|
end
|
@@ -1,12 +1,40 @@
|
|
1
1
|
module OpsBackups
|
2
2
|
class BackupsController < ApplicationController
|
3
3
|
def index
|
4
|
-
@backups = OpsBackups::Backup.
|
4
|
+
@backups = OpsBackups::Backup.all.order(updated_at: :desc)
|
5
|
+
@jobs = jobs
|
5
6
|
end
|
6
7
|
|
7
8
|
def download
|
8
9
|
backup = OpsBackups::Backup.find(params[:id])
|
9
10
|
redirect_to backup.backup_file.url(disposition: :attachment), allow_other_host: true
|
10
11
|
end
|
12
|
+
|
13
|
+
def destroy
|
14
|
+
backup = OpsBackups::Backup.find(params[:id])
|
15
|
+
backup.destroy
|
16
|
+
redirect_to backups_url, notice: "#{backup} destroyed."
|
17
|
+
end
|
18
|
+
|
19
|
+
def trigger_job
|
20
|
+
job_name = params[:job_name]
|
21
|
+
job_config = jobs[job_name]
|
22
|
+
|
23
|
+
job_class = job_config["class"].constantize
|
24
|
+
args = job_config["args"] || []
|
25
|
+
|
26
|
+
Rails.logger.info "Triggering job: #{job_name} with args: #{args.reduce({}, :merge).symbolize_keys}"
|
27
|
+
|
28
|
+
job_class.perform_later(args.reduce({}, :merge).symbolize_keys)
|
29
|
+
|
30
|
+
redirect_to backups_url, notice: "#{job_name} triggered successfully."
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def jobs
|
36
|
+
jobs = YAML.load_file(Rails.root.join("config", "recurring.yml"))[Rails.env]
|
37
|
+
jobs&.select { |k, v| v["class"].start_with?("OpsBackups") }
|
38
|
+
end
|
11
39
|
end
|
12
40
|
end
|
@@ -12,6 +12,7 @@ class OpsBackups::BackupDbJob < ApplicationJob
|
|
12
12
|
exclude_tables = options[:exclude_tables] || []
|
13
13
|
tag = options[:tag] || (exclude_tables.empty? ? "db_pg_full" : "db_pg_partial")
|
14
14
|
cleanup = options[:cleanup]
|
15
|
+
Rails.logger.info "Performing backup with tag: #{tag} and exclude_tables: #{exclude_tables}"
|
15
16
|
OpsBackups::Backup.new.db_pg_backup(exclude_tables:, tag:)
|
16
17
|
OpsBackups::Backup.send("#{cleanup}_cleanup_policy", tag: tag) if cleanup.present?
|
17
18
|
end
|
@@ -8,10 +8,6 @@ module OpsBackups
|
|
8
8
|
|
9
9
|
default_scope { order(updated_at: :desc) }
|
10
10
|
|
11
|
-
def self.ransackable_attributes(auth_object = nil)
|
12
|
-
[ "created_at", "id", "name", "new_id", "tag", "updated_at" ]
|
13
|
-
end
|
14
|
-
|
15
11
|
def size
|
16
12
|
backup_file.attached? ? number_to_human_size(backup_file.byte_size) : "N/A"
|
17
13
|
end
|
@@ -21,6 +17,11 @@ module OpsBackups
|
|
21
17
|
end
|
22
18
|
|
23
19
|
def db_pg_backup(tag: nil, exclude_tables: [])
|
20
|
+
if ENV["DATABASE_URL"].blank?
|
21
|
+
Rails.logger.error("Failed to backup database: DATABASE_URL is not set")
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
24
25
|
db_url = ENV["DATABASE_URL"]
|
25
26
|
tag ||= exclude_tables.empty? ? "db_pg_full" : "db_pg_partial" # if tag.empty?
|
26
27
|
self.tag = tag
|
@@ -1,46 +1,119 @@
|
|
1
1
|
<style>
|
2
|
+
@keyframes fadeOut {
|
3
|
+
to {
|
4
|
+
opacity: 0;
|
5
|
+
}
|
6
|
+
}
|
7
|
+
/* all elements have a border */
|
8
|
+
ops-backups * {
|
9
|
+
/* border: 1px solid red; */
|
10
|
+
}
|
2
11
|
ops-backups {
|
3
12
|
display: flex;
|
4
|
-
padding:
|
5
|
-
/* flex column */
|
13
|
+
padding: 0.5em;
|
6
14
|
flex-direction: column;
|
15
|
+
& h1 {
|
16
|
+
margin: 0;
|
17
|
+
margin-bottom: 0.25em;
|
18
|
+
}
|
7
19
|
& ops-backup {
|
8
20
|
display: grid;
|
9
|
-
grid-template-columns:
|
21
|
+
grid-template-columns: 3em 3fr 2fr 6em 6em 12em 6em;
|
22
|
+
gap: 0.5em;
|
10
23
|
padding: 0.25em;
|
11
|
-
& ops-backup-
|
24
|
+
& ops-backup-index,
|
25
|
+
ops-backup-size,
|
12
26
|
ops-backup-duration,
|
13
27
|
ops-backup-created_at,
|
14
|
-
ops-backup-
|
28
|
+
ops-backup-actions {
|
15
29
|
text-align: right;
|
16
30
|
}
|
31
|
+
& ops-backup-actions {
|
32
|
+
display: flex;
|
33
|
+
flex-direction: row;
|
34
|
+
justify-content: flex-end;
|
35
|
+
gap: 0.5em;
|
36
|
+
}
|
17
37
|
}
|
18
38
|
& ops-backup.header {
|
19
39
|
font-weight: bold;
|
40
|
+
border-bottom: 1px solid black;
|
41
|
+
margin-bottom: 0.25em;
|
42
|
+
}
|
43
|
+
& ops-backup-jobs {
|
44
|
+
display: flex;
|
45
|
+
gap: 0.5em;
|
46
|
+
margin-bottom: 0.5em;
|
47
|
+
/* padding: 0.5em; */
|
48
|
+
}
|
49
|
+
}
|
50
|
+
.alerts {
|
51
|
+
/* position these alerts at the bottom right */
|
52
|
+
position: fixed;
|
53
|
+
bottom: 1em;
|
54
|
+
right: 1em;
|
55
|
+
animation: fadeOut 3s forwards 3s;
|
56
|
+
display: flex;
|
57
|
+
flex-direction: column;
|
58
|
+
align-items: flex-end;
|
59
|
+
gap: 0.5em;
|
60
|
+
& .alert {
|
61
|
+
padding: 0.5em;
|
62
|
+
border-radius: 0.25em;
|
63
|
+
}
|
64
|
+
& .alert-notice {
|
65
|
+
background-color: lightgreen;
|
66
|
+
}
|
67
|
+
& .alert-danger {
|
68
|
+
background-color: lightcoral;
|
20
69
|
}
|
21
70
|
}
|
22
71
|
</style>
|
23
|
-
<h1>Backups</h1>
|
24
72
|
<ops-backups>
|
73
|
+
<h1>Backups</h1>
|
74
|
+
<% if @jobs.present? %>
|
75
|
+
<ops-backup-jobs>
|
76
|
+
<% @jobs.each do |name, config| %>
|
77
|
+
<%= button_to name, trigger_job_path(job_name: name), method: :post %>
|
78
|
+
<% end %>
|
79
|
+
</ops-backup-jobs>
|
80
|
+
<% end %>
|
25
81
|
<ops-backup class="header">
|
82
|
+
<ops-backup-index>Index</ops-backup-index>
|
26
83
|
<ops-backup-name>Name</ops-backup-name>
|
27
84
|
<ops-backup-tag>Tag</ops-backup-tag>
|
28
85
|
<ops-backup-size>Size</ops-backup-size>
|
29
86
|
<ops-backup-duration>Duration</ops-backup-duration>
|
30
87
|
<ops-backup-created_at>Created At</ops-backup-created_at>
|
88
|
+
<ops-backup-actions>Actions</ops-backup-actions>
|
31
89
|
</ops-backup>
|
32
|
-
<% @backups.each do |backup| %>
|
33
|
-
<ops-backup>
|
90
|
+
<% @backups.each.with_index do |backup, i| %>
|
91
|
+
<ops-backup id="ops-backup-<%= i %>">
|
92
|
+
<ops-backup-index><%= i %></ops-backup-index>
|
34
93
|
<ops-backup-name><%= backup.name %></ops-backup-name>
|
35
|
-
<ops-backup-tag><%= backup.
|
94
|
+
<ops-backup-tag><%= backup.tag %></ops-backup-tag>
|
36
95
|
<ops-backup-size><%= backup.size %></ops-backup-size>
|
37
96
|
<ops-backup-duration><%= backup.duration %></ops-backup-duration>
|
38
|
-
<ops-backup-created_at><%= backup.created_at %></ops-backup-created_at>
|
39
|
-
<ops-backup-
|
97
|
+
<ops-backup-created_at data-time="<%= backup.created_at.iso8601 %>"><%= l backup.created_at, format: :short %></ops-backup-created_at>
|
98
|
+
<ops-backup-actions>
|
40
99
|
<% if backup.backup_file.attached? %>
|
41
|
-
|
100
|
+
<%= button_to "📎", download_backup_path(backup), method: :get %>
|
42
101
|
<% end %>
|
43
|
-
|
102
|
+
<%= button_to "🗑️", backup_path(backup), method: :delete %>
|
103
|
+
</ops-backup-actions>
|
44
104
|
</ops-backup>
|
45
105
|
<% end %>
|
46
106
|
</ops-backups>
|
107
|
+
<div class="alerts">
|
108
|
+
<% flash.each do |key, value| %>
|
109
|
+
<div class="alert alert-<%= key %>"><%= value %></div>
|
110
|
+
<% end %>
|
111
|
+
</div>
|
112
|
+
<script>
|
113
|
+
// document.addEventListener("DOMContentLoaded", function() {
|
114
|
+
// document.querySelectorAll("ops-backup-created_at").forEach(function(element) {
|
115
|
+
// const time = new Date(element.getAttribute("data-time"));
|
116
|
+
// element.textContent = time.toLocaleString();
|
117
|
+
// });
|
118
|
+
// });
|
119
|
+
</script>
|
data/config/routes.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
OpsBackups::Engine.routes.draw do
|
2
|
-
get
|
3
|
-
get
|
2
|
+
get "backups", to: "backups#index"
|
3
|
+
get "backups/:id/download", to: "backups#download", as: "download_backup"
|
4
|
+
delete "backups/:id", to: "backups#destroy", as: "backup"
|
5
|
+
post "backups/trigger_job", to: "backups#trigger_job", as: "trigger_job"
|
4
6
|
end
|
@@ -1,3 +1,13 @@
|
|
1
|
+
# monkey patch the Backup model to allow for ransackable_attributes
|
2
|
+
module OpsBackups
|
3
|
+
class Backup < ActiveRecord::Base
|
4
|
+
def self.ransackable_attributes(auth_object = nil)
|
5
|
+
[ "created_at", "id", "name", "new_id", "tag", "updated_at" ]
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# ActiveAdmin resource for OpsBackups::Backup
|
1
11
|
ActiveAdmin.register OpsBackups::Backup do
|
2
12
|
menu parent: "Ops", label: I18n.t("admin.ops.backup")
|
3
13
|
|
@@ -39,7 +39,7 @@ class OpsBackups::InstallGenerator < Rails::Generators::Base
|
|
39
39
|
recurring_config = <<~YAML.lines.map { |line| " #{line}" }.join
|
40
40
|
backup_db:
|
41
41
|
class: OpsBackups::BackupDbJob
|
42
|
-
args: [tag: "db_pg_backup", cleanup: "
|
42
|
+
args: [tag: "db_pg_backup", cleanup: "retain_tiered"]
|
43
43
|
schedule: every hour
|
44
44
|
YAML
|
45
45
|
|
data/lib/ops_backups/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ops_backups
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koen Handekyn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|