activestorage-ftp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 10f69e9a517b7eb01c78baf94a8cfbde39b60aecb34a2fdf70f6ce2bd862bdad
4
+ data.tar.gz: 22528e4214f7f111ff8291d3b5842bac210c347937531619c44f415df076630a
5
+ SHA512:
6
+ metadata.gz: c71cfe3d8b2bab2cfce28991261b39a2b23fec07f4d77e390edbb5e7a5e7e2e7fa6da0a81e1ab3d51acad32b1329c42d922fb2bc7f3513637f829f9aca5b5157
7
+ data.tar.gz: 6e0fa031fcf8e636788e7fc79783b63a114f685dfb8c37c1d3f9106b034d02bb4ba504bacee83fb3647fee76bf0fefd25d05d46f350fee1bcb1e5ef292ebac00
@@ -0,0 +1,3 @@
1
+ ## 0.1.0
2
+
3
+ - First release.
@@ -0,0 +1,39 @@
1
+ # ActiveStorage FTP Service
2
+
3
+ FTP Active Storage service.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'activestorage-ftp'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ bundle install
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ config/storage.yml
22
+
23
+ ```yml
24
+ production:
25
+ service: Ftp
26
+ ftp_host: <%= ENV["FTP_HOST"] %>
27
+ ftp_port: <%= ENV["FTP_PORT"] %>
28
+ ftp_user: <%= ENV["FTP_USER"] %>
29
+ ftp_passwd: <%= ENV["FTP_PASSWD"] %>
30
+ ftp_folder: <%= ENV["FTP_FOLDER"] %>
31
+ ftp_url: <%= ENV["FTP_URL"] %>
32
+ # optional
33
+ ftp_passive: true
34
+ ftp_chmod: 0600
35
+ ```
36
+
37
+ ## License
38
+
39
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+ require "active_storage_ftp/ex_ftp"
3
+ require "active_storage_ftp/ex_ftptls"
4
+ require "digest/md5"
5
+ require "active_support/core_ext/numeric/bytes"
6
+
7
+ module ActiveStorage
8
+
9
+ class Service::FtpService < Service
10
+
11
+ def initialize(**config)
12
+ @config = config
13
+ end
14
+
15
+ def upload(key, io, checksum: nil, **)
16
+ instrument :upload, key: key, checksum: checksum do
17
+ connection do |ftp|
18
+ path_for(key).tap do |path|
19
+ ftp.mkdir_p(::File.dirname path)
20
+ ftp.chdir(::File.dirname path)
21
+ ftp.storbinary("STOR #{File.basename(key)}", io, Net::FTP::DEFAULT_BLOCKSIZE)
22
+ if ftp_chmod
23
+ ftp.sendcmd("SITE CHMOD #{ftp_chmod.to_s(8)} #{path_for(key)}")
24
+ end
25
+ end
26
+ end
27
+ ensure_integrity_of(key, checksum) if checksum
28
+ end
29
+ end
30
+
31
+ def download(key)
32
+ if block_given?
33
+ instrument :streaming_download, key: key do
34
+ open(http_url_for(key)) do |file|
35
+ while data = file.read(64.kilobytes)
36
+ yield data
37
+ end
38
+ end
39
+ end
40
+ else
41
+ instrument :download, key: key do
42
+ open(http_url_for(key)) do |file|
43
+ file.read
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def download_chunk(key, range)
50
+ instrument :download_chunk, key: key, range: range do
51
+ open(http_url_for(key)) do |file|
52
+ file.seek range.begin
53
+ file.read range.size
54
+ end
55
+ end
56
+ end
57
+
58
+ def delete(key)
59
+ instrument :delete, key: key do
60
+ begin
61
+ connection do |ftp|
62
+ ftp.chdir(::File.dirname path_for(key))
63
+ ftp.delete(::File.basename path_for(key))
64
+ end
65
+ rescue
66
+ # Ignore files already deleted
67
+ end
68
+ end
69
+ end
70
+
71
+ def delete_prefixed(prefix)
72
+ instrument :delete_prefixed, prefix: prefix do
73
+ connection do |ftp|
74
+ ftp.chdir(path_for(prefix))
75
+ ftp.list.each do |file|
76
+ ftp.delete(file.split.last)
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def exist?(key)
83
+ instrument :exist, key: key do |payload|
84
+ response = request_head(key)
85
+ answer = response.code.to_i == 200
86
+ payload[:exist] = answer
87
+ answer
88
+ end
89
+ end
90
+
91
+ def url(key, expires_in:, filename:, disposition:, content_type:)
92
+ instrument :url, key: key do |payload|
93
+ generated_url = http_url_for(key)
94
+ payload[:url] = generated_url
95
+ generated_url
96
+ end
97
+ end
98
+
99
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
100
+ instrument :url, key: key do |payload|
101
+ verified_token_with_expiration = ActiveStorage.verifier.generate(
102
+ {
103
+ key: key,
104
+ content_type: content_type,
105
+ content_length: content_length,
106
+ checksum: checksum
107
+ },
108
+ {expires_in: expires_in,
109
+ purpose: :blob_token}
110
+ )
111
+
112
+ generated_url = url_helpers.update_rails_disk_service_url(verified_token_with_expiration, host: current_host)
113
+ payload[:url] = generated_url
114
+ generated_url
115
+
116
+ end
117
+ end
118
+
119
+ def headers_for_direct_upload(key, content_type:, **)
120
+ {"Content-Type" => content_type}
121
+ end
122
+
123
+ def path_for(key) #:nodoc:
124
+ File.join ftp_folder, folder_for(key), key
125
+ end
126
+
127
+ private
128
+
129
+ attr_reader :config
130
+
131
+ def folder_for(key)
132
+ [key[0..1], key[2..3]].join("/")
133
+ end
134
+
135
+ def ensure_integrity_of(key, checksum)
136
+ response = request_head(key)
137
+ unless "#{response['Content-MD5']}==" == checksum
138
+ delete key
139
+ raise ActiveStorage::IntegrityError
140
+ end
141
+ end
142
+
143
+ def url_helpers
144
+ @url_helpers ||= Rails.application.routes.url_helpers
145
+ end
146
+
147
+ def current_host
148
+ ActiveStorage::Current.host
149
+ end
150
+
151
+ def request_head(key)
152
+ uri = URI(http_url_for(key))
153
+ request = Net::HTTP.new(uri.host, uri.port)
154
+ request.use_ssl = uri.scheme == 'https'
155
+ request.request_head(uri.path)
156
+ end
157
+
158
+ def http_url_for(key)
159
+ ([ftp_url, folder_for(key), key].join('/'))
160
+ end
161
+
162
+ def inferred_content_type
163
+ SanitizedFile.new(path).content_type
164
+ end
165
+
166
+ def ftp_host
167
+ config.fetch(:ftp_host)
168
+ end
169
+
170
+ def ftp_port
171
+ config.fetch(:ftp_port)
172
+ end
173
+
174
+ def ftp_user
175
+ config.fetch(:ftp_user)
176
+ end
177
+
178
+ def ftp_passwd
179
+ config.fetch(:ftp_passwd)
180
+ end
181
+
182
+ def ftp_folder
183
+ config.fetch(:ftp_folder)
184
+ end
185
+
186
+ def ftp_url
187
+ config.fetch(:ftp_url)
188
+ end
189
+
190
+ def ftp_passive
191
+ config.fetch(:ftp_passive)
192
+ end
193
+
194
+ def ftp_chmod
195
+ config.fetch(:ftp_chmod, 0600)
196
+ end
197
+
198
+ def connection
199
+ ftp = ExFTP.new
200
+ ftp.connect(ftp_host, ftp_port)
201
+ begin
202
+ ftp.passive = ftp_passive
203
+ ftp.login(ftp_user, ftp_passwd)
204
+ yield ftp
205
+ ensure
206
+ ftp.quit
207
+ end
208
+ end
209
+
210
+ end
211
+ end
@@ -0,0 +1,25 @@
1
+ require 'net/ftp'
2
+
3
+ class ExFTP < Net::FTP
4
+ def mkdir_p(dir)
5
+ parts = dir.split("/")
6
+ if parts.first == "~"
7
+ growing_path = ""
8
+ else
9
+ growing_path = "/"
10
+ end
11
+ for part in parts
12
+ next if part == ""
13
+ if growing_path == ""
14
+ growing_path = part
15
+ else
16
+ growing_path = File.join(growing_path, part)
17
+ end
18
+ begin
19
+ mkdir(growing_path)
20
+ chdir(growing_path)
21
+ rescue Net::FTPPermError, Net::FTPTempError => e
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ require 'double_bag_ftps'
2
+
3
+ class ExFTPTLS < DoubleBagFTPS
4
+ def mkdir_p(dir)
5
+ parts = dir.split("/")
6
+ if parts.first == "~"
7
+ growing_path = ""
8
+ else
9
+ growing_path = "/"
10
+ end
11
+ for part in parts
12
+ next if part == ""
13
+ if growing_path == ""
14
+ growing_path = part
15
+ else
16
+ growing_path = File.join(growing_path, part)
17
+ end
18
+ begin
19
+ mkdir(growing_path)
20
+ chdir(growing_path)
21
+ rescue Net::FTPPermError, Net::FTPTempError => e
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ module ActiveStorageFtp
2
+ class Railtie < ::Rails::Railtie
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveStorageFtp
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,5 @@
1
+ # require "active_storage_ftp/railtie"
2
+
3
+ module ActiveStorageFtp
4
+ # Your code goes here...
5
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activestorage-ftp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexey Gordienko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.0
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '5.2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 5.2.0
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.2'
33
+ - !ruby/object:Gem::Dependency
34
+ name: net-sftp
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 2.1.2
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 2.1.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: double-bag-ftps
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.1.3
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '='
59
+ - !ruby/object:Gem::Version
60
+ version: 0.1.3
61
+ description: FTP Active Storage service.
62
+ email: alx@anadyr.org
63
+ executables: []
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - CHANGELOG.md
68
+ - README.md
69
+ - lib/active_storage/service/ftp_service.rb
70
+ - lib/active_storage_ftp/ex_ftp.rb
71
+ - lib/active_storage_ftp/ex_ftptls.rb
72
+ - lib/active_storage_ftp/railtie.rb
73
+ - lib/active_storage_ftp/version.rb
74
+ - lib/activestorage-ftp.rb
75
+ homepage: https://github.com/gordienko/activestorage-ftp
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.7.9
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: FTP Active Storage service
99
+ test_files: []