Ru-b2-SQL-Backups/mysql_database_restore.rb

100 lines
3.0 KiB
Ruby
Raw Permalink Normal View History

2023-07-30 09:38:50 -06:00
# frozen_string_literal: true
require 'English'
require 'tty-prompt'
require 'tty-progressbar'
require 'sequel'
require 'open3'
config = JSON.parse(File.read('config.json'))
host = config['mysql']['host']
username = config['mysql']['username']
password = config['mysql']['password']
b2_bucket_name = config['b2']&.dig('bucket_name')
prompt = TTY::Prompt.new
# Connect to the SQLite database
db = Sequel.sqlite('backups.db')
# Get a reference to the backups table
backups_table = db[:backups]
# Query the database for remote and local backups
remote_backups = backups_table.where(backup_type: 'remote').map(:backup_name)
local_backups = backups_table.where(backup_type: 'local').map(:backup_name)
# Define the categories with the queried lists
choices = [
{
name: 'Remote Backups',
choices: remote_backups
},
{
name: 'Local Backups',
choices: local_backups
}
]
user_choice = prompt.select('Choose a backup to restore:', choices)
# Determine if the chosen backup is local or remote
backup_type = backups_table.where(backup_name: user_choice).get(:backup_type)
if backup_type == 'remote'
# Download the remote backup from B2
`./b2 download-file-by-name #{b2_bucket_name} #{user_choice} ./#{user_choice}`
end
# Perform a "dry run" of the SQL script
output = `mysql --host=#{host} --user=#{username} --password=#{password} #{user_choice}
--execute="START TRANSACTION; SOURCE #{user_choice}; ROLLBACK;"`
if $CHILD_STATUS.success?
prompt.say('SQL file passed the dry run.')
else
prompt.say("Error in SQL file: #{output}")
exit 1
end
# Ask the user for confirmation before dropping the database
if prompt.yes?("Are you sure you want to drop the database and restore it from the backup #{user_choice}?")
`mysql --host=#{host} --user=#{username} --password=#{password} --execute="DROP DATABASE IF EXISTS #{user_choice};
CREATE DATABASE #{user_choice};"`
else
prompt.say('Database restoration cancelled.')
exit 0
end
# Create a progress bar
total_size = File.size(user_choice)
bar = TTY::ProgressBar.new('Restoring [:bar] :percent', total: total_size)
# Open the SQL file and the MySQL process
File.open(user_choice) do |file|
sql_chunk = ''
file.each_line do |line|
sql_chunk += line
# If the line ends with a semicolon and the chunk size is over 1024 bytes, execute the chunk
next unless line.strip.end_with?(';') && sql_chunk.bytesize >= 1024
# Write the chunk to the MySQL process
`mysql --host=#{host} --user=#{username} --password=#{password} #{user_choice} --execute="#{sql_chunk}"`
# Update the progress bar
bar.advance(sql_chunk.bytesize)
sql_chunk = ''
end
# Write the remaining SQL commands to the MySQL process
unless sql_chunk.empty?
`mysql --host=#{host} --user=#{username} --password=#{password} #{user_choice} --execute="#{sql_chunk}"`
bar.advance(sql_chunk.bytesize)
end
end
if $CHILD_STATUS.success?
prompt.say('Import completed successfully.')
else
prompt.say("Error during import: #{output}")
end
prompt.say("Backup #{user_choice} restored successfully.")