2013-06-15 11:29:20 +02:00
require " digest/sha1 "
2016-04-11 20:42:40 +02:00
################################################################################
# gather #
################################################################################
task " uploads:gather " = > :environment do
require " db_helper "
2016-04-12 16:00:25 +02:00
ENV [ " RAILS_DB " ] ? gather_uploads : gather_uploads_for_all_sites
end
def gather_uploads_for_all_sites
RailsMultisite :: ConnectionManagement . each_connection { gather_uploads }
end
2016-04-13 16:33:00 +02:00
def file_exists? ( path )
File . exists? ( path ) && File . size ( path ) > 0
rescue
false
end
2016-04-12 16:00:25 +02:00
def gather_uploads
2016-04-11 20:42:40 +02:00
public_directory = " #{ Rails . root } /public "
current_db = RailsMultisite :: ConnectionManagement . current_db
puts " " , " Gathering uploads for ' #{ current_db } '... " , " "
2016-04-11 21:17:33 +02:00
Upload . where ( " url ~ '^ \ /uploads \ /' " )
. where ( " url !~ '^ \ /uploads \ / #{ current_db } ' " )
. find_each do | upload |
2016-04-11 20:42:40 +02:00
begin
old_db = upload . url [ / ^ \/ uploads \/ ([^ \/ ]+) \/ / , 1 ]
from = upload . url . dup
to = upload . url . sub ( " /uploads/ #{ old_db } / " , " /uploads/ #{ current_db } / " )
source = " #{ public_directory } #{ from } "
destination = " #{ public_directory } #{ to } "
2016-04-13 16:33:00 +02:00
# create destination directory & copy file unless it already exists
unless file_exists? ( destination )
` mkdir -p ' #{ File . dirname ( destination ) } ' `
` cp --link ' #{ source } ' ' #{ destination } ' `
end
2016-04-11 20:42:40 +02:00
# ensure file has been succesfuly copied over
2016-04-13 16:33:00 +02:00
raise unless file_exists? ( destination )
2016-04-11 20:42:40 +02:00
# remap links in db
DbHelper . remap ( from , to )
rescue
putc " ! "
else
putc " . "
end
end
puts " " , " Done! "
end
2015-05-25 17:59:00 +02:00
################################################################################
# backfill_shas #
################################################################################
2013-06-15 11:29:20 +02:00
task " uploads:backfill_shas " = > :environment do
RailsMultisite :: ConnectionManagement . each_connection do | db |
2015-06-10 17:19:58 +02:00
puts " Backfilling #{ db } ... "
Upload . where ( sha1 : nil ) . find_each do | u |
begin
path = Discourse . store . path_for ( u )
u . sha1 = Digest :: SHA1 . file ( path ) . hexdigest
u . save!
2013-06-15 11:29:20 +02:00
putc " . "
2015-06-10 17:19:58 +02:00
rescue Errno :: ENOENT
putc " X "
2013-06-15 11:29:20 +02:00
end
end
end
2015-06-10 17:19:58 +02:00
puts " " , " Done "
2013-06-15 11:29:20 +02:00
end
2014-06-24 15:35:15 +02:00
2015-05-25 17:59:00 +02:00
################################################################################
# migrate_from_s3 #
################################################################################
2014-06-24 15:35:15 +02:00
task " uploads:migrate_from_s3 " = > :environment do
2015-05-25 17:59:00 +02:00
require " file_store/local_store "
require " file_helper "
2014-06-24 15:35:15 +02:00
2015-05-26 16:39:41 +02:00
max_file_size_kb = [ SiteSetting . max_image_size_kb , SiteSetting . max_attachment_size_kb ] . max . kilobytes
2014-06-24 15:35:15 +02:00
local_store = FileStore :: LocalStore . new
2015-03-18 18:23:55 +01:00
puts " Deleting all optimized images... "
puts
OptimizedImage . destroy_all
2014-06-24 15:35:15 +02:00
puts " Migrating uploads from S3 to local storage "
puts
2015-05-12 09:37:48 +02:00
Upload . find_each do | upload |
2014-06-24 15:35:15 +02:00
# remove invalid uploads
if upload . url . blank?
upload . destroy!
next
end
# no need to download an upload twice
if local_store . has_been_uploaded? ( upload . url )
2015-05-25 17:59:00 +02:00
putc " . "
2014-06-24 15:35:15 +02:00
next
end
# try to download the upload
begin
# keep track of the previous url
previous_url = upload . url
# fix the name of pasted images
upload . original_filename = " blob.png " if upload . original_filename == " blob "
# download the file (in a temp file)
2015-05-26 16:39:41 +02:00
temp_file = FileHelper . download ( " http: " + previous_url , max_file_size_kb , " from_s3 " )
2014-06-24 15:35:15 +02:00
# store the file locally
upload . url = local_store . store_upload ( temp_file , upload )
# save the new url
if upload . save
# update & rebake the posts (if any)
Post . where ( " raw ILIKE ? " , " % #{ previous_url } % " ) . find_each do | post |
2015-03-18 18:23:55 +01:00
post . raw = post . raw . gsub ( previous_url , upload . url )
post . save
2014-06-24 15:35:15 +02:00
end
2015-05-25 17:59:00 +02:00
putc " # "
2014-06-24 15:35:15 +02:00
else
2015-05-25 17:59:00 +02:00
putc " X "
2014-06-24 15:35:15 +02:00
end
# close the temp_file
temp_file . close! if temp_file . respond_to? :close!
rescue
2015-05-25 17:59:00 +02:00
putc " X "
2014-06-24 15:35:15 +02:00
end
end
puts
end
2014-09-29 18:31:53 +02:00
2015-05-25 17:59:00 +02:00
################################################################################
# migrate_to_s3 #
################################################################################
task " uploads:migrate_to_s3 " = > :environment do
require " file_store/s3_store "
require " file_store/local_store "
2015-06-12 12:02:36 +02:00
require " db_helper "
2015-05-25 17:59:00 +02:00
ENV [ " RAILS_DB " ] ? migrate_to_s3 : migrate_to_s3_all_sites
end
def migrate_to_s3_all_sites
RailsMultisite :: ConnectionManagement . each_connection { migrate_to_s3 }
end
def migrate_to_s3
# make sure s3 is enabled
if ! SiteSetting . enable_s3_uploads
puts " You must enable s3 uploads before running that task "
return
end
db = RailsMultisite :: ConnectionManagement . current_db
puts " Migrating uploads to S3 ( #{ SiteSetting . s3_upload_bucket } ) for ' #{ db } '... "
# will throw an exception if the bucket is missing
s3 = FileStore :: S3Store . new
local = FileStore :: LocalStore . new
# Migrate all uploads
Upload . where . not ( sha1 : nil )
. where ( " url NOT LIKE ' #{ s3 . absolute_base_url } %' " )
. find_each do | upload |
# remove invalid uploads
if upload . url . blank?
upload . destroy!
next
end
# store the old url
from = upload . url
# retrieve the path to the local file
path = local . path_for ( upload )
# make sure the file exists locally
2015-11-16 11:39:38 +01:00
if ! path or ! File . exists? ( path )
2015-05-25 17:59:00 +02:00
putc " X "
next
end
begin
file = File . open ( path )
content_type = ` file --mime-type -b #{ path } ` . strip
to = s3 . store_upload ( file , upload , content_type )
rescue
putc " X "
next
ensure
file . try ( :close! ) rescue nil
end
# remap the URL
2015-06-12 12:02:36 +02:00
DbHelper . remap ( from , to )
2015-05-25 17:59:00 +02:00
putc " . "
end
end
################################################################################
# clean_up #
################################################################################
2014-09-29 18:31:53 +02:00
task " uploads:clean_up " = > :environment do
RailsMultisite :: ConnectionManagement . each_connection do | db |
puts " Cleaning up uploads and thumbnails for ' #{ db } '... "
if Discourse . store . external?
puts " This task only works for internal storages. "
next
end
public_directory = " #{ Rails . root } /public "
##
## DATABASE vs FILE SYSTEM
##
# uploads & avatars
2015-05-12 09:37:48 +02:00
Upload . find_each do | upload |
2014-09-29 18:31:53 +02:00
path = " #{ public_directory } #{ upload . url } "
if ! File . exists? ( path )
upload . destroy rescue nil
putc " # "
else
putc " . "
end
end
# optimized images
2015-05-12 09:37:48 +02:00
OptimizedImage . find_each do | optimized_image |
2014-09-29 18:31:53 +02:00
path = " #{ public_directory } #{ optimized_image . url } "
if ! File . exists? ( path )
optimized_image . destroy rescue nil
putc " # "
else
putc " . "
end
end
##
## FILE SYSTEM vs DATABASE
##
uploads_directory = " #{ public_directory } /uploads/ #{ db } "
# avatars (no avatar should be stored in that old directory)
FileUtils . rm_rf ( " #{ uploads_directory } /avatars " ) rescue nil
# uploads
Dir . glob ( " #{ uploads_directory } /*/*.* " ) . each do | f |
url = " /uploads/ #{ db } / " << f . split ( " /uploads/ #{ db } / " ) [ 1 ]
if ! Upload . where ( url : url ) . exists?
FileUtils . rm ( f ) rescue nil
putc " # "
else
putc " . "
end
end
# optimized images
Dir . glob ( " #{ uploads_directory } /_optimized/*/*/*.* " ) . each do | f |
url = " /uploads/ #{ db } /_optimized/ " << f . split ( " /uploads/ #{ db } /_optimized/ " ) [ 1 ]
if ! OptimizedImage . where ( url : url ) . exists?
FileUtils . rm ( f ) rescue nil
putc " # "
else
putc " . "
end
end
puts
end
end
2015-05-11 10:30:22 +10:00
2015-05-25 17:59:00 +02:00
################################################################################
# missing #
################################################################################
2015-05-11 10:30:22 +10:00
# list all missing uploads and optimized images
task " uploads:missing " = > :environment do
public_directory = " #{ Rails . root } /public "
RailsMultisite :: ConnectionManagement . each_connection do | db |
if Discourse . store . external?
puts " This task only works for internal storages. "
next
end
2015-05-12 09:37:48 +02:00
Upload . find_each do | upload |
2015-05-11 10:30:22 +10:00
# could be a remote image
2015-05-12 09:28:43 +02:00
next unless upload . url =~ / ^ \/ [^ \/ ] /
2015-05-11 10:30:22 +10:00
path = " #{ public_directory } #{ upload . url } "
bad = true
begin
bad = false if File . size ( path ) != 0
rescue
# something is messed up
end
puts path if bad
end
2015-05-12 09:37:48 +02:00
OptimizedImage . find_each do | optimized_image |
2015-05-11 10:30:22 +10:00
# remote?
2015-05-12 09:28:43 +02:00
next unless optimized_image . url =~ / ^ \/ [^ \/ ] /
2015-05-11 10:30:22 +10:00
path = " #{ public_directory } #{ optimized_image . url } "
bad = true
begin
bad = false if File . size ( path ) != 0
rescue
# something is messed up
end
puts path if bad
end
end
end
2015-05-11 12:59:50 +02:00
2015-05-25 17:59:00 +02:00
################################################################################
# regenerate_missing_optimized #
################################################################################
2015-05-11 12:59:50 +02:00
# regenerate missing optimized images
task " uploads:regenerate_missing_optimized " = > :environment do
2015-05-11 16:19:16 +02:00
ENV [ " RAILS_DB " ] ? regenerate_missing_optimized : regenerate_missing_optimized_all_sites
end
def regenerate_missing_optimized_all_sites
RailsMultisite :: ConnectionManagement . each_connection { regenerate_missing_optimized }
end
def regenerate_missing_optimized
2015-05-11 19:07:39 +02:00
db = RailsMultisite :: ConnectionManagement . current_db
puts " Regenerating missing optimized images for ' #{ db } '... "
2015-05-11 12:59:50 +02:00
if Discourse . store . external?
puts " This task only works for internal storages. "
return
end
public_directory = " #{ Rails . root } /public "
missing_uploads = Set . new
OptimizedImage . includes ( :upload )
. where ( " LENGTH(COALESCE(url, '')) > 0 " )
. where ( " width > 0 AND height > 0 " )
. find_each do | optimized_image |
2015-05-11 19:07:39 +02:00
upload = optimized_image . upload
2015-05-12 09:28:43 +02:00
next unless optimized_image . url =~ / ^ \/ [^ \/ ] /
next unless upload . url =~ / ^ \/ [^ \/ ] /
2015-05-11 15:41:52 +02:00
2015-05-11 12:59:50 +02:00
thumbnail = " #{ public_directory } #{ optimized_image . url } "
2015-05-11 19:07:39 +02:00
original = " #{ public_directory } #{ upload . url } "
2015-05-11 12:59:50 +02:00
if ! File . exists? ( thumbnail ) || File . size ( thumbnail ) < = 0
2015-05-11 17:03:48 +02:00
# make sure the original image exists locally
2015-05-11 19:07:39 +02:00
if ( ! File . exists? ( original ) || File . size ( original ) < = 0 ) && upload . origin . present?
2015-05-11 17:03:48 +02:00
# try to fix it by redownloading it
begin
2015-05-11 19:07:39 +02:00
downloaded = FileHelper . download ( upload . origin , SiteSetting . max_image_size_kb . kilobytes , " discourse-missing " , true ) rescue nil
if downloaded && downloaded . size > 0
FileUtils . mkdir_p ( File . dirname ( original ) )
File . open ( original , " wb " ) { | f | f . write ( downloaded . read ) }
end
2015-05-11 17:03:48 +02:00
ensure
downloaded . try ( :close! ) if downloaded . respond_to? ( :close! )
end
end
2015-05-11 19:07:39 +02:00
if File . exists? ( original ) && File . size ( original ) > 0
2015-05-11 12:59:50 +02:00
FileUtils . mkdir_p ( File . dirname ( thumbnail ) )
2015-05-11 19:07:39 +02:00
OptimizedImage . resize ( original , thumbnail , optimized_image . width , optimized_image . height )
2015-05-11 12:59:50 +02:00
putc " # "
else
2015-05-11 19:07:39 +02:00
missing_uploads << original
2015-05-11 12:59:50 +02:00
putc " X "
end
else
putc " . "
end
end
puts " " , " Done "
if missing_uploads . size > 0
puts " Missing uploads: "
missing_uploads . sort . each { | u | puts u }
end
end
2015-05-19 12:31:51 +02:00
2015-05-25 17:59:00 +02:00
################################################################################
2015-06-12 12:02:36 +02:00
# migrate_to_new_scheme #
2015-05-25 17:59:00 +02:00
################################################################################
2015-06-12 12:02:36 +02:00
task " uploads:start_migration " = > :environment do
SiteSetting . migrate_to_new_scheme = true
puts " Migration started! "
2015-05-19 12:31:51 +02:00
end
2015-06-12 12:02:36 +02:00
task " uploads:stop_migration " = > :environment do
SiteSetting . migrate_to_new_scheme = false
puts " Migration stoped! "
2015-05-19 12:31:51 +02:00
end