activestorage-sftp 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 53fb9c020a024d48787fccfbdd311b88f1eef5b6
4
- data.tar.gz: 396a0b2ffc2cde6bb0f88bf169cdbf2932110855
3
+ metadata.gz: 86f61c010b69d4972dbd32fd3eeec9ad415d5051
4
+ data.tar.gz: 3d82a1f0208e565c98a791506c8c11619faa7a4e
5
5
  SHA512:
6
- metadata.gz: 930f62d8856cfda94bfa90285ab6c59fc5d3d4ccdd14ce851a2dece838e5f78114438c87195d2c467738aa01031be7eea591e1f7244f40222507aaa52f30ffbe
7
- data.tar.gz: 5a7f980c04c712750beec2f4a328bc676d0be570cc9c52854c46c2e053d0f99fe69f6a47b955fb3157eb554d5efff98579d9bf0b1cb14099ce8718d2b57689b1
6
+ metadata.gz: 6ccd930d3aa5c2cd6f26dd6476b64000e935e1984f01536efd7abd46191f587ed48defdeac2f74efe3ee7155d1a45fe79ce14034613ac3b6c84002192bbca61c
7
+ data.tar.gz: cd1c9b1f1983508558e833af339105e2089adb47e23f70b4903c775beac5d75a2bc67c06d844908c15fafebfa183debce548b0df38d60b05f5459e83d81d286b
data/README.md CHANGED
@@ -20,8 +20,8 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
-
24
23
  Each application server saves blobs to file server through SFTP:
24
+
25
25
  ```yml
26
26
  # config/storage.yml
27
27
  sftp:
@@ -30,7 +30,9 @@ sftp:
30
30
  root: /var/www/proj/shared/storage
31
31
  host: file.intranet
32
32
  public_host: https://file.internet
33
+ password: <%= ENV['PASSWORD'] %> # optional
33
34
  ```
35
+
34
36
  File server serves blobs using DiskService:
35
37
  ```yml
36
38
  # config/storage.yml
@@ -54,6 +56,36 @@ sftp:
54
56
  host: secure.backup
55
57
  ```
56
58
 
59
+ ### Further configuration options:
60
+
61
+ #### use_public_url: Generate plain ("dumb") URLs of upload server
62
+
63
+ By default the generated URLs will include parameters for `content_disposition`, expiration hints etc. A generated blobs URL might thus look like:
64
+
65
+ https://publichost/PATH/rails/active_storage/disk/hash-hash/name.JPG?content_type=image%2Fjpeg&disposition=inline%3B+filename%3D
66
+
67
+ If you prefer simple URLs like
68
+
69
+ https://publichost/PATH/hash
70
+
71
+ you can set a configuration option:
72
+
73
+ ```yml
74
+ # config/storage.yml
75
+ sftp:
76
+ simple_public_urls: true # defaults to false
77
+ ```
78
+
79
+
80
+ #### verify_via_http_get: Faster existence verification via HTTP GET
81
+
82
+ The default way of verifying that a blob does exist is to login to the sftp server and stat() the relevant file. This is done e.g. before re-transforming and uploading an image variant. While other "caching" solutions exist to speed up that process, a simple and efficient way of verifying the existence of a file is to query the relevant server with an HTTP HEAD request. Depending on the setup this might not always be a viable way, so it can be switched on with a configuration option.
83
+
84
+ ```yml
85
+ # config/storage.yml
86
+ sftp:
87
+ verify_via_http_get: true # defaults to false
88
+ ```
57
89
 
58
90
  ## Contributing
59
91
 
@@ -23,8 +23,8 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.add_dependency "rails", "> 5.2.0"
27
- spec.add_dependency "net-sftp", "~> 2.1.2"
26
+ spec.add_dependency "rails", ">= 5.2.0"
27
+ spec.add_dependency "net-sftp", ">= 2.1.2"
28
28
 
29
29
  spec.add_development_dependency "bundler", "~> 1.17"
30
30
  spec.add_development_dependency "rake", "~> 10.0"
@@ -11,12 +11,15 @@ module ActiveStorage
11
11
 
12
12
  attr_reader :host, :user, :root, :public_host, :public_root
13
13
 
14
- def initialize(host:, user:, public_host: nil, root: './', public_root: nil)
14
+ def initialize(host:, user:, public_host: nil, root: './', public_root: './', password: nil, simple_public_urls: false, verify_via_http_get: false)
15
15
  @host = host
16
16
  @user = user
17
17
  @root = root
18
18
  @public_host = public_host
19
19
  @public_root = public_root
20
+ @password = password
21
+ @simple_public_urls = simple_public_urls
22
+ @verify_via_http_get = verify_via_http_get
20
23
  end
21
24
 
22
25
  def upload(key, io, checksum: nil, **)
@@ -116,37 +119,76 @@ module ActiveStorage
116
119
  def exist?(key)
117
120
  instrument :exist, key: key do |payload|
118
121
  answer = false
119
- through_sftp do |sftp|
120
- answer = sftp.stat(path_for(key)).present?
122
+
123
+ if @verify_via_http_get
124
+ uri = URI([public_host, relative_folder_for(key), key].join('/'))
125
+ request = Net::HTTP.new uri.host
126
+ response = request.request_head uri.path
127
+
128
+ answer = (response.code.to_i == 200)
129
+ else
130
+ through_sftp do |sftp|
131
+ # TODO Probably adviseable to let some more exceptions go through
132
+ begin
133
+ sftp.stat!(path_for(key)) do |response|
134
+ answer = response.ok?
135
+ end
136
+ rescue Net::SFTP::StatusException => e
137
+ answer = false
138
+ end
139
+ end
121
140
  end
141
+
122
142
  payload[:exist] = answer
123
143
  answer
124
144
  end
125
145
  end
126
146
 
127
147
  def url(key, expires_in:, filename:, disposition:, content_type:)
128
- instrument :url, key: key do |payload|
129
- raise "public_host not defined." unless public_host
130
- content_disposition = content_disposition_with(type: disposition, filename: filename)
131
- verified_key_with_expiration = ActiveStorage.verifier.generate(
132
- {
133
- key: key,
134
- disposition: content_disposition,
135
- content_type: content_type
136
- },
137
- { expires_in: expires_in,
138
- purpose: :blob_key }
139
- )
140
-
141
- generated_url = url_helpers.rails_disk_service_url(verified_key_with_expiration,
142
- host: public_host,
143
- disposition: content_disposition,
144
- content_type: content_type,
145
- filename: filename
146
- )
147
- payload[:url] = generated_url
148
-
149
- generated_url
148
+ if @simple_public_urls
149
+ public_url(key)
150
+ else
151
+ classic_url(key,
152
+ expires_in: expires_in,
153
+ filename: filename,
154
+ disposition: disposition,
155
+ content_type: content_type)
156
+ end
157
+ end
158
+
159
+ def classic_url(key, expires_in:, filename:, disposition:, content_type:)
160
+ instrument :url, key: key do |payload|
161
+ raise NotConfigured, "public_host not defined." unless public_host
162
+ content_disposition = content_disposition_with(type: disposition, filename: filename)
163
+ verified_key_with_expiration = ActiveStorage.verifier.generate(
164
+ {
165
+ key: key,
166
+ disposition: content_disposition,
167
+ content_type: content_type
168
+ },
169
+ {
170
+ expires_in: expires_in,
171
+ purpose: :blob_key
172
+ }
173
+ )
174
+
175
+ generated_url = url_helpers.rails_disk_service_url(verified_key_with_expiration,
176
+ host: public_host,
177
+ disposition: content_disposition,
178
+ content_type: content_type,
179
+ filename: filename
180
+ )
181
+ payload[:url] = generated_url
182
+ generated_url
183
+ end
184
+ end
185
+
186
+ def public_url(key)
187
+ instrument :url, key: key do |payload|
188
+ raise NotConfigured, "public_host not defined." unless public_host
189
+ generated_url = File.join(public_host, public_root, path_for(key), key)
190
+ payload[:url] = generated_url
191
+ generated_url
150
192
  end
151
193
  end
152
194
 
@@ -183,7 +225,8 @@ module ActiveStorage
183
225
 
184
226
  protected
185
227
  def through_sftp(&block)
186
- Net::SFTP.start(@host, @user) do |sftp|
228
+ opts = @password.present? ? {password: @password} : {}
229
+ Net::SFTP.start(@host, @user, opts) do |sftp|
187
230
  block.call(sftp)
188
231
  end
189
232
  end
@@ -197,18 +240,20 @@ module ActiveStorage
197
240
  end
198
241
 
199
242
  def mkdir_for(key)
243
+ mkdir_p_for(path_for key)
244
+ end
245
+
246
+ def mkdir_p_for(abs_path)
200
247
  through_sftp do |sftp|
201
- sub_folder = File.join root, key[0..1]
202
- begin
203
- sftp.opendir!(sub_folder)
204
- rescue => e
205
- sftp.mkdir!(sub_folder)
206
- end
207
- sub_folder = File.join(sub_folder, key[2..3])
208
- begin
209
- sftp.opendir!(sub_folder)
210
- rescue => e
211
- sftp.mkdir!(sub_folder)
248
+ base_path = ''
249
+ abs_path.split('/')[0...-1].each do |path|
250
+ sub_folder = File.join(base_path, path)
251
+ begin
252
+ sftp.opendir!(sub_folder)
253
+ rescue => e
254
+ sftp.mkdir!(sub_folder)
255
+ end
256
+ base_path = sub_folder
212
257
  end
213
258
  end
214
259
  end
@@ -6,5 +6,8 @@ module ActiveStorage
6
6
  class Error < StandardError; end
7
7
  class ChunkSizeError < Error; end
8
8
  class SFTPResponseError < Error; end
9
+ class NotConfigured < Error; end
9
10
  end
10
11
  end
12
+
13
+
@@ -1,5 +1,5 @@
1
1
  module ActiveStorage
2
2
  module SFTP
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,41 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activestorage-sftp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - treenewbee
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-16 00:00:00.000000000 Z
11
+ date: 2019-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 5.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: net-sftp
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 2.1.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 2.1.2
41
41
  - !ruby/object:Gem::Dependency