ops_backups 0.1.12 → 0.1.14
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/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
|