ops_backups 0.1.11 → 0.1.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b2fef2adc42b4749261dd1a0eb29142b0e55c84dd0465a519bdfdea0e3b86314
4
- data.tar.gz: 4bf5d15d8584abfb624fc8071da8cb5afa578830e1bc4b8754b8e716692e67d6
3
+ metadata.gz: 9fd477dfd14deed2d6a2781ed4da42ccd0acd90e1c72111d1b577d7392f5cda5
4
+ data.tar.gz: c881e6740abefb06b6367a855be6a46b3231def32529c6773b1e471d57705d25
5
5
  SHA512:
6
- metadata.gz: 7e70cc33d5f7d23e4f9cff8b22b0a6bcaa32d0830d4ea232c5d3e41db2f9adaca62ad484423c96ffbc9ec85d38fb2202094f3ff55c28b5a24c29c320a4a4caf3
7
- data.tar.gz: 277e00a4449260ff40a4558df7bf7cd624e20a73ca72fe696d7bebd257529bbf2b175e813351401e0756d6f135ad2646b1139bdc0f35e68cf2e49a747247d55d
6
+ metadata.gz: a5d2218b533240662da7fa48af937ea24b7f5c7e669278c0dd352b59ba28dde091a03ebdda9202045867bbbe2d82f911b3214a16d66a343f7951b99a3a26ad6b
7
+ data.tar.gz: 8211a1acf5bffbb596c709e9dbc1f857fade276d017d83cd246ba03c6b084378deea6afa1ac3c9753348724ca082d41a675648ce685f8e2dce7b26f2064599c7
@@ -13,3 +13,6 @@
13
13
  *= require_tree .
14
14
  *= require_self
15
15
  */
16
+ html {
17
+ font-family: 'Courier New', Courier, monospace;
18
+ }
@@ -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
@@ -1,4 +1,4 @@
1
1
  module OpsBackups
2
- module ApplicationHelper
2
+ module BackupsHelper
3
3
  end
4
4
  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
- # has_one_attached :backup_file, service: :backup_storage
4
- has_one_attached :backup_file, service: :backups
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 backups</title>
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 "ops_backups/application", media: "all" %>
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: "retain_tiered_cleanup_policy"]
42
+ args: [tag: "db_pg_backup", cleanup: "retain_tiered"]
43
43
  schedule: every hour
44
44
  YAML
45
45
 
@@ -1,3 +1,3 @@
1
1
  module OpsBackups
2
- VERSION = "0.1.11"
2
+ VERSION = "0.1.13"
3
3
  end
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.11
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-28 00:00:00.000000000 Z
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/helpers/ops_backups/application_helper.rb
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
@@ -1,6 +0,0 @@
1
- module OpsBackups
2
- class ApplicationMailer < ActionMailer::Base
3
- default from: "from@example.com"
4
- layout "mailer"
5
- end
6
- end