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
         |