activestorage_saas 5.2.5.1 → 7.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +6 -3
- data/Gemfile.lock +190 -66
- data/Guardfile +42 -0
- data/Rakefile +5 -9
- data/activestorage_saas.gemspec +2 -5
- data/config/initializers/active_storage_saas.rb +6 -0
- data/lib/active_storage/service/saas_service.rb +20 -7
- data/lib/active_storage_saas/blob_model_mixin.rb +48 -0
- data/lib/active_storage_saas/direct_uploads_controller_mixin.rb +31 -0
- data/lib/active_storage_saas/engine.rb +39 -0
- data/lib/{actives_torage_saas → active_storage_saas}/routes.rb +0 -0
- data/lib/active_storage_saas/storage_service_configuration_model_mixin.rb +47 -0
- data/lib/active_storage_saas.rb +4 -2
- data/lib/generators/active_storage_saas/install/USAGE +5 -0
- data/lib/generators/active_storage_saas/install/install_generator.rb +19 -0
- data/lib/generators/active_storage_saas/install/templates/config/initializers/active_storage_saas.rb +6 -0
- metadata +23 -16
- data/app/controller/active_storage_saas/base_controller.rb +0 -7
- data/app/controller/active_storage_saas/blobs_controller.rb +0 -8
- data/app/controller/active_storage_saas/direct_uploads_controller.rb +0 -26
- data/app/controller/active_storage_saas/disk_controller.rb +0 -63
- data/app/controller/active_storage_saas/representations_controller.rb +0 -14
- data/db/migrate/001_activestorage_saas_tables.rb +0 -17
- data/lib/actives_torage_saas/blob_patch.rb +0 -41
- data/lib/actives_torage_saas/engine.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2582d10000f7aae9780b0d273c172c4a76cb08c97bf092126979ce4110d00d8
|
4
|
+
data.tar.gz: 7f88a5f75a7a18bf1587d114e2b87d7d9888afd633d88488868910b495b2c72a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7d76f176c4a9c6ed75544f7008d874363c0cd9b80a372af94d93c8d24800835644fc18ed62e96546e4ea924491b789e8ec07e676e3aaa9bbc1e7b19bff4aad0
|
7
|
+
data.tar.gz: 5d9ed9018b5a423dc66ed88bec3b93e34abd12d060c11ca245767a9eb91aa22bf2115bbef8e73e1c5fdedf17a698c139303a641dd1da81ddbc617a30ce7fb998
|
data/Gemfile
CHANGED
@@ -5,8 +5,11 @@ source "https://rubygems.org"
|
|
5
5
|
# Specify your gem's dependencies in activestorage_saas.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
-
gem "
|
8
|
+
gem "rails", "~> 7.0"
|
9
|
+
gem "sqlite3"
|
10
|
+
gem "propshaft"
|
11
|
+
gem "rspec-rails", "~> 5.1"
|
9
12
|
|
10
|
-
gem "rspec", "~>
|
13
|
+
gem "guard-rspec", "~> 4.7"
|
11
14
|
|
12
|
-
gem "
|
15
|
+
gem "aws-sdk-s3", "~> 1.114"
|
data/Gemfile.lock
CHANGED
@@ -1,112 +1,236 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
activestorage_saas (
|
5
|
-
activestorage (
|
4
|
+
activestorage_saas (7.0.4)
|
5
|
+
activestorage (>= 7.0.3.1, <= 7.0.4)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
|
11
|
-
|
12
|
-
activesupport (=
|
13
|
-
|
10
|
+
actioncable (7.0.4)
|
11
|
+
actionpack (= 7.0.4)
|
12
|
+
activesupport (= 7.0.4)
|
13
|
+
nio4r (~> 2.0)
|
14
|
+
websocket-driver (>= 0.6.1)
|
15
|
+
actionmailbox (7.0.4)
|
16
|
+
actionpack (= 7.0.4)
|
17
|
+
activejob (= 7.0.4)
|
18
|
+
activerecord (= 7.0.4)
|
19
|
+
activestorage (= 7.0.4)
|
20
|
+
activesupport (= 7.0.4)
|
21
|
+
mail (>= 2.7.1)
|
22
|
+
net-imap
|
23
|
+
net-pop
|
24
|
+
net-smtp
|
25
|
+
actionmailer (7.0.4)
|
26
|
+
actionpack (= 7.0.4)
|
27
|
+
actionview (= 7.0.4)
|
28
|
+
activejob (= 7.0.4)
|
29
|
+
activesupport (= 7.0.4)
|
30
|
+
mail (~> 2.5, >= 2.5.4)
|
31
|
+
net-imap
|
32
|
+
net-pop
|
33
|
+
net-smtp
|
34
|
+
rails-dom-testing (~> 2.0)
|
35
|
+
actionpack (7.0.4)
|
36
|
+
actionview (= 7.0.4)
|
37
|
+
activesupport (= 7.0.4)
|
38
|
+
rack (~> 2.0, >= 2.2.0)
|
14
39
|
rack-test (>= 0.6.3)
|
15
40
|
rails-dom-testing (~> 2.0)
|
16
|
-
rails-html-sanitizer (~> 1.0, >= 1.0
|
17
|
-
|
18
|
-
|
41
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
42
|
+
actiontext (7.0.4)
|
43
|
+
actionpack (= 7.0.4)
|
44
|
+
activerecord (= 7.0.4)
|
45
|
+
activestorage (= 7.0.4)
|
46
|
+
activesupport (= 7.0.4)
|
47
|
+
globalid (>= 0.6.0)
|
48
|
+
nokogiri (>= 1.8.5)
|
49
|
+
actionview (7.0.4)
|
50
|
+
activesupport (= 7.0.4)
|
19
51
|
builder (~> 3.1)
|
20
52
|
erubi (~> 1.4)
|
21
53
|
rails-dom-testing (~> 2.0)
|
22
|
-
rails-html-sanitizer (~> 1.
|
23
|
-
|
24
|
-
activesupport (=
|
25
|
-
|
26
|
-
|
27
|
-
activesupport (=
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
54
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
55
|
+
activejob (7.0.4)
|
56
|
+
activesupport (= 7.0.4)
|
57
|
+
globalid (>= 0.3.6)
|
58
|
+
activemodel (7.0.4)
|
59
|
+
activesupport (= 7.0.4)
|
60
|
+
activerecord (7.0.4)
|
61
|
+
activemodel (= 7.0.4)
|
62
|
+
activesupport (= 7.0.4)
|
63
|
+
activestorage (7.0.4)
|
64
|
+
actionpack (= 7.0.4)
|
65
|
+
activejob (= 7.0.4)
|
66
|
+
activerecord (= 7.0.4)
|
67
|
+
activesupport (= 7.0.4)
|
68
|
+
marcel (~> 1.0)
|
69
|
+
mini_mime (>= 1.1.0)
|
70
|
+
activesupport (7.0.4)
|
34
71
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
35
|
-
i18n (>=
|
36
|
-
minitest (
|
37
|
-
tzinfo (~>
|
38
|
-
|
39
|
-
|
72
|
+
i18n (>= 1.6, < 2)
|
73
|
+
minitest (>= 5.1)
|
74
|
+
tzinfo (~> 2.0)
|
75
|
+
aws-eventstream (1.2.0)
|
76
|
+
aws-partitions (1.664.0)
|
77
|
+
aws-sdk-core (3.168.1)
|
78
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
79
|
+
aws-partitions (~> 1, >= 1.651.0)
|
80
|
+
aws-sigv4 (~> 1.5)
|
81
|
+
jmespath (~> 1, >= 1.6.1)
|
82
|
+
aws-sdk-kms (1.59.0)
|
83
|
+
aws-sdk-core (~> 3, >= 3.165.0)
|
84
|
+
aws-sigv4 (~> 1.1)
|
85
|
+
aws-sdk-s3 (1.117.1)
|
86
|
+
aws-sdk-core (~> 3, >= 3.165.0)
|
87
|
+
aws-sdk-kms (~> 1)
|
88
|
+
aws-sigv4 (~> 1.4)
|
89
|
+
aws-sigv4 (1.5.2)
|
90
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
40
91
|
builder (3.2.4)
|
92
|
+
coderay (1.1.3)
|
41
93
|
concurrent-ruby (1.1.10)
|
42
94
|
crass (1.0.6)
|
43
95
|
diff-lcs (1.5.0)
|
44
96
|
erubi (1.11.0)
|
97
|
+
ffi (1.15.5)
|
98
|
+
formatador (1.1.0)
|
99
|
+
globalid (1.0.0)
|
100
|
+
activesupport (>= 5.0)
|
101
|
+
guard (2.18.0)
|
102
|
+
formatador (>= 0.2.4)
|
103
|
+
listen (>= 2.7, < 4.0)
|
104
|
+
lumberjack (>= 1.0.12, < 2.0)
|
105
|
+
nenv (~> 0.1)
|
106
|
+
notiffany (~> 0.0)
|
107
|
+
pry (>= 0.13.0)
|
108
|
+
shellany (~> 0.0)
|
109
|
+
thor (>= 0.18.1)
|
110
|
+
guard-compat (1.2.1)
|
111
|
+
guard-rspec (4.7.3)
|
112
|
+
guard (~> 2.1)
|
113
|
+
guard-compat (~> 1.1)
|
114
|
+
rspec (>= 2.99.0, < 4.0)
|
45
115
|
i18n (1.12.0)
|
46
116
|
concurrent-ruby (~> 1.0)
|
47
|
-
|
117
|
+
jmespath (1.6.1)
|
118
|
+
listen (3.7.1)
|
119
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
120
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
121
|
+
loofah (2.19.0)
|
48
122
|
crass (~> 1.0.2)
|
49
123
|
nokogiri (>= 1.5.9)
|
124
|
+
lumberjack (1.2.8)
|
125
|
+
mail (2.7.1)
|
126
|
+
mini_mime (>= 0.1.1)
|
50
127
|
marcel (1.0.2)
|
128
|
+
method_source (1.0.0)
|
129
|
+
mini_mime (1.1.2)
|
51
130
|
mini_portile2 (2.8.0)
|
52
|
-
minitest (5.16.
|
53
|
-
|
131
|
+
minitest (5.16.3)
|
132
|
+
nenv (0.3.0)
|
133
|
+
net-imap (0.3.1)
|
134
|
+
net-protocol
|
135
|
+
net-pop (0.1.2)
|
136
|
+
net-protocol
|
137
|
+
net-protocol (0.1.3)
|
138
|
+
timeout
|
139
|
+
net-smtp (0.3.3)
|
140
|
+
net-protocol
|
141
|
+
nio4r (2.5.8)
|
142
|
+
nokogiri (1.13.9)
|
54
143
|
mini_portile2 (~> 2.8.0)
|
55
144
|
racc (~> 1.4)
|
56
|
-
|
57
|
-
|
58
|
-
|
145
|
+
notiffany (0.1.3)
|
146
|
+
nenv (~> 0.1)
|
147
|
+
shellany (~> 0.0)
|
148
|
+
propshaft (0.6.4)
|
149
|
+
actionpack (>= 7.0.0)
|
150
|
+
activesupport (>= 7.0.0)
|
151
|
+
rack
|
152
|
+
railties (>= 7.0.0)
|
153
|
+
pry (0.14.1)
|
154
|
+
coderay (~> 1.1)
|
155
|
+
method_source (~> 1.0)
|
59
156
|
racc (1.6.0)
|
60
157
|
rack (2.2.4)
|
61
158
|
rack-test (2.0.2)
|
62
159
|
rack (>= 1.3)
|
160
|
+
rails (7.0.4)
|
161
|
+
actioncable (= 7.0.4)
|
162
|
+
actionmailbox (= 7.0.4)
|
163
|
+
actionmailer (= 7.0.4)
|
164
|
+
actionpack (= 7.0.4)
|
165
|
+
actiontext (= 7.0.4)
|
166
|
+
actionview (= 7.0.4)
|
167
|
+
activejob (= 7.0.4)
|
168
|
+
activemodel (= 7.0.4)
|
169
|
+
activerecord (= 7.0.4)
|
170
|
+
activestorage (= 7.0.4)
|
171
|
+
activesupport (= 7.0.4)
|
172
|
+
bundler (>= 1.15.0)
|
173
|
+
railties (= 7.0.4)
|
63
174
|
rails-dom-testing (2.0.3)
|
64
175
|
activesupport (>= 4.2.0)
|
65
176
|
nokogiri (>= 1.6)
|
66
177
|
rails-html-sanitizer (1.4.3)
|
67
178
|
loofah (~> 2.3)
|
68
|
-
|
179
|
+
railties (7.0.4)
|
180
|
+
actionpack (= 7.0.4)
|
181
|
+
activesupport (= 7.0.4)
|
182
|
+
method_source
|
183
|
+
rake (>= 12.2)
|
184
|
+
thor (~> 1.0)
|
185
|
+
zeitwerk (~> 2.5)
|
69
186
|
rake (13.0.6)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
rspec-
|
75
|
-
rspec-
|
76
|
-
|
77
|
-
|
78
|
-
|
187
|
+
rb-fsevent (0.11.2)
|
188
|
+
rb-inotify (0.10.1)
|
189
|
+
ffi (~> 1.0)
|
190
|
+
rspec (3.12.0)
|
191
|
+
rspec-core (~> 3.12.0)
|
192
|
+
rspec-expectations (~> 3.12.0)
|
193
|
+
rspec-mocks (~> 3.12.0)
|
194
|
+
rspec-core (3.12.0)
|
195
|
+
rspec-support (~> 3.12.0)
|
196
|
+
rspec-expectations (3.12.0)
|
79
197
|
diff-lcs (>= 1.2.0, < 2.0)
|
80
|
-
rspec-support (~> 3.
|
81
|
-
rspec-mocks (3.
|
198
|
+
rspec-support (~> 3.12.0)
|
199
|
+
rspec-mocks (3.12.0)
|
82
200
|
diff-lcs (>= 1.2.0, < 2.0)
|
83
|
-
rspec-support (~> 3.
|
84
|
-
rspec-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
tzinfo (
|
99
|
-
|
100
|
-
|
201
|
+
rspec-support (~> 3.12.0)
|
202
|
+
rspec-rails (5.1.2)
|
203
|
+
actionpack (>= 5.2)
|
204
|
+
activesupport (>= 5.2)
|
205
|
+
railties (>= 5.2)
|
206
|
+
rspec-core (~> 3.10)
|
207
|
+
rspec-expectations (~> 3.10)
|
208
|
+
rspec-mocks (~> 3.10)
|
209
|
+
rspec-support (~> 3.10)
|
210
|
+
rspec-support (3.12.0)
|
211
|
+
shellany (0.0.1)
|
212
|
+
sqlite3 (1.5.4)
|
213
|
+
mini_portile2 (~> 2.8.0)
|
214
|
+
thor (1.2.1)
|
215
|
+
timeout (0.3.0)
|
216
|
+
tzinfo (2.0.5)
|
217
|
+
concurrent-ruby (~> 1.0)
|
218
|
+
websocket-driver (0.7.5)
|
219
|
+
websocket-extensions (>= 0.1.0)
|
220
|
+
websocket-extensions (0.1.5)
|
221
|
+
zeitwerk (2.6.6)
|
101
222
|
|
102
223
|
PLATFORMS
|
103
224
|
ruby
|
104
225
|
|
105
226
|
DEPENDENCIES
|
106
227
|
activestorage_saas!
|
107
|
-
|
108
|
-
rspec (~>
|
109
|
-
|
228
|
+
aws-sdk-s3 (~> 1.114)
|
229
|
+
guard-rspec (~> 4.7)
|
230
|
+
propshaft
|
231
|
+
rails (~> 7.0)
|
232
|
+
rspec-rails (~> 5.1)
|
233
|
+
sqlite3
|
110
234
|
|
111
235
|
BUNDLED WITH
|
112
|
-
2.3.
|
236
|
+
2.3.17
|
data/Guardfile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
20
|
+
# * bundler: 'bundle exec rspec'
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
22
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
23
|
+
# installed the spring binstubs per the docs)
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
|
+
# * 'just' rspec: 'rspec'
|
26
|
+
|
27
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
28
|
+
require "guard/rspec/dsl"
|
29
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
30
|
+
|
31
|
+
# Feel free to open issues for suggestions and improvements
|
32
|
+
|
33
|
+
# RSpec files
|
34
|
+
rspec = dsl.rspec
|
35
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
36
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
37
|
+
watch(rspec.spec_files)
|
38
|
+
|
39
|
+
# Ruby files
|
40
|
+
ruby = dsl.ruby
|
41
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
42
|
+
end
|
data/Rakefile
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
-
|
1
|
+
require "bundler/setup"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
RSpec::Core::RakeTask.new(:spec)
|
3
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
4
|
+
load "rails/tasks/engine.rake"
|
7
5
|
|
8
|
-
|
6
|
+
load "rails/tasks/statistics.rake"
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
task default: %i[spec rubocop]
|
8
|
+
require "bundler/gem_tasks"
|
data/activestorage_saas.gemspec
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
activestorage_version = '5.2.5'
|
4
|
-
gem_version = 1
|
5
|
-
|
6
3
|
Gem::Specification.new do |spec|
|
7
4
|
spec.name = "activestorage_saas"
|
8
|
-
spec.version = "
|
5
|
+
spec.version = "7.0.4"
|
9
6
|
spec.authors = ["xiaohui"]
|
10
7
|
spec.email = ["xiaohui@tanmer.com"]
|
11
8
|
|
@@ -29,5 +26,5 @@ Gem::Specification.new do |spec|
|
|
29
26
|
|
30
27
|
spec.require_paths = ["lib"]
|
31
28
|
|
32
|
-
spec.add_dependency "activestorage",
|
29
|
+
spec.add_dependency "activestorage", '>=7.0.3.1', '<= 7.0.4'
|
33
30
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
Rails.application.configure do
|
2
|
+
# config.active_storage_saas.service_resolver = ->(blob) { StorageServiceConfiguration.from_service_name(blob.service_name)&.to_service }
|
3
|
+
# config.active_storage_saas.service_name_converter = ->(controller) { controller.send(:current_tenant).storage_service&.to_service_name }
|
4
|
+
# config.active_storage_saas.service_name_validator = ->(service_name) { StorageServiceConfiguration.valid_service_name?(service_name) }
|
5
|
+
# config.active_storage_saas.direct_upload_extra_blob_args = ->(controller) { { tenant: controller.send(:current_tenant) } }
|
6
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'ruby2_keywords'
|
1
2
|
module ActiveStorage
|
2
3
|
class Service
|
3
4
|
# # config/storage.yml
|
@@ -22,23 +23,38 @@ module ActiveStorage
|
|
22
23
|
end
|
23
24
|
|
24
25
|
Service.public_instance_methods(false).each do |name|
|
26
|
+
next if name.in?(%i[name=])
|
27
|
+
|
25
28
|
define_method name do |*args, &block|
|
26
29
|
service.public_send(name, *args, &block)
|
27
30
|
end
|
31
|
+
ruby2_keywords name
|
32
|
+
end
|
33
|
+
|
34
|
+
def name=(_)
|
35
|
+
# do nothing, name should be service.name
|
28
36
|
end
|
29
37
|
|
30
38
|
redefine_method :url_expires_in do
|
31
39
|
@options.fetch(:url_expires_in, 5.minutes)
|
32
40
|
end
|
33
41
|
|
34
|
-
def method_missing(name, *args, &block)
|
42
|
+
ruby2_keywords def method_missing(name, *args, &block)
|
35
43
|
service.public_send(name, *args, &block)
|
36
44
|
end
|
37
45
|
|
38
|
-
def respond_to_missing?(method)
|
46
|
+
def respond_to_missing?(method, _include_all)
|
39
47
|
service.respond_to?(method)
|
40
48
|
end
|
41
49
|
|
50
|
+
def http_method_for_direct_upload
|
51
|
+
service.http_method_for_direct_upload if service.respond_to?(:http_method_for_direct_upload)
|
52
|
+
end
|
53
|
+
|
54
|
+
def http_response_type_for_direct_upload
|
55
|
+
service.http_response_type_for_direct_upload if service.respond_to?(:http_response_type_for_direct_upload)
|
56
|
+
end
|
57
|
+
|
42
58
|
def form_data_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
|
43
59
|
if service.respond_to?(:form_data_for_direct_upload)
|
44
60
|
service.form_data_for_direct_upload key,
|
@@ -57,14 +73,11 @@ module ActiveStorage
|
|
57
73
|
end
|
58
74
|
|
59
75
|
def service
|
60
|
-
@service ||= blob
|
76
|
+
@service ||= (blob && ActiveStorageSaas.service_resolver.call(blob)) || default_service
|
61
77
|
end
|
62
78
|
|
63
|
-
private
|
64
|
-
|
65
79
|
def default_service
|
66
|
-
@default_service ||= ActiveStorage::
|
67
|
-
Rails.configuration.active_storage.service_configurations
|
80
|
+
@default_service ||= ActiveStorage::Blob.services.fetch(@options[:default_service])
|
68
81
|
end
|
69
82
|
end
|
70
83
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_storage/service/saas_service'
|
2
|
+
|
3
|
+
module ActiveStorageSaas
|
4
|
+
module BlobModelMixin
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
prepend InstanceMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
prepended do
|
12
|
+
prepend InstanceMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods
|
16
|
+
extend ActiveSupport::Concern
|
17
|
+
|
18
|
+
prepended do
|
19
|
+
redefine_method :service do
|
20
|
+
@service ||= begin
|
21
|
+
raise KeyError, "storage service name should not be 'saas'" if service_name.to_s == 'saas'
|
22
|
+
|
23
|
+
services.fetch(service_name) { |_| ActiveStorageSaas.service_resolver.call(self) } || global_service
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
validate on: :save do
|
28
|
+
errors.add(:service_name, :invalid) if service_name.to_s == 'saas'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def global_service
|
35
|
+
self.class.service
|
36
|
+
end
|
37
|
+
|
38
|
+
def analyzer_class
|
39
|
+
analyzers = service.class.respond_to?(:analyzers) ? service.class.analyzers : ActiveStorage.analyzers
|
40
|
+
analyzers.detect { |klass| klass.accept?(self) } || ActiveStorage::Analyzer::NullAnalyzer
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_service_name_in_services
|
44
|
+
ActiveStorageSaas.service_name_validator.call(service_name) || super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActiveStorageSaas
|
2
|
+
module DirectUploadsControllerMixin
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
prepend InstanceMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
prepended do
|
10
|
+
prepend InstanceMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
def create
|
15
|
+
blob = ActiveStorage::Blob.create!(blob_args)
|
16
|
+
render json: direct_upload_json(blob)
|
17
|
+
end
|
18
|
+
|
19
|
+
def callback
|
20
|
+
ActiveStorageSaas.direct_upload_callback.call(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def blob_args
|
26
|
+
super.merge(ActiveStorageSaas.direct_upload_extra_blob_args.call(self))
|
27
|
+
.merge(service_name: ActiveStorageSaas.service_name_converter.call(self))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'active_storage_saas/blob_model_mixin'
|
2
|
+
require 'active_storage_saas/direct_uploads_controller_mixin'
|
3
|
+
require 'active_storage_saas/storage_service_configuration_model_mixin'
|
4
|
+
module ActiveStorageSaas
|
5
|
+
class Engine < Rails::Engine
|
6
|
+
config.generators do |g|
|
7
|
+
g.test_framework :rspec
|
8
|
+
end
|
9
|
+
config.active_storage_saas = ActiveSupport::OrderedOptions.new
|
10
|
+
|
11
|
+
initializer "active_storage_saas.configs" do
|
12
|
+
config.after_initialize do |app|
|
13
|
+
default_service_resolver = ->(blob) { StorageServiceConfiguration.from_service_name(blob.service_name)&.to_service }
|
14
|
+
default_service_name_converter = ->(controller) { StorageServiceConfiguration.first.to_service_name }
|
15
|
+
default_service_name_validator = ->(service_name) { StorageServiceConfiguration.valid_service_name?(service_name) }
|
16
|
+
default_direct_upload_extra_blob_args = ->(controller) { { } }
|
17
|
+
ActiveStorageSaas.service_resolver = app.config.active_storage_saas.service_resolver || default_service_resolver
|
18
|
+
ActiveStorageSaas.service_name_converter = app.config.active_storage_saas.service_name_converter || default_service_name_converter
|
19
|
+
ActiveStorageSaas.service_name_validator = app.config.active_storage_saas.service_name_validator || default_service_name_validator
|
20
|
+
ActiveStorageSaas.direct_upload_extra_blob_args = app.config.active_storage_saas.direct_upload_extra_blob_args || default_direct_upload_extra_blob_args
|
21
|
+
ActiveStorage::Blob # call this class to load mixin
|
22
|
+
end
|
23
|
+
|
24
|
+
config.to_prepare do
|
25
|
+
ActiveStorage::DirectUploadsController.include ActiveStorageSaas::DirectUploadsControllerMixin
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
config.to_prepare do
|
30
|
+
ActionDispatch::Routing::Mapper.include Routes
|
31
|
+
end
|
32
|
+
|
33
|
+
initializer 'active_storage_saas.load_mixins' do
|
34
|
+
ActiveSupport.on_load(:active_storage_blob) do
|
35
|
+
include ActiveStorageSaas::BlobModelMixin
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
File without changes
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ActiveStorageSaas
|
2
|
+
module StorageServiceConfigurationMixin
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
prepend InstanceMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
prepended do
|
10
|
+
prepend InstanceMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
class_methods do
|
17
|
+
def service_name_pattern
|
18
|
+
/^#{name}:(\d+)$/
|
19
|
+
end
|
20
|
+
|
21
|
+
def from_service_name(service_name)
|
22
|
+
find_by(id: Regexp.last_match(1)) if service_name =~ service_name_pattern
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid_service_name?(service_name)
|
26
|
+
service_name_pattern.match?(service_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_service
|
31
|
+
defined_service = ActiveStorage::Blob.services.fetch(service_name)
|
32
|
+
klass = defined_service.class
|
33
|
+
options = ActiveStorage::Blob.services.send(:configurations).fetch(service_name.to_sym)
|
34
|
+
options.deep_merge! service_options.deep_symbolize_keys
|
35
|
+
klass.new(**options).tap do |instance|
|
36
|
+
instance.instance_eval <<~RUBY
|
37
|
+
define_singleton_method(:name) { "#{to_service_name}" }
|
38
|
+
RUBY
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_service_name
|
43
|
+
"#{self.class.name}:#{id}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/active_storage_saas.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
module ActiveStorageSaas
|
4
4
|
class Error < StandardError; end
|
5
5
|
|
6
|
-
mattr_accessor :
|
6
|
+
mattr_accessor :service_resolver
|
7
|
+
mattr_accessor :service_name_converter
|
8
|
+
mattr_accessor :service_name_validator
|
9
|
+
mattr_accessor :direct_upload_extra_blob_args
|
7
10
|
|
8
11
|
require 'active_storage_saas/routes'
|
9
|
-
require 'active_storage_saas/blob_patch'
|
10
12
|
require 'active_storage_saas/engine'
|
11
13
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveStorageSaas
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
source_root File.expand_path("templates", __dir__)
|
4
|
+
class_option :configuration_model, type: :string, default: 'StorageServiceConfiguration'
|
5
|
+
|
6
|
+
def generate_model
|
7
|
+
generate "model", "#{options['configuration_model']} service_name service_options:json"
|
8
|
+
inject_into_file "app/models/#{options['configuration_model'].underscore}.rb", after: /^class .+\n/ do <<-'RUBY'
|
9
|
+
include ActiveStorageSaas::StorageServiceConfigurationMixin
|
10
|
+
|
11
|
+
RUBY
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def copy_files
|
16
|
+
template 'config/initializers/active_storage_saas.rb'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/generators/active_storage_saas/install/templates/config/initializers/active_storage_saas.rb
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
Rails.application.configure do
|
2
|
+
# config.active_storage_saas.service_resolver = ->(blob) { StorageServiceConfiguration.from_service_name(blob.service_name)&.to_service }
|
3
|
+
# config.active_storage_saas.service_name_converter = ->(controller) { controller.send(:current_tenant).storage_service&.to_service_name }
|
4
|
+
# config.active_storage_saas.service_name_validator = ->(service_name) { StorageServiceConfiguration.valid_service_name?(service_name) }
|
5
|
+
# config.active_storage_saas.direct_upload_extra_blob_args = ->(controller) { { tenant: controller.send(:current_tenant) } }
|
6
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activestorage_saas
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 7.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- xiaohui
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activestorage
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 7.0.3.1
|
20
|
+
- - "<="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 7.0.4
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- -
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 7.0.3.1
|
30
|
+
- - "<="
|
25
31
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
32
|
+
version: 7.0.4
|
27
33
|
description: Each tenant can set its own storage service, ActiveStorage service can
|
28
34
|
be dynamically loaded.
|
29
35
|
email:
|
@@ -37,22 +43,23 @@ files:
|
|
37
43
|
- CHANGELOG.md
|
38
44
|
- Gemfile
|
39
45
|
- Gemfile.lock
|
46
|
+
- Guardfile
|
40
47
|
- LICENSE.txt
|
41
48
|
- README.md
|
42
49
|
- Rakefile
|
43
50
|
- activestorage_saas.gemspec
|
44
|
-
-
|
45
|
-
- app/controller/active_storage_saas/blobs_controller.rb
|
46
|
-
- app/controller/active_storage_saas/direct_uploads_controller.rb
|
47
|
-
- app/controller/active_storage_saas/disk_controller.rb
|
48
|
-
- app/controller/active_storage_saas/representations_controller.rb
|
49
|
-
- db/migrate/001_activestorage_saas_tables.rb
|
51
|
+
- config/initializers/active_storage_saas.rb
|
50
52
|
- lib/active_storage/service/saas_service.rb
|
51
53
|
- lib/active_storage_saas.rb
|
52
|
-
- lib/
|
53
|
-
- lib/
|
54
|
-
- lib/
|
54
|
+
- lib/active_storage_saas/blob_model_mixin.rb
|
55
|
+
- lib/active_storage_saas/direct_uploads_controller_mixin.rb
|
56
|
+
- lib/active_storage_saas/engine.rb
|
57
|
+
- lib/active_storage_saas/routes.rb
|
58
|
+
- lib/active_storage_saas/storage_service_configuration_model_mixin.rb
|
55
59
|
- lib/activestorage_saas.rb
|
60
|
+
- lib/generators/active_storage_saas/install/USAGE
|
61
|
+
- lib/generators/active_storage_saas/install/install_generator.rb
|
62
|
+
- lib/generators/active_storage_saas/install/templates/config/initializers/active_storage_saas.rb
|
56
63
|
- sig/activestorage_saas.rbs
|
57
64
|
homepage: https://github.com/xiaohui-zhangxh/activestorage_saas
|
58
65
|
licenses:
|
@@ -76,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
83
|
- !ruby/object:Gem::Version
|
77
84
|
version: '0'
|
78
85
|
requirements: []
|
79
|
-
rubygems_version: 3.
|
86
|
+
rubygems_version: 3.3.7
|
80
87
|
signing_key:
|
81
88
|
specification_version: 4
|
82
89
|
summary: Wraps multi-tenant storage services as ActiveStorage service
|
@@ -1,26 +0,0 @@
|
|
1
|
-
class ActiveStorageSaas::DirectUploadsController < ActiveStorageSaas::BaseController
|
2
|
-
def create
|
3
|
-
blob = ActiveStorage::Blob.create!(blob_args)
|
4
|
-
render json: direct_upload_json(blob)
|
5
|
-
end
|
6
|
-
|
7
|
-
def callback
|
8
|
-
raise NotImplementedError
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def blob_args
|
14
|
-
blob_args = params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type).to_h.symbolize_keys
|
15
|
-
blob_args.merge! tenant: current_tenant, tenant_storage_service: current_tenant.storage_service
|
16
|
-
end
|
17
|
-
|
18
|
-
def direct_upload_json(blob)
|
19
|
-
blob.as_json(root: false, methods: :signed_id, only: :signed_id)
|
20
|
-
.merge(direct_upload: {
|
21
|
-
url: blob.service_url_for_direct_upload,
|
22
|
-
headers: blob.service_headers_for_direct_upload,
|
23
|
-
formData: blob.service_form_data_for_direct_upload
|
24
|
-
})
|
25
|
-
end
|
26
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Serves files stored with the disk service in the same way that the cloud services do.
|
4
|
-
# This means using expiring, signed URLs that are meant for immediate access, not permanent linking.
|
5
|
-
# Always go through the BlobsController, or your own authenticated controller, rather than directly
|
6
|
-
# to the service url.
|
7
|
-
class ActiveStorageSaas::DiskController < ActiveStorage::BaseController
|
8
|
-
skip_forgery_protection
|
9
|
-
|
10
|
-
def show
|
11
|
-
if key = decode_verified_key
|
12
|
-
serve_file disk_service.path_for(key[:key]), content_type: key[:content_type], disposition: key[:disposition]
|
13
|
-
else
|
14
|
-
head :not_found
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def update
|
19
|
-
if token = decode_verified_token
|
20
|
-
if acceptable_content?(token)
|
21
|
-
disk_service.upload token[:key], request.body, checksum: token[:checksum]
|
22
|
-
head :no_content
|
23
|
-
else
|
24
|
-
head :unprocessable_entity
|
25
|
-
end
|
26
|
-
end
|
27
|
-
rescue ActiveStorage::IntegrityError
|
28
|
-
head :unprocessable_entity
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
def disk_service
|
33
|
-
ActiveStorage::Blob.service
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
def decode_verified_key
|
38
|
-
ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key)
|
39
|
-
end
|
40
|
-
|
41
|
-
def serve_file(path, content_type:, disposition:)
|
42
|
-
Rack::File.new(nil).serving(request, path).tap do |(status, headers, body)|
|
43
|
-
self.status = status
|
44
|
-
self.response_body = body
|
45
|
-
|
46
|
-
headers.each do |name, value|
|
47
|
-
response.headers[name] = value
|
48
|
-
end
|
49
|
-
|
50
|
-
response.headers["Content-Type"] = content_type || DEFAULT_SEND_FILE_TYPE
|
51
|
-
response.headers["Content-Disposition"] = disposition || DEFAULT_SEND_FILE_DISPOSITION
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
def decode_verified_token
|
57
|
-
ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token)
|
58
|
-
end
|
59
|
-
|
60
|
-
def acceptable_content?(token)
|
61
|
-
token[:content_type] == request.content_mime_type && token[:content_length] == request.content_length
|
62
|
-
end
|
63
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Take a signed permanent reference for a blob representation and turn it into an expiring service URL for download.
|
4
|
-
# Note: These URLs are publicly accessible. If you need to enforce access protection beyond the
|
5
|
-
# security-through-obscurity factor of the signed blob and variation reference, you'll need to implement your own
|
6
|
-
# authenticated redirection controller.
|
7
|
-
class ActiveStorageSaas::RepresentationsController < ActiveStorage::BaseController
|
8
|
-
include ActiveStorage::SetBlob
|
9
|
-
|
10
|
-
def show
|
11
|
-
expires_in ActiveStorage::Blob.service.url_expires_in
|
12
|
-
redirect_to @blob.representation(params[:variation_key]).processed.service_url(disposition: params[:disposition])
|
13
|
-
end
|
14
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
class ActivestorageSaasTables < ActiveRecord::Migration[5.2]
|
2
|
-
def change
|
3
|
-
tenant_class = ActiveStorageSaas.tenant_class_name.constantize
|
4
|
-
tenant_primary_key = tenant_class.columns.find{|col| col.name == tenant_class.primary_key }
|
5
|
-
create_table :tenant_storage_services do |t|
|
6
|
-
t.references :tenant, type: tenant_primary_key.type, foreign_key: { to_table: tenant_class.table_name }
|
7
|
-
t.string :service_name
|
8
|
-
t.json :service_options
|
9
|
-
|
10
|
-
t.timestamps
|
11
|
-
end
|
12
|
-
|
13
|
-
add_reference tenant_class.table_name.to_sym, :tenant_storage_service, foreign_key: true
|
14
|
-
add_reference :active_storage_blobs, :tenant, type: tenant_primary_key.type, foreign_key: true
|
15
|
-
add_reference :active_storage_blobs, :tenant_storage_service, foreign_key: true
|
16
|
-
end
|
17
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module ActiveStorageSaas
|
2
|
-
module BlobPatch
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
included do
|
6
|
-
belongs_to :tenant, class_name: ActiveStorageSaas.tenant_class_name # rubocop: disable Rails/ReflectionClassName
|
7
|
-
belongs_to :tenant_storage_service, optional: true
|
8
|
-
|
9
|
-
redefine_method :service do
|
10
|
-
class_service = self.class.service
|
11
|
-
if class_service.is_a?(ActiveStorage::Service::SaasService)
|
12
|
-
@service ||= ActiveStorage::Service::SaasService.new(class_service.options.merge(blob: self))
|
13
|
-
else
|
14
|
-
class_service
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
# support sending data from form instead of headers
|
20
|
-
def service_form_data_for_direct_upload(expires_in: service.url_expires_in)
|
21
|
-
return {} unless service.respond_to?(:form_data_for_direct_upload)
|
22
|
-
|
23
|
-
service.form_data_for_direct_upload(key,
|
24
|
-
expires_in: expires_in,
|
25
|
-
content_type: content_type,
|
26
|
-
content_length: byte_size,
|
27
|
-
checksum: checksum)
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def saas_service?
|
33
|
-
service.is_a?(ActiveStorage::Service::SaasService)
|
34
|
-
end
|
35
|
-
|
36
|
-
def analyzer_class
|
37
|
-
analyzers = saas_service? ? service.analyzers : ActiveStorage.analyzers
|
38
|
-
analyzers.detect { |klass| klass.accept?(self) } || ActiveStorage::Analyzer::NullAnalyzer
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module ActiveStorageSaas
|
2
|
-
class Engine < Rails::Engine
|
3
|
-
config.before_configuration do
|
4
|
-
# disable active_storage routes
|
5
|
-
ActiveStorage::Engine.config.paths['config/routes.rb'] = []
|
6
|
-
end
|
7
|
-
|
8
|
-
config.to_prepare do
|
9
|
-
ActionDispatch::Routing::Mapper.include Routes
|
10
|
-
end
|
11
|
-
|
12
|
-
initializer 'patch' do
|
13
|
-
ActiveSupport.on_load(:active_storage_blob) do
|
14
|
-
include ActiveStorageSaas::BlobPatch
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|