debian-mirror-gitlab/app/models/upload.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

181 lines
4.9 KiB
Ruby
Raw Normal View History

2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
2019-07-07 11:18:12 +05:30
class Upload < ApplicationRecord
2019-12-21 20:55:43 +05:30
include Checksummable
2021-12-11 22:18:48 +05:30
2017-08-17 22:00:37 +05:30
# Upper limit for foreground checksum processing
CHECKSUM_THRESHOLD = 100.megabytes
2017-09-10 17:25:29 +05:30
belongs_to :model, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
2017-08-17 22:00:37 +05:30
validates :size, presence: true
validates :path, presence: true
validates :model, presence: true
validates :uploader, presence: true
2018-12-13 13:39:08 +05:30
scope :with_files_stored_locally, -> { where(store: ObjectStorage::Store::LOCAL) }
scope :with_files_stored_remotely, -> { where(store: ObjectStorage::Store::REMOTE) }
2018-05-09 12:01:36 +05:30
2023-03-04 22:38:38 +05:30
before_save :calculate_checksum!, if: :foreground_checksummable?
2018-03-17 18:26:18 +05:30
# as the FileUploader is not mounted, the default CarrierWave ActiveRecord
# hooks are not executed and the file will not be deleted
after_destroy :delete_file!, if: -> { uploader_class <= FileUploader }
2023-03-04 22:38:38 +05:30
after_commit :schedule_checksum, if: :needs_checksum?
after_commit :update_project_statistics, on: [:create, :destroy], if: :project?
2017-08-17 22:00:37 +05:30
2019-02-15 15:39:39 +05:30
class << self
2020-01-01 13:55:28 +05:30
def inner_join_local_uploads_projects
upload_table = Upload.arel_table
project_table = Project.arel_table
join_statement = upload_table.project(upload_table[Arel.star])
.join(project_table)
.on(
upload_table[:model_type].eq('Project')
.and(upload_table[:model_id].eq(project_table[:id]))
.and(upload_table[:store].eq(ObjectStorage::Store::LOCAL))
)
joins(join_statement.join_sources)
end
2019-02-15 15:39:39 +05:30
##
# FastDestroyAll concerns
def begin_fast_destroy
{
Uploads::Local => Uploads::Local.new.keys(with_files_stored_locally),
Uploads::Fog => Uploads::Fog.new.keys(with_files_stored_remotely)
}
end
##
# FastDestroyAll concerns
2021-12-11 22:18:48 +05:30
def finalize_fast_destroy(items_to_remove)
items_to_remove.each do |store_class, keys|
store_class.new.delete_keys_async(keys)
2019-02-15 15:39:39 +05:30
end
end
end
2017-08-17 22:00:37 +05:30
def absolute_path
2019-07-31 22:56:46 +05:30
raise ObjectStorage::RemoteStoreError, _("Remote object has no absolute path.") unless local?
2017-08-17 22:00:37 +05:30
return path unless relative_path?
uploader_class.absolute_path(self)
end
2021-12-11 22:18:48 +05:30
def relative_path
uploader_class.relative_path(self)
end
2018-03-17 18:26:18 +05:30
def calculate_checksum!
self.checksum = nil
2019-12-21 20:55:43 +05:30
return unless needs_checksum?
2018-03-17 18:26:18 +05:30
2021-11-18 22:05:49 +05:30
self.checksum = self.class.sha256_hexdigest(absolute_path)
2018-03-17 18:26:18 +05:30
end
2017-08-17 22:00:37 +05:30
2019-12-21 20:55:43 +05:30
# Initialize the associated Uploader class with current model
#
# @param [String] mounted_as
# @return [GitlabUploader] one of the subclasses, defined at the model's uploader attribute
2018-05-09 12:01:36 +05:30
def build_uploader(mounted_as = nil)
uploader_class.new(model, mounted_as || mount_point).tap do |uploader|
2018-03-17 18:26:18 +05:30
uploader.upload = self
2019-12-21 20:55:43 +05:30
end
end
# Initialize the associated Uploader class with current model and
# retrieve existing file from the store to a local cache
#
# @param [String] mounted_as
# @return [GitlabUploader] one of the subclasses, defined at the model's uploader attribute
def retrieve_uploader(mounted_as = nil)
build_uploader(mounted_as).tap do |uploader|
2018-03-17 18:26:18 +05:30
uploader.retrieve_from_store!(identifier)
end
2017-08-17 22:00:37 +05:30
end
2019-12-21 20:55:43 +05:30
# This checks for existence of the upload on storage
#
# @return [Boolean] whether upload exists on storage
2017-08-17 22:00:37 +05:30
def exist?
2019-12-21 20:55:43 +05:30
exist = if local?
File.exist?(absolute_path)
else
retrieve_uploader.exists?
end
2018-12-13 13:39:08 +05:30
# Help sysadmins find missing upload files
if persisted? && !exist
2020-01-01 13:55:28 +05:30
exception = RuntimeError.new("Uploaded file does not exist")
Gitlab::ErrorTracking.track_exception(exception, self.attributes)
2019-07-31 22:56:46 +05:30
Gitlab::Metrics.counter(:upload_file_does_not_exist_total, _('The number of times an upload record could not find its file')).increment
2018-12-13 13:39:08 +05:30
end
exist
2017-08-17 22:00:37 +05:30
end
2018-03-17 18:26:18 +05:30
def uploader_context
{
identifier: identifier,
secret: secret
}.compact
end
2018-05-09 12:01:36 +05:30
def local?
store == ObjectStorage::Store::LOCAL
end
2019-12-21 20:55:43 +05:30
# Returns whether generating checksum is needed
#
# This takes into account whether file exists, if any checksum exists
# or if the storage has checksum generation code implemented
#
# @return [Boolean] whether generating a checksum is needed
def needs_checksum?
checksum.nil? && local? && exist?
end
2017-08-17 22:00:37 +05:30
private
2018-03-17 18:26:18 +05:30
def delete_file!
2019-12-21 20:55:43 +05:30
retrieve_uploader.remove!
2018-03-17 18:26:18 +05:30
end
def foreground_checksummable?
2019-12-21 20:55:43 +05:30
needs_checksum? && size <= CHECKSUM_THRESHOLD
2017-08-17 22:00:37 +05:30
end
def schedule_checksum
UploadChecksumWorker.perform_async(id)
end
def relative_path?
!path.start_with?('/')
end
def uploader_class
2019-12-21 20:55:43 +05:30
Object.const_get(uploader, false)
2017-08-17 22:00:37 +05:30
end
2018-03-17 18:26:18 +05:30
def identifier
File.basename(path)
end
def mount_point
super&.to_sym
end
2021-11-18 22:05:49 +05:30
def project?
model_type == "Project"
end
def update_project_statistics
ProjectCacheWorker.perform_async(model_id, [], [:uploads_size])
end
2017-08-17 22:00:37 +05:30
end
2019-12-04 20:38:33 +05:30
2021-06-08 01:23:25 +05:30
Upload.prepend_mod_with('Upload')