summaryrefslogtreecommitdiff
path: root/app/uploaders/gitlab_uploader.rb
blob: 654bb15378c5542e7a0e509b4a13915c3167da5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# frozen_string_literal: true

class GitlabUploader < CarrierWave::Uploader::Base
  include ContentTypeWhitelist::Concern

  class_attribute :options

  class << self
    # DSL setter
    def storage_options(options)
      self.options = options
    end

    def root
      options.storage_path
    end

    # represent the directory namespacing at the class level
    def base_dir
      options.fetch('base_dir', '')
    end

    def file_storage?
      storage == CarrierWave::Storage::File
    end

    def absolute_path(upload_record)
      File.join(root, upload_record.path)
    end
  end

  storage_options Gitlab.config.uploads

  delegate :base_dir, :file_storage?, to: :class

  def initialize(model, mounted_as = nil, **uploader_context)
    super(model, mounted_as)
  end

  def file_cache_storage?
    cache_storage.is_a?(CarrierWave::Storage::File)
  end

  def move_to_cache
    file_storage?
  end

  def move_to_store
    file_storage?
  end

  def exists?
    file.present?
  end

  def cache_dir
    File.join(root, base_dir, 'tmp/cache')
  end

  def work_dir
    File.join(root, base_dir, 'tmp/work')
  end

  def filename
    super || file&.filename
  end

  def relative_path
    return path if pathname.relative?

    pathname.relative_path_from(Pathname.new(root))
  end

  def model_valid?
    !!model
  end

  def local_url
    File.join('/', self.class.base_dir, dynamic_segment, filename)
  end

  def cached_size
    size
  end

  def open
    stream =
      if file_storage?
        File.open(path, "rb") if path
      else
        ::Gitlab::HttpIO.new(url, cached_size) if url
      end

    return unless stream
    return stream unless block_given?

    begin
      yield(stream)
    ensure
      stream.close
    end
  end

  # Used to replace an existing upload with another +file+ without modifying stored metadata
  # Use this method only to repair/replace an existing upload, or to upload to a Geo secondary node
  #
  # @param [CarrierWave::SanitizedFile] file that will replace existing upload
  # @return CarrierWave::SanitizedFile
  def replace_file_without_saving!(file)
    raise ArgumentError, 'should be a CarrierWave::SanitizedFile' unless file.is_a? CarrierWave::SanitizedFile

    storage.store!(file)
  end

  private

  # Designed to be overridden by child uploaders that have a dynamic path
  # segment -- that is, a path that changes based on mutable attributes of its
  # associated model
  #
  # For example, `FileUploader` builds the storage path based on the associated
  # project model's `path_with_namespace` value, which can change when the
  # project or its containing namespace is moved or renamed.
  def dynamic_segment
    raise(NotImplementedError)
  end

  # To prevent files from moving across filesystems, override the default
  # implementation:
  # http://github.com/carrierwaveuploader/carrierwave/blob/v1.0.0/lib/carrierwave/uploader/cache.rb#L181-L183
  def workfile_path(for_file = original_filename)
    # To be safe, keep this directory outside of the the cache directory
    # because calling CarrierWave.clean_cache_files! will remove any files in
    # the cache directory.
    File.join(work_dir, cache_id, version_name.to_s, for_file)
  end

  def pathname
    @pathname ||= Pathname.new(path)
  end
end