Skip to content

Commit 22c2532

Browse files
authored
Add custom services (#188)
* feature: Add custom services Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com> * fix: Use separate config for services Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com> * chore: Update README with custom services info Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com> * chore: Dry Imgproxy::Config - move attributes to :default service Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com> * chore: Drop comments on moved attributes Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com> * chore: Add getters for old options and dry builder a bit Signed-off-by: Valentin Kiselev <mrexox@evilmartians.com>
1 parent fe1b68c commit 22c2532

8 files changed

Lines changed: 246 additions & 52 deletions

File tree

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,34 @@ end
381381

382382
**NOTE:** imgproxy.rb provides built-in adapters for Active Storage and Shrine that are automatically added when Active Storage or Shrine support is enabled.
383383

384+
## Custom services
385+
386+
If you use more than one instance of imgproxy and they have different endpoints and key/salt configurations you can specify them in `services` option.
387+
388+
```ruby
389+
Imgproxy.configure do |config|
390+
config.endpoint = "https://main.imgproxy.com/"
391+
config.service(:pro) do |pro|
392+
pro.endpoint = "https://pro.imgproxy.com/"
393+
pro.key = ENV["IMGPROXY_PRO_KEY"]
394+
pro.salt = ENV["IMGPROXY_PRO_SALT"]
395+
end
396+
end
397+
```
398+
399+
Or via YAML config:
400+
401+
```yaml
402+
endpoint: "https://main.imgproxy.com/"
403+
services:
404+
pro:
405+
endpoint: "https://pro.imgproxy.com/"
406+
key: <%= ENV["IMGPROXY_PRO_KEY"] %>
407+
salt: <%= ENV["IMGPROXY_PRO_SALT"] %>
408+
```
409+
410+
If you don't specify `key`, `salt`, `endpoint`, or `signature_size`, they are inherited from the global configuration.
411+
384412
## Contributing
385413

386414
Bug reports and pull requests are welcome on GitHub at https://github.com/imgproxy/imgproxy.rb.

lib/imgproxy/builder.rb

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ module Imgproxy
1818
# builder.url_for("http://images.example.com/images/image1.jpg")
1919
# builder.url_for("http://images.example.com/images/image2.jpg")
2020
class Builder
21+
class UnknownServiceError < StandardError; end
22+
2123
# @param [Hash] options Processing options
2224
# @see Imgproxy.url_for
2325
def initialize(options = {})
@@ -39,7 +41,7 @@ def url_for(image)
3941
path = [*processing_options, url(image, ext: @format)].join("/")
4042
signature = sign_path(path)
4143

42-
File.join(Imgproxy.config.endpoint.to_s, signature, path)
44+
File.join(endpoint.to_s, signature, path)
4345
end
4446

4547
# Genrates imgproxy info URL
@@ -52,14 +54,18 @@ def info_url_for(image)
5254
path = url(image)
5355
signature = sign_path(path)
5456

55-
File.join(Imgproxy.config.endpoint.to_s, "info", signature, path)
57+
File.join(endpoint.to_s, "info", signature, path)
5658
end
5759

5860
private
5961

62+
attr_reader :service
63+
6064
NEED_ESCAPE_RE = /[@?% ]|[^\p{Ascii}]/.freeze
6165

6266
def extract_builder_options(options)
67+
@service = options.delete(:service)&.to_sym || :default
68+
6369
@use_short_options = not_nil_or(options.delete(:use_short_options), config.use_short_options)
6470
@base64_encode_url = not_nil_or(options.delete(:base64_encode_url), config.base64_encode_urls)
6571
@escape_plain_url =
@@ -118,23 +124,33 @@ def ready_to_sign?
118124
end
119125

120126
def signature_key
121-
config.raw_key
127+
service_config.raw_key
122128
end
123129

124130
def signature_salt
125-
config.raw_salt
131+
service_config.raw_salt
126132
end
127133

128134
def signature_size
129-
config.signature_size
130-
end
131-
132-
def config
133-
Imgproxy.config
135+
service_config.signature_size
134136
end
135137

136138
def not_nil_or(value, fallback)
137139
value.nil? ? fallback : value
138140
end
141+
142+
def endpoint
143+
service_config.endpoint
144+
end
145+
146+
def service_config
147+
@service_config ||= config.services[service].tap do |c|
148+
raise UnknownServiceError, service unless c
149+
end
150+
end
151+
152+
def config
153+
Imgproxy.config
154+
end
139155
end
140156
end

lib/imgproxy/config.rb

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,11 @@
11
require "anyway_config"
22

3+
require "imgproxy/service_config"
34
require "imgproxy/url_adapters"
45

56
module Imgproxy
67
# Imgproxy config
78
#
8-
# @!attribute endpoint
9-
# imgproxy endpoint
10-
# @return [String]
11-
# @!attribute key
12-
# imgproxy hex-encoded signature key
13-
# @return [String]
14-
# @!attribute salt
15-
# imgproxy hex-encoded signature salt
16-
# @return [String]
17-
# @!attribute raw_key
18-
# Decoded signature key
19-
# @return [String]
20-
# @!attribute raw_salt
21-
# Decoded signature salt
22-
# @return [String]
23-
# @!attribute signature_size
24-
# imgproxy signature size. Defaults to 32
25-
# @return [String]
269
# @!attribute use_short_options
2710
# Use short processing option names (+rs+ for +resize+, +g+ for +gravity+, etc).
2811
# Defaults to true
@@ -50,49 +33,81 @@ module Imgproxy
5033
# @see https://github.com/palkan/anyway_config anyway_config
5134
class Config < Anyway::Config
5235
attr_config(
53-
:endpoint,
54-
:key,
55-
:salt,
56-
:raw_key,
57-
:raw_salt,
58-
signature_size: 32,
5936
use_short_options: true,
6037
base64_encode_urls: false,
6138
always_escape_plain_urls: false,
6239
use_s3_urls: false,
6340
use_gcs_urls: false,
6441
gcs_bucket: nil,
6542
shrine_host: nil,
43+
services: {},
6644
)
6745

68-
alias_method :set_key, :key=
69-
alias_method :set_raw_key, :raw_key=
70-
alias_method :set_salt, :salt=
71-
alias_method :set_raw_salt, :raw_salt=
72-
private :set_key, :set_raw_key, :set_salt, :set_raw_salt
46+
def endpoint
47+
service(:default).endpoint
48+
end
49+
50+
def endpoint=(value)
51+
service(:default).endpoint = value
52+
end
53+
54+
def key
55+
service(:default).key
56+
end
7357

7458
def key=(value)
75-
value = value&.to_s
76-
super(value)
77-
set_raw_key(value && [value].pack("H*"))
59+
service(:default).key = value
60+
end
61+
62+
def raw_key
63+
service(:default).raw_key
7864
end
7965

8066
def raw_key=(value)
81-
value = value&.to_s
82-
super(value)
83-
set_key(value&.unpack("H*")&.first)
67+
service(:default).raw_key = value
68+
end
69+
70+
def salt
71+
service(:default).salt
8472
end
8573

8674
def salt=(value)
87-
value = value&.to_s
88-
super(value)
89-
set_raw_salt(value && [value].pack("H*"))
75+
service(:default).salt = value
76+
end
77+
78+
def raw_salt
79+
service(:default).raw_salt
9080
end
9181

9282
def raw_salt=(value)
93-
value = value&.to_s
94-
super(value)
95-
set_salt(value&.unpack("H*")&.first)
83+
service(:default).raw_salt = value
84+
end
85+
86+
def signature_size
87+
service(:default).signature_size
88+
end
89+
90+
def signature_size=(value)
91+
service(:default).signature_size = value
92+
end
93+
94+
def service(name)
95+
services[name.to_sym] ||= ServiceConfig.new(services[:default].to_h)
96+
yield services[name.to_sym] if block_given?
97+
98+
services[name.to_sym]
99+
end
100+
101+
def services
102+
@services ||= {}.tap do |s|
103+
s[:default] = ServiceConfig.new
104+
105+
super.each do |name, data|
106+
s[name.to_sym] = ServiceConfig.new(
107+
s[:default].to_h.merge(data.symbolize_keys),
108+
)
109+
end
110+
end
96111
end
97112

98113
# @deprecated Please use {#key} instead

lib/imgproxy/service_config.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
require "anyway_config"
2+
3+
module Imgproxy
4+
# Imgproxy custom config for services
5+
#
6+
# @!attribute endpoint
7+
# imgproxy endpoint
8+
# @return [String]
9+
# @!attribute key
10+
# imgproxy hex-encoded signature key
11+
# @return [String]
12+
# @!attribute salt
13+
# imgproxy hex-encoded signature salt
14+
# @return [String]
15+
# @!attribute raw_key
16+
# Decoded signature key
17+
# @return [String]
18+
# @!attribute raw_salt
19+
# Decoded signature salt
20+
# @return [String]
21+
# @!attribute signature_size
22+
# imgproxy signature size. Defaults to 32
23+
# @return [String]
24+
#
25+
# @see Imgproxy::Config
26+
class ServiceConfig < Anyway::Config
27+
# Inherit global config values
28+
config_name :imgproxy
29+
30+
attr_config(
31+
:endpoint,
32+
:key,
33+
:salt,
34+
:raw_key,
35+
:raw_salt,
36+
signature_size: 32,
37+
)
38+
39+
alias_method :set_key, :key=
40+
alias_method :set_raw_key, :raw_key=
41+
alias_method :set_salt, :salt=
42+
alias_method :set_raw_salt, :raw_salt=
43+
private :set_key, :set_raw_key, :set_salt, :set_raw_salt
44+
45+
def key=(value)
46+
value = value&.to_s
47+
super(value)
48+
set_raw_key(value && [value].pack("H*"))
49+
end
50+
51+
def raw_key=(value)
52+
value = value&.to_s
53+
super(value)
54+
set_key(value&.unpack("H*")&.first)
55+
end
56+
57+
def salt=(value)
58+
value = value&.to_s
59+
super(value)
60+
set_raw_salt(value && [value].pack("H*"))
61+
end
62+
63+
def raw_salt=(value)
64+
value = value&.to_s
65+
super(value)
66+
set_salt(value&.unpack("H*")&.first)
67+
end
68+
end
69+
end

lib/imgproxy/url_adapters/shrine.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def url(image)
1616
return s3_url(image) if use_s3_url(image)
1717

1818
opts = {}
19-
opts[:host] = Imgproxy.config.shrine_host if Imgproxy.config.shrine_host
19+
opts[:host] = config.shrine_host if config.shrine_host
2020
image.url(**opts)
2121
end
2222

@@ -28,9 +28,13 @@ def s3_url(image)
2828
end
2929

3030
def use_s3_url(image)
31-
Imgproxy.config.use_s3_urls &&
31+
config.use_s3_urls &&
3232
image.storage.is_a?(::Shrine::Storage::S3)
3333
end
34+
35+
def config
36+
Imgproxy.config
37+
end
3438
end
3539
end
3640
end

spec/imgproxy_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,5 +656,42 @@
656656
end
657657
end
658658
end
659+
660+
context "when custom service specified" do
661+
let(:options) { { width: 100, height: 100 } }
662+
663+
before do
664+
described_class.configure do |config|
665+
config.service(:custom) do |custom|
666+
custom.key = "Lorem".unpack1("H*")
667+
custom.salt = "Ipsum".unpack1("H*")
668+
custom.endpoint = "http://custom-imgproxy.test/"
669+
end
670+
end
671+
end
672+
673+
it "signs the info URL" do
674+
expect(described_class.info_url_for(src_url, service: :custom)).to start_with(
675+
"http://custom-imgproxy.test/info/Ce7iwcp9c0K7mvJD9pLbwXmQ-r-rkNJ1jLIPr2sCVv8/",
676+
)
677+
end
678+
679+
context "when signature is truncated" do
680+
before { described_class.config.service(:custom).signature_size = 5 }
681+
682+
it "signs the info URL with truncated signature" do
683+
expect(described_class.info_url_for(src_url, service: :custom)).to start_with(
684+
"http://custom-imgproxy.test/info/Ce7iwco/",
685+
)
686+
end
687+
end
688+
689+
context "when service is unknown" do
690+
it "raises UnknownServiceError" do
691+
expect { described_class.info_url_for(src_url, service: :unknown) }
692+
.to raise_error(Imgproxy::Builder::UnknownServiceError)
693+
end
694+
end
695+
end
659696
end
660697
end

0 commit comments

Comments
 (0)