ops_backups 0.1.11 → 0.1.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/ops_backups/application.css +3 -0
- data/app/controllers/ops_backups/backups_controller.rb +40 -0
- data/app/helpers/ops_backups/{application_helper.rb → backups_helper.rb} +1 -1
- data/app/jobs/ops_backups/backup_db_job.rb +1 -0
- data/app/models/ops_backups/backup.rb +17 -2
- data/app/views/layouts/ops_backups/application.html.erb +2 -2
- data/app/views/ops_backups/backups/index.html.erb +111 -0
- data/config/routes.rb +4 -0
- data/lib/generators/ops_backups/install/install_generator.rb +1 -1
- data/lib/ops_backups/version.rb +1 -1
- metadata +5 -4
- data/app/mailers/ops_backups/application_mailer.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9fd477dfd14deed2d6a2781ed4da42ccd0acd90e1c72111d1b577d7392f5cda5
|
4
|
+
data.tar.gz: c881e6740abefb06b6367a855be6a46b3231def32529c6773b1e471d57705d25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5d2218b533240662da7fa48af937ea24b7f5c7e669278c0dd352b59ba28dde091a03ebdda9202045867bbbe2d82f911b3214a16d66a343f7951b99a3a26ad6b
|
7
|
+
data.tar.gz: 8211a1acf5bffbb596c709e9dbc1f857fade276d017d83cd246ba03c6b084378deea6afa1ac3c9753348724ca082d41a675648ce685f8e2dce7b26f2064599c7
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module OpsBackups
|
2
|
+
class BackupsController < ApplicationController
|
3
|
+
def index
|
4
|
+
@backups = OpsBackups::Backup.all.order(updated_at: :desc)
|
5
|
+
@jobs = jobs
|
6
|
+
end
|
7
|
+
|
8
|
+
def download
|
9
|
+
backup = OpsBackups::Backup.find(params[:id])
|
10
|
+
redirect_to backup.backup_file.url(disposition: :attachment), allow_other_host: true
|
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
|
39
|
+
end
|
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
|
@@ -1,16 +1,31 @@
|
|
1
1
|
module OpsBackups
|
2
2
|
class Backup < ActiveRecord::Base
|
3
|
-
|
4
|
-
|
3
|
+
include ActionView::Helpers::NumberHelper
|
4
|
+
|
5
5
|
self.table_name = "ops_backups"
|
6
6
|
|
7
|
+
has_one_attached :backup_file, service: :backups
|
8
|
+
|
7
9
|
default_scope { order(updated_at: :desc) }
|
8
10
|
|
9
11
|
def self.ransackable_attributes(auth_object = nil)
|
10
12
|
[ "created_at", "id", "name", "new_id", "tag", "updated_at" ]
|
11
13
|
end
|
12
14
|
|
15
|
+
def size
|
16
|
+
backup_file.attached? ? number_to_human_size(backup_file.byte_size) : "N/A"
|
17
|
+
end
|
18
|
+
|
19
|
+
def duration
|
20
|
+
Time.at((updated_at - created_at)).utc.strftime("%H:%M:%S")
|
21
|
+
end
|
22
|
+
|
13
23
|
def db_pg_backup(tag: nil, exclude_tables: [])
|
24
|
+
if ENV["DATABASE_URL"].blank?
|
25
|
+
Rails.logger.error("Failed to backup database: DATABASE_URL is not set")
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
14
29
|
db_url = ENV["DATABASE_URL"]
|
15
30
|
tag ||= exclude_tables.empty? ? "db_pg_full" : "db_pg_partial" # if tag.empty?
|
16
31
|
self.tag = tag
|
@@ -1,13 +1,13 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
-
<title>Ops
|
4
|
+
<title>Ops Backups</title>
|
5
5
|
<%= csrf_meta_tags %>
|
6
6
|
<%= csp_meta_tag %>
|
7
7
|
|
8
8
|
<%= yield :head %>
|
9
9
|
|
10
|
-
<%= stylesheet_link_tag
|
10
|
+
<%= stylesheet_link_tag "ops_backups/application", media: "all" %>
|
11
11
|
</head>
|
12
12
|
<body>
|
13
13
|
|
@@ -0,0 +1,111 @@
|
|
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
|
+
}
|
11
|
+
ops-backups {
|
12
|
+
display: flex;
|
13
|
+
padding: 0.5em;
|
14
|
+
flex-direction: column;
|
15
|
+
& h1 {
|
16
|
+
margin: 0;
|
17
|
+
margin-bottom: 0.25em;
|
18
|
+
}
|
19
|
+
& ops-backup {
|
20
|
+
display: grid;
|
21
|
+
grid-template-columns: 3em 1fr 1fr 6em 8em 16em 8em;
|
22
|
+
gap: 0.5em;
|
23
|
+
padding: 0.25em;
|
24
|
+
& ops-backup-index,
|
25
|
+
ops-backup-size,
|
26
|
+
ops-backup-duration,
|
27
|
+
ops-backup-created_at,
|
28
|
+
ops-backup-actions {
|
29
|
+
text-align: right;
|
30
|
+
}
|
31
|
+
& ops-backup-actions {
|
32
|
+
display: flex;
|
33
|
+
flex-direction: row;
|
34
|
+
justify-content: flex-end;
|
35
|
+
gap: 0.5em;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
& ops-backup.header {
|
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;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
</style>
|
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 %>
|
81
|
+
<ops-backup class="header">
|
82
|
+
<ops-backup-index>Index</ops-backup-index>
|
83
|
+
<ops-backup-name>Name</ops-backup-name>
|
84
|
+
<ops-backup-tag>Tag</ops-backup-tag>
|
85
|
+
<ops-backup-size>Size</ops-backup-size>
|
86
|
+
<ops-backup-duration>Duration</ops-backup-duration>
|
87
|
+
<ops-backup-created_at>Created At</ops-backup-created_at>
|
88
|
+
<ops-backup-actions>Actions</ops-backup-actions>
|
89
|
+
</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>
|
93
|
+
<ops-backup-name><%= backup.name %></ops-backup-name>
|
94
|
+
<ops-backup-tag><%= backup.name %></ops-backup-tag>
|
95
|
+
<ops-backup-size><%= backup.size %></ops-backup-size>
|
96
|
+
<ops-backup-duration><%= backup.duration %></ops-backup-duration>
|
97
|
+
<ops-backup-created_at><%= backup.created_at %></ops-backup-created_at>
|
98
|
+
<ops-backup-actions>
|
99
|
+
<% if backup.backup_file.attached? %>
|
100
|
+
<%= button_to "📎", download_backup_path(backup), method: :get %>
|
101
|
+
<% end %>
|
102
|
+
<%= button_to "🗑️", backup_path(backup), method: :delete %>
|
103
|
+
</ops-backup-actions>
|
104
|
+
</ops-backup>
|
105
|
+
<% end %>
|
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>
|
data/config/routes.rb
CHANGED
@@ -1,2 +1,6 @@
|
|
1
1
|
OpsBackups::Engine.routes.draw do
|
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"
|
2
6
|
end
|
@@ -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.13
|
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-
|
11
|
+
date: 2024-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -38,15 +38,16 @@ files:
|
|
38
38
|
- Rakefile
|
39
39
|
- app/assets/stylesheets/ops_backups/application.css
|
40
40
|
- app/controllers/ops_backups/application_controller.rb
|
41
|
-
- app/
|
41
|
+
- app/controllers/ops_backups/backups_controller.rb
|
42
|
+
- app/helpers/ops_backups/backups_helper.rb
|
42
43
|
- app/jobs/ops_backups/application_job.rb
|
43
44
|
- app/jobs/ops_backups/backup_db_job.rb
|
44
45
|
- app/jobs/ops_backups/cleanup_limit_job.rb
|
45
46
|
- app/jobs/ops_backups/cleanup_tiered_job.rb
|
46
|
-
- app/mailers/ops_backups/application_mailer.rb
|
47
47
|
- app/models/ops_backups/application_record.rb
|
48
48
|
- app/models/ops_backups/backup.rb
|
49
49
|
- app/views/layouts/ops_backups/application.html.erb
|
50
|
+
- app/views/ops_backups/backups/index.html.erb
|
50
51
|
- bin/pgdb
|
51
52
|
- bin/pgdb.sh
|
52
53
|
- config/locales/en.yml
|