class Projects::LfsStorageController < Projects::GitHttpClientController include LfsRequest include WorkhorseRequest skip_before_action :verify_workhorse_api!, only: [:download, :upload_finalize] def download lfs_object = LfsObject.find_by_oid(oid) unless lfs_object && lfs_object.file.exists? render_lfs_not_found return end send_file lfs_object.file.path, content_type: "application/octet-stream" end def upload_authorize set_workhorse_internal_api_content_type render json: Gitlab::Workhorse.lfs_upload_ok(oid, size) end def upload_finalize unless tmp_filename render_lfs_forbidden return end if store_file(oid, size, tmp_filename) head 200 else render plain: 'Unprocessable entity', status: 422 end end private def download_request? action_name == 'download' end def upload_request? %w[upload_authorize upload_finalize].include? action_name end def oid params[:oid].to_s end def size params[:size].to_i end def tmp_filename name = request.headers['X-Gitlab-Lfs-Tmp'] return if name.include?('/') return unless oid.present? && name.start_with?(oid) name end def store_file(oid, size, tmp_file) # Define tmp_file_path early because we use it in "ensure" tmp_file_path = File.join("#{Gitlab.config.lfs.storage_path}/tmp/upload", tmp_file) object = LfsObject.find_or_create_by(oid: oid, size: size) file_exists = object.file.exists? || move_tmp_file_to_storage(object, tmp_file_path) file_exists && link_to_project(object) ensure FileUtils.rm_f(tmp_file_path) end def move_tmp_file_to_storage(object, path) File.open(path) do |f| object.file = f end object.file.store! object.save end def link_to_project(object) if object && !object.projects.exists?(storage_project.id) object.projects << storage_project object.save end end end