Skip to content

Commit 2bcabef

Browse files
committed
Improve rake db commands
1 parent 8e79c5f commit 2bcabef

File tree

1 file changed

+138
-25
lines changed

1 file changed

+138
-25
lines changed

lib/tasks/db.rake

Lines changed: 138 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,50 +18,163 @@ namespace :db do
1818
end
1919
end
2020

21+
task import_from_existing_dump: [:environment, :create] do
22+
config = db_config
23+
env = { "PGPASSWORD" => config[:password] }
24+
puts "[1/1] Mounting backup on local database"
25+
system(env, "pg_restore", "--clean", "--verbose", "--no-acl", "--no-owner",
26+
"--host", config[:host], "--username", config[:username], "--dbname", config[:database],
27+
"tmp/latest.dump")
28+
puts "Done."
29+
end
30+
2131
desc "Dumps the database to db/APP_NAME.dump"
2232
task dump: :environment do
23-
cmd = nil
24-
with_config do |app, host, db, user|
25-
cmd = "pg_dump --host #{host} --username #{user} --verbose --clean --no-owner --no-acl --format=c #{db} > #{Rails.root}/db/#{app}.dump"
26-
end
33+
config = db_config
34+
env = { "PGPASSWORD" => config[:password] }
35+
cmd = "pg_dump --host #{config[:host]} --username #{config[:username]} --verbose --clean --no-owner --no-acl --format=c #{config[:database]} > #{Rails.root}/db/#{config[:app]}.dump"
36+
puts cmd
37+
system(env, cmd)
38+
end
39+
40+
desc "Dumps and compresses the database to db/APP_NAME.dump.gz"
41+
task dump_compressed: :environment do
42+
config = db_config
43+
env = { "PGPASSWORD" => config[:password] }
44+
cmd = "pg_dump --host #{config[:host]} --username #{config[:username]} --verbose --clean --no-owner --no-acl --format=c #{config[:database]} | gzip > #{Rails.root}/db/#{config[:app]}.dump.gz"
2745
puts cmd
28-
exec cmd
46+
system(env, cmd)
2947
end
3048

3149
desc "Restores the database dump at db/APP_NAME.dump."
3250
task restore: :environment do
33-
cmd = nil
34-
with_config do |app, host, db, user|
35-
cmd = "pg_restore --verbose --host #{host} --username #{user} --clean --no-owner --no-acl --dbname #{db} #{Rails.root}/db/#{app}.dump"
36-
end
51+
abort "Refusing to restore dump in production." if Rails.env.production?
52+
53+
config = db_config
54+
env = { "PGPASSWORD" => config[:password] }
3755
Rake::Task["db:drop"].invoke
3856
Rake::Task["db:create"].invoke
57+
cmd = "pg_restore --verbose --host #{config[:host]} --username #{config[:username]} --clean --no-owner --no-acl --dbname #{config[:database]} #{Rails.root}/db/#{config[:app]}.dump"
3958
puts cmd
40-
exec cmd
59+
system(env, cmd)
60+
end
61+
62+
desc "Restores the compressed dump at db/APP_NAME.dump.gz."
63+
task restore_compressed: :environment do
64+
abort "Refusing to restore compressed dump in production." if Rails.env.production?
65+
66+
config = db_config
67+
env = { "PGPASSWORD" => config[:password] }
68+
Rake::Task["db:drop"].invoke
69+
Rake::Task["db:create"].invoke
70+
cmd = "gunzip -c #{Rails.root}/db/#{config[:app]}.dump.gz | pg_restore --verbose --host #{config[:host]} --username #{config[:username]} --clean --no-owner --no-acl --dbname #{config[:database]}"
71+
puts cmd
72+
system(env, cmd)
73+
end
74+
75+
desc "Anonymizes sensitive data in the database"
76+
task anonymize: :environment do
77+
abort "Refusing to anonymize in production." if Rails.env.production?
78+
79+
puts "Anonymizing users..."
80+
User.find_each do |user|
81+
puts "Anonymizing User##{user.id} (#{user.username})..."
82+
user.username = unique_value(User, :username, "user#{user.id}", exclude_id: user.id)
83+
user.email = unique_value(User, :email, "user#{user.id}@example.invalid", exclude_id: user.id)
84+
user.password = "password"
85+
user.password_confirmation = "password"
86+
user.reset_password_token = nil
87+
user.reset_password_sent_at = nil
88+
user.remember_created_at = nil
89+
user.current_sign_in_at = nil
90+
user.last_sign_in_at = nil
91+
user.current_sign_in_ip = nil
92+
user.last_sign_in_ip = nil
93+
user.provider = nil
94+
user.uid = nil
95+
user.discord_avatar_url = nil
96+
user.token = nil
97+
user.token_expiry = nil
98+
user.save!(validate: false)
99+
end
100+
101+
puts "Scrubbing attachment filenames..."
102+
scrub_shrine_metadata(Blueprint, :cover_picture_data, prefix: "cover")
103+
scrub_shrine_metadata(Blueprint, :blueprint_file_data, prefix: "blueprint")
104+
scrub_shrine_metadata(Picture, :picture_data, prefix: "picture")
105+
scrub_active_storage_filenames
106+
107+
puts "Done."
41108
end
42109

43110
desc "Hides private information in the database"
44111
task privatize: :environment do
45-
if Rails.env.development? # Don't want that in production whatever the reason
46-
User.all.each do |u|
47-
puts "Privatizing User##{u.id} (#{u.username})..."
48-
u.username = "User##{u.id}"
49-
u.email = "user##{u.id}@test.com"
50-
u.password = "password"
51-
u.password_confirmation = "password"
52-
u.save!
53-
end
54-
puts "Done."
55-
end
112+
Rake::Task["db:anonymize"].invoke
56113
end
57114

58115
private
59116

117+
def db_config
118+
config = Rails.configuration.database_configuration[Rails.env]
119+
{
120+
app: Rails.application.class.module_parent_name.underscore,
121+
host: config["host"] || "localhost",
122+
database: config["database"],
123+
username: config["username"] || config["user"] || ENV["PG_USER"] || "dev",
124+
password: config["password"] || ENV["PG_PASS"] || "password",
125+
}
126+
end
127+
60128
def with_config
61-
yield Rails.application.class.module_parent_name.underscore,
62-
ActiveRecord::Base.connection_config[:host],
63-
ActiveRecord::Base.connection_config[:database],
64-
ActiveRecord::Base.connection_config[:username]
129+
config = db_config
130+
yield config[:app], config[:host], config[:database], config[:username]
131+
end
132+
133+
def scrub_shrine_metadata(scope, column, prefix:)
134+
scope.find_each do |record|
135+
data = record.public_send(column)
136+
next if data.blank?
137+
138+
parsed = begin
139+
JSON.parse(data)
140+
rescue StandardError
141+
nil
142+
end
143+
next unless parsed.is_a?(Hash)
144+
145+
metadata = parsed["metadata"] || {}
146+
filename = metadata["filename"].to_s
147+
next if filename.empty?
148+
149+
ext = File.extname(filename)
150+
metadata["filename"] = "#{prefix}-#{record.id}#{ext}"
151+
parsed["metadata"] = metadata
152+
record.update_column(column, parsed.to_json)
153+
end
154+
end
155+
156+
def scrub_active_storage_filenames
157+
return unless defined?(ActiveStorage::Blob)
158+
159+
ActiveStorage::Blob.find_each do |blob|
160+
filename = blob.filename.to_s
161+
ext = File.extname(filename)
162+
blob.update!(filename: "file-#{blob.id}#{ext}")
163+
end
164+
end
165+
166+
def unique_value(scope, column, base, exclude_id:)
167+
value = base
168+
relation = scope.where.not(id: exclude_id)
169+
170+
if base.include?("@")
171+
local, domain = base.split("@", 2)
172+
value = "#{local}-#{SecureRandom.hex(3)}@#{domain}" while relation.exists?(column => value)
173+
else
174+
value = "#{base}-#{SecureRandom.hex(3)}" while relation.exists?(column => value)
175+
end
176+
177+
value
65178
end
66179

67180
desc "Analyze and reindex the database"

0 commit comments

Comments
 (0)