cloudimage 0.2.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -1
- data/README.md +169 -29
- data/lib/cloudimage.rb +0 -1
- data/lib/cloudimage/client.rb +25 -15
- data/lib/cloudimage/custom_helpers.rb +6 -0
- data/lib/cloudimage/invalidation.rb +50 -0
- data/lib/cloudimage/params.rb +1 -0
- data/lib/cloudimage/refinements.rb +47 -0
- data/lib/cloudimage/security.rb +56 -0
- data/lib/cloudimage/srcset.rb +31 -0
- data/lib/cloudimage/uri.rb +59 -26
- metadata +34 -3
- data/lib/cloudimage/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c02d5fa30289aa70eb6b0d94e13f31130e5c1918ffb18d45197bb0b62cda26de
|
4
|
+
data.tar.gz: ce5da501f8200577f7c9003dba581b59f88628c99a03d54fa662ed0f336737fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f698aefba14fee30249e29e431cf53d1e25440c97c08731ac4c050ba0bc58a95eeeac93143301fdd7cc31d7e96a4cdedac653aa832b61adf2f8c2a7681748d52
|
7
|
+
data.tar.gz: 3a58f037be0329dfbdfd4c1b0d0a71848caaf7d9c42fc1e9425d5f4c43015b81ee60cec29ef84cbf3318a6c195af851d82ab042d12166e7e98ae16bacff5bf2b
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,38 @@
|
|
1
|
-
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [v0.5.0](https://github.com/scaleflex/cloudimage-rb/tree/v0.5.0) (2020-08-02)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/scaleflex/cloudimage-rb/compare/v0.4.0...v0.5.0)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Invalidation [\#24](https://github.com/scaleflex/cloudimage-rb/pull/24) ([janklimo](https://github.com/janklimo))
|
10
|
+
- Add support for custom CNAMEs [\#21](https://github.com/scaleflex/cloudimage-rb/pull/21) ([janklimo](https://github.com/janklimo))
|
11
|
+
|
12
|
+
## [v0.4.0](https://github.com/scaleflex/cloudimage-rb/tree/v0.4.0) (2020-07-19)
|
13
|
+
|
14
|
+
[Full Changelog](https://github.com/scaleflex/cloudimage-rb/compare/v0.3.0...v0.4.0)
|
15
|
+
|
16
|
+
**Implemented enhancements:**
|
17
|
+
|
18
|
+
- Add support for aliases [\#20](https://github.com/scaleflex/cloudimage-rb/pull/20) ([janklimo](https://github.com/janklimo))
|
19
|
+
|
20
|
+
## [v0.3.0](https://github.com/scaleflex/cloudimage-rb/tree/v0.3.0) (2020-07-09)
|
21
|
+
|
22
|
+
[Full Changelog](https://github.com/scaleflex/cloudimage-rb/compare/v0.2.1...v0.3.0)
|
23
|
+
|
24
|
+
**Implemented enhancements:**
|
25
|
+
|
26
|
+
- Introduce URL sealing [\#10](https://github.com/scaleflex/cloudimage-rb/pull/10) ([janklimo](https://github.com/janklimo))
|
27
|
+
|
28
|
+
**Merged pull requests:**
|
29
|
+
|
30
|
+
- Use changelog generation [\#16](https://github.com/scaleflex/cloudimage-rb/pull/16) ([janklimo](https://github.com/janklimo))
|
31
|
+
- Add test coverage with SimpleCov [\#9](https://github.com/scaleflex/cloudimage-rb/pull/9) ([janklimo](https://github.com/janklimo))
|
32
|
+
|
33
|
+
## 0.2.1 (2020-06-29)
|
34
|
+
|
35
|
+
- Include `force_download` param.
|
2
36
|
|
3
37
|
## 0.2.0 (2020-06-28)
|
4
38
|
|
@@ -19,3 +53,6 @@
|
|
19
53
|
|
20
54
|
- Set up Github actions for CI.
|
21
55
|
[#1](https://github.com/scaleflex/cloudimage-rb/pull/1) (@janklimo)
|
56
|
+
|
57
|
+
|
58
|
+
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
data/README.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# cloudimage
|
2
2
|
|
3
|
-

|
3
|
+
[](https://badge.fury.io/rb/cloudimage) 
|
4
4
|
|
5
|
-
`cloudimage` is the official Ruby API wrapper for
|
6
|
-
[Cloudimage's API](https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction).
|
5
|
+
`cloudimage` is the official Ruby API wrapper for [Cloudimage's API][docs].
|
7
6
|
|
8
7
|
Supports Ruby `2.4` and above, `JRuby`, and `TruffleRuby`.
|
9
8
|
|
@@ -12,14 +11,20 @@ Supports Ruby `2.4` and above, `JRuby`, and `TruffleRuby`.
|
|
12
11
|
- [Usage](#usage)
|
13
12
|
- [Hash of params](#hash-of-params)
|
14
13
|
- [Chainable helpers](#chainable-helpers)
|
15
|
-
- [
|
14
|
+
- [Method aliases](#method-aliases)
|
16
15
|
- [Custom helpers](#custom-helpers)
|
16
|
+
- [URL aliases](#url-aliases)
|
17
|
+
- [`srcset` generation](#srcset-generation)
|
18
|
+
- [CNAME](#cname)
|
19
|
+
- [Optional API version](#optional-api-version)
|
17
20
|
- [Security](#security)
|
21
|
+
- [URL signature](#url-signature)
|
22
|
+
- [URL sealing](#url-sealing)
|
23
|
+
- [Invalidation API](#invalidation-api)
|
18
24
|
- [Development](#development)
|
19
|
-
- [TODOs](#todos)
|
20
25
|
- [Contributing](#contributing)
|
21
26
|
- [License](#license)
|
22
|
-
- [
|
27
|
+
- [Showcase](#showcase)
|
23
28
|
|
24
29
|
## Installation
|
25
30
|
|
@@ -39,7 +44,7 @@ Or install it yourself as:
|
|
39
44
|
|
40
45
|
## Usage
|
41
46
|
|
42
|
-
The
|
47
|
+
The most common way to use Cloudimage is by means of your customer token. You can
|
43
48
|
find it within your Admin interface:
|
44
49
|
|
45
50
|

|
@@ -53,12 +58,16 @@ client = Cloudimage::Client.new(token: 'mysecrettoken')
|
|
53
58
|
|
54
59
|
Cloudimage client accepts the following options:
|
55
60
|
|
56
|
-
| Option
|
57
|
-
|
|
58
|
-
| `token`
|
59
|
-
| `
|
60
|
-
| `
|
61
|
-
| `
|
61
|
+
| Option | Type | Additional info |
|
62
|
+
| --------------------- | ------- | ------------------------------------------------------------------------------ |
|
63
|
+
| `token` | string | Required if `cname` is missing. |
|
64
|
+
| `cname` | string | Required if `token` is missing. See [CNAME](#cname). |
|
65
|
+
| `salt` | string | Optional. See [Security](#security). |
|
66
|
+
| `signature_length` | integer | Optional. Integer value in the range `6..40`. Defaults to 18. |
|
67
|
+
| `sign_urls` | boolean | Optional. Defaults to `true`. See [Security](#security). |
|
68
|
+
| `aliases` | hash | Optional. See [URL aliases](#url-aliases). |
|
69
|
+
| `api_key` | string | Optional. See [Invalidation API](#invalidation-api). |
|
70
|
+
| `include_api_version` | boolean | Optional. Defaults to true. See [Optional API version](#optional-api-version). |
|
62
71
|
|
63
72
|
Calling `path` on the client object returns an instance of `Cloudimage::URI`.
|
64
73
|
It accepts path to the image as a string and we we will use it to build
|
@@ -101,7 +110,7 @@ uri.heigth(200).to_url
|
|
101
110
|
This is useful for catching typos and identifying deprecated methods in
|
102
111
|
case Cloudimage's API changes.
|
103
112
|
|
104
|
-
###
|
113
|
+
### Method aliases
|
105
114
|
|
106
115
|
The gem comes with a handful of useful aliases. Consult
|
107
116
|
[`Cloudimage::Params`](lib/cloudimage/params.rb) module for their full list.
|
@@ -119,8 +128,76 @@ need to accept arguments and will be translated into `param=1` in the final URL.
|
|
119
128
|
For a list of custom helpers available to you, please consult
|
120
129
|
[`Cloudimage::CustomHelpers`](lib/cloudimage/custom_helpers.rb) module.
|
121
130
|
|
131
|
+
### URL aliases
|
132
|
+
|
133
|
+
Specify [aliases][aliases] to automatically replace parts of path with defined values.
|
134
|
+
Aliases is a hash which maps strings to be replaced with values to be used instead.
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
my_alias = 'https://store.s3-us-west-2.amazonaws.com/uploads'
|
138
|
+
client = Cloudimage::Client.new(token: 'token', aliases: { my_alias => '_uploads_' })
|
139
|
+
client.path('https://store.s3-us-west-2.amazonaws.com/uploads/image.jpg').to_url
|
140
|
+
# => "https://token.cloudimg.io/v7/_uploads_/image.jpg"
|
141
|
+
```
|
142
|
+
|
143
|
+
[URL prefix][url-prefix] is just another form of URL alias. Simply make the target value an empty string:
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
prefix = 'https://store.s3-us-west-2.amazonaws.com/uploads/'
|
147
|
+
client = Cloudimage::Client.new(token: 'token', aliases: { prefix => '' })
|
148
|
+
client.path('https://store.s3-us-west-2.amazonaws.com/uploads/image.jpg').to_url
|
149
|
+
# => "https://token.cloudimg.io/v7/image.jpg"
|
150
|
+
```
|
151
|
+
|
152
|
+
You don't need to specify an alias if the input to `path` is a URL that
|
153
|
+
already matches the base of the generated URL:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
client = Cloudimage::Client.new(token: 'token')
|
157
|
+
client.path('https://token.cloudimg.io/v7/image.jpg').to_url(w: 200)
|
158
|
+
=> "https://token.cloudimg.io/v7/image.jpg?w=200"
|
159
|
+
```
|
160
|
+
|
161
|
+
### `srcset` generation
|
162
|
+
|
163
|
+
Use the provided `to_srcset` method which also accepts any additional params to
|
164
|
+
be applied to the `srcset` URLs:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
client = Cloudimage::Client.new(token: 'token')
|
168
|
+
client.path('/assets/image.jpg').to_srcset(blur: 5)
|
169
|
+
# => "https://token.cloudimg.io/v7/assets/image.jpg?blur=5&w=100 100w, https://token.cloudimg.io/v7/assets/image.jpg?blur=5&w=170 170w, https://token.cloudimg.io/v7/assets/image.jpg?blur=5&w=280 280w, https://token.cloudimg.io/v7/assets/image.jpg?blur=5&w=470 470w, https://token.cloudimg.io/v7/assets/image.jpg?blur=5&w=780 780w, https://token.cloudimg.io/v7/assets/image.jpg?blur=5&w=1300 1300w, https://token.cloudimg.io/v7/assets/image.jpg?blur=5&w=2170 2170w, https://token.cloudimg.io/v7/assets/image.jpg?blur=5&w=3620 3620w, https://token.cloudimg.io/v7/assets/image.jpg?blur=5&w=5760 5760w"
|
170
|
+
```
|
171
|
+
|
172
|
+
A growth factor is applied to exponentially distribute widths between 100 and 5760 pixels.
|
173
|
+
See [`Cloudimage::Srcset`](/lib/cloudimage/srcset.rb) for implementation details.
|
174
|
+
|
175
|
+
### CNAME
|
176
|
+
|
177
|
+
If you have a custom CNAME configured for your account, you can
|
178
|
+
use it to initialize the client:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
client = Cloudimage::Client.new(cname: 'img.klimo.io')
|
182
|
+
client.path('/assets/image.jpg').to_url
|
183
|
+
# => 'https://img.klimo.io/v7/assets/image.jpg'
|
184
|
+
```
|
185
|
+
|
186
|
+
### Optional API version
|
187
|
+
|
188
|
+
If your account is configured to work without the API version component in the URL,
|
189
|
+
you can configure client not to include it in the generated URL:
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
client = Cloudimage::Client.new(cname: 'img.klimo.io', include_api_version: false)
|
193
|
+
client.path('/assets/image.jpg').to_url
|
194
|
+
# => "https://img.klimo.io/assets/image.jpg"
|
195
|
+
```
|
196
|
+
|
122
197
|
### Security
|
123
198
|
|
199
|
+
#### URL signature
|
200
|
+
|
124
201
|
If `salt` is defined, all URLs will be signed.
|
125
202
|
|
126
203
|
You can control the length of the generated signature by specifying `signature_length`
|
@@ -133,6 +210,67 @@ uri.w(200).h(400).to_url
|
|
133
210
|
# => "https://mysecrettoken.cloudimg.io/v7/assets/image.png?h=400&w=200&ci_sign=79cfbc458b"
|
134
211
|
```
|
135
212
|
|
213
|
+
#### URL sealing
|
214
|
+
|
215
|
+
Whereas URL signatures let you protect your URL from any kind of
|
216
|
+
tampering, URL sealing protects the params you specify while making
|
217
|
+
it possible to append additional params on the fly.
|
218
|
+
|
219
|
+
This is useful when working with Cloudimage's [responsive frontend libraries][responsive-images].
|
220
|
+
A common use case would be sealing your watermark but letting the
|
221
|
+
React client request the best possible width.
|
222
|
+
|
223
|
+
To seal your URLs, initialize client with `salt` and set
|
224
|
+
`sign_urls` to `false`. `signature_length` setting is applied
|
225
|
+
to control the length of the generated `ci_seal` value.
|
226
|
+
|
227
|
+
Use the `seal_params` helper to specify which params to seal
|
228
|
+
as a list of arguments. These could be symbols or strings.
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
client = Cloudimage::Client.new(token: 'demoseal', salt: 'test', sign_urls: false)
|
232
|
+
|
233
|
+
client
|
234
|
+
.path('/sample.li/birds.jpg')
|
235
|
+
.f('bright:10,contrast:20')
|
236
|
+
.w(300)
|
237
|
+
.h(400)
|
238
|
+
.seal_params(:w, :f)
|
239
|
+
.to_url
|
240
|
+
# => "https://demoseal.cloudimg.io/v7/sample.li/birds.jpg?ci_eqs=Zj1icmlnaHQlM0ExMCUyQ2NvbnRyYXN0JTNBMjAmdz0zMDA&ci_seal=67dd8cc44f6ba44ee5&h=400"
|
241
|
+
|
242
|
+
# Alternative approach:
|
243
|
+
client
|
244
|
+
.path('/sample.li/birds.jpg')
|
245
|
+
.to_url(f: 'bright:10,contrast:20', w: 300, h: 400, seal_params: [:w, :f])
|
246
|
+
# => "https://demoseal.cloudimg.io/v7/sample.li/birds.jpg?ci_eqs=Zj1icmlnaHQlM0ExMCUyQ2NvbnRyYXN0JTNBMjAmdz0zMDA&ci_seal=67dd8cc44f6ba44ee5&h=400"
|
247
|
+
```
|
248
|
+
|
249
|
+
This approach protects `w` and `f` values from being edited but
|
250
|
+
makes it possible to freely modify the value of `h`.
|
251
|
+
|
252
|
+
### Invalidation API
|
253
|
+
|
254
|
+
To access invalidation API you'll need to initialize client with
|
255
|
+
an API key.
|
256
|
+
|
257
|
+
The provided helper methods accept any number of strings:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
client = Cloudimage::Client.new(token: 'token', api_key: 'key')
|
261
|
+
|
262
|
+
# Invalidate original
|
263
|
+
client.invalidate_original('/v7/image.jpg')
|
264
|
+
|
265
|
+
# Invalidate URLs
|
266
|
+
client.invalidate_urls('/v7/image.jpg?w=200', '/v7/image.jpg?h=300')
|
267
|
+
|
268
|
+
# Invalidate all
|
269
|
+
client.invalidate_all
|
270
|
+
```
|
271
|
+
|
272
|
+
Consult the [invalidation API docs][invalidation-docs] for further details.
|
273
|
+
|
136
274
|
## Development
|
137
275
|
|
138
276
|
After checking out the repo, run `bin/setup` to install dependencies.
|
@@ -140,27 +278,29 @@ Then, run `bundle exec rake` to run the tests. You can also run
|
|
140
278
|
`bin/console` for an interactive prompt that will allow you to
|
141
279
|
experiment.
|
142
280
|
|
143
|
-
### TODOs
|
144
|
-
|
145
|
-
- URL sealing
|
146
|
-
- Add support for custom CNAMEs
|
147
|
-
- `srcset` generation
|
148
|
-
- Purge cache API
|
149
|
-
|
150
281
|
## Contributing
|
151
282
|
|
152
283
|
Bug reports and pull requests are welcome. This project is intended
|
153
284
|
to be a safe, welcoming space for collaboration, and contributors
|
154
|
-
are expected to adhere to the
|
155
|
-
[code of conduct](https://github.com/scaleflex/cloudimage-rb/blob/master/CODE_OF_CONDUCT.md).
|
285
|
+
are expected to adhere to the [code of conduct][code-of-conduct].
|
156
286
|
|
157
287
|
## License
|
158
288
|
|
159
|
-
|
160
|
-
|
289
|
+
[MIT](https://opensource.org/licenses/MIT)
|
290
|
+
|
291
|
+
## Showcase
|
292
|
+
|
293
|
+
Among others, `cloudimage` is used to power the following apps:
|
294
|
+
|
295
|
+
- [Robin PRO][robin-pro] - Fast, beautiful, mobile-friendly image galleries for Shopify stores.
|
161
296
|
|
162
|
-
|
297
|
+
Using this gem in your app? Let us know in [this issue](https://github.com/scaleflex/cloudimage-rb/issues/8)
|
298
|
+
so that we can feature it.
|
163
299
|
|
164
|
-
|
165
|
-
|
166
|
-
[
|
300
|
+
[aliases]: https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/domains-urls/aliases
|
301
|
+
[code-of-conduct]: https://github.com/scaleflex/cloudimage-rb/blob/master/CODE_OF_CONDUCT.md
|
302
|
+
[docs]: https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/introduction
|
303
|
+
[invalidation-docs]: https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/caching-acceleration/invalidation-api
|
304
|
+
[responsive-images]: https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/responsive-images
|
305
|
+
[robin-pro]: https://apps.shopify.com/robin-pro-image-gallery
|
306
|
+
[url-prefix]: https://docs.cloudimage.io/go/cloudimage-documentation-v7/en/domains-urls/origin-url-prefix
|
data/lib/cloudimage.rb
CHANGED
data/lib/cloudimage/client.rb
CHANGED
@@ -1,25 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'uri'
|
4
|
+
require_relative 'invalidation'
|
4
5
|
|
5
6
|
module Cloudimage
|
6
7
|
class InvalidConfig < StandardError; end
|
7
8
|
|
8
9
|
class Client
|
10
|
+
include Invalidation
|
11
|
+
|
9
12
|
attr_reader :config
|
10
13
|
|
11
14
|
API_VERSION = 'v7'
|
12
15
|
DEFAULT_SIGNATURE_LENGTH = 18
|
13
16
|
|
14
17
|
def initialize(**options)
|
15
|
-
@config =
|
16
|
-
|
17
|
-
@config[:salt] = options[:salt]
|
18
|
-
@config[:signature_length] =
|
19
|
-
options[:signature_length] || DEFAULT_SIGNATURE_LENGTH
|
20
|
-
@config[:api_version] = API_VERSION
|
21
|
-
|
22
|
-
ensure_valid_config
|
18
|
+
@config = set_config_defaults(options)
|
19
|
+
validate_config
|
23
20
|
end
|
24
21
|
|
25
22
|
def path(path)
|
@@ -28,18 +25,31 @@ module Cloudimage
|
|
28
25
|
|
29
26
|
private
|
30
27
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
28
|
+
def set_config_defaults(options)
|
29
|
+
options.tap do |config|
|
30
|
+
config[:signature_length] =
|
31
|
+
options[:signature_length] || DEFAULT_SIGNATURE_LENGTH
|
32
|
+
config[:api_version] = API_VERSION
|
33
|
+
config[:include_api_version] =
|
34
|
+
options[:include_api_version].nil? ? true : false
|
35
|
+
config[:sign_urls] = options[:sign_urls].nil? ? true : false
|
36
|
+
config[:aliases] = options[:aliases] || {}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_config
|
41
|
+
validate_site_config
|
42
|
+
validate_signature_length
|
34
43
|
end
|
35
44
|
|
36
|
-
def
|
37
|
-
return unless config[:token].nil?
|
45
|
+
def validate_site_config
|
46
|
+
return unless config[:token].nil? && config[:cname].nil?
|
38
47
|
|
39
|
-
raise InvalidConfig,
|
48
|
+
raise InvalidConfig,
|
49
|
+
'Please specify your customer token or a custom CNAME.'
|
40
50
|
end
|
41
51
|
|
42
|
-
def
|
52
|
+
def validate_signature_length
|
43
53
|
return if config[:salt].nil?
|
44
54
|
return if (6..40).cover? config[:signature_length]
|
45
55
|
|
@@ -5,5 +5,11 @@ module Cloudimage
|
|
5
5
|
def positionable_crop(origin_x:, origin_y:, width:, height:)
|
6
6
|
tl_px(origin_x, origin_y).br_px(origin_x + width, origin_y + height)
|
7
7
|
end
|
8
|
+
|
9
|
+
def seal_params(*query_params)
|
10
|
+
# URI#query_values returns hash where keys are strings.
|
11
|
+
sealed_params.merge(query_params.map(&:to_s))
|
12
|
+
self
|
13
|
+
end
|
8
14
|
end
|
9
15
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Cloudimage
|
7
|
+
module Invalidation
|
8
|
+
ENDPOINT = ::URI.parse('https://api.cloudimage.com/invalidate')
|
9
|
+
|
10
|
+
%i[original urls all].each do |type|
|
11
|
+
define_method "invalidate_#{type}" do |*paths|
|
12
|
+
validate_api_key
|
13
|
+
|
14
|
+
body = {
|
15
|
+
scope: type,
|
16
|
+
}
|
17
|
+
|
18
|
+
body[:urls] = paths if paths.any?
|
19
|
+
|
20
|
+
send_request(body)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def validate_api_key
|
27
|
+
return if config[:api_key]
|
28
|
+
|
29
|
+
raise InvalidConfig, 'API key is required to perform cache invalidation.'
|
30
|
+
end
|
31
|
+
|
32
|
+
def headers
|
33
|
+
{
|
34
|
+
'X-Client-Key': config[:api_key],
|
35
|
+
'Content-Type': 'application/json',
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def send_request(body)
|
40
|
+
http = Net::HTTP.new(ENDPOINT.host, ENDPOINT.port)
|
41
|
+
http.use_ssl = true
|
42
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
43
|
+
|
44
|
+
request = Net::HTTP::Post.new(ENDPOINT.path, headers)
|
45
|
+
request.body = body.to_json
|
46
|
+
|
47
|
+
http.request(request)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/cloudimage/params.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cloudimage
|
4
|
+
module Refinements
|
5
|
+
unless {}.respond_to?(:slice)
|
6
|
+
refine Hash do
|
7
|
+
def slice(*keys)
|
8
|
+
keys.each_with_object({}) do |k, acc|
|
9
|
+
acc[k] = self[k] if key?(k)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
unless ''.respond_to?(:delete_prefix)
|
16
|
+
refine String do
|
17
|
+
def delete_prefix(prefix)
|
18
|
+
sub(/\A#{prefix}/, '')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
unless Kernel.respond_to?(:yield_self)
|
24
|
+
refine Kernel do
|
25
|
+
def yield_self
|
26
|
+
unless block_given?
|
27
|
+
return ::Enumerator.new(1) do |yielder|
|
28
|
+
yielder.yield(self)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
yield(self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
unless Kernel.respond_to?(:then)
|
38
|
+
refine Kernel do
|
39
|
+
def then
|
40
|
+
return yield_self unless block_given?
|
41
|
+
|
42
|
+
yield_self(&::Proc.new)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
require_relative 'refinements'
|
7
|
+
|
8
|
+
module Cloudimage
|
9
|
+
class Security
|
10
|
+
using Refinements
|
11
|
+
|
12
|
+
attr_reader :uri, :config
|
13
|
+
|
14
|
+
def initialize(uri, **config)
|
15
|
+
@uri = uri
|
16
|
+
@config = config
|
17
|
+
end
|
18
|
+
|
19
|
+
def sign_url(request_uri)
|
20
|
+
query = uri.query_values || {}
|
21
|
+
uri.query_values = query.merge(ci_sign: signature(request_uri))
|
22
|
+
end
|
23
|
+
|
24
|
+
def seal_url(path, sealed_params)
|
25
|
+
query = uri.query_values || {}
|
26
|
+
sealed_query = query.slice(*sealed_params)
|
27
|
+
query.keep_if { |k, _| !sealed_query.key?(k) }
|
28
|
+
eqs = eqs(sealed_query)
|
29
|
+
query[:ci_eqs] = eqs unless eqs.empty?
|
30
|
+
query[:ci_seal] = seal(path, eqs)
|
31
|
+
uri.query_values = query
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def signature(request_uri)
|
37
|
+
digest = Digest::SHA1.hexdigest(config[:salt] + request_uri)
|
38
|
+
trim(digest, config[:signature_length])
|
39
|
+
end
|
40
|
+
|
41
|
+
def eqs(query_params)
|
42
|
+
uri = Addressable::URI.new
|
43
|
+
uri.query_values = query_params
|
44
|
+
Base64.urlsafe_encode64(uri.query, padding: false)
|
45
|
+
end
|
46
|
+
|
47
|
+
def seal(path, eqs)
|
48
|
+
digest = Digest::SHA1.hexdigest(path + eqs + config[:salt])
|
49
|
+
trim(digest, config[:signature_length])
|
50
|
+
end
|
51
|
+
|
52
|
+
def trim(signature, length)
|
53
|
+
signature[0, length]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cloudimage
|
4
|
+
module Srcset
|
5
|
+
# 5760 is 3x 1920 (the largest common screen width):
|
6
|
+
# https://gs.statcounter.com/screen-resolution-stats
|
7
|
+
SRCSET_RANGE = (100..5_760).freeze
|
8
|
+
SRCSET_GROWTH_FACTOR = 1.67
|
9
|
+
|
10
|
+
def to_srcset(**extra_params)
|
11
|
+
srcset_widths
|
12
|
+
.map { |width| "#{to_url(**extra_params, w: width)} #{width}w" }
|
13
|
+
.join(', ')
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def srcset_widths
|
19
|
+
current = SRCSET_RANGE.begin
|
20
|
+
|
21
|
+
[].tap do |widths|
|
22
|
+
loop do
|
23
|
+
widths << current
|
24
|
+
current = (current * SRCSET_GROWTH_FACTOR).round(-1)
|
25
|
+
|
26
|
+
break if current >= SRCSET_RANGE.end && widths << SRCSET_RANGE.end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/cloudimage/uri.rb
CHANGED
@@ -1,31 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'set'
|
4
4
|
|
5
5
|
require_relative 'params'
|
6
6
|
require_relative 'custom_helpers'
|
7
|
+
require_relative 'security'
|
8
|
+
require_relative 'srcset'
|
9
|
+
require_relative 'refinements'
|
7
10
|
|
8
11
|
module Cloudimage
|
9
12
|
class URI
|
13
|
+
using Refinements
|
14
|
+
|
10
15
|
include Params
|
11
16
|
include CustomHelpers
|
17
|
+
include Srcset
|
12
18
|
|
13
|
-
attr_reader :uri, :params, :config
|
19
|
+
attr_reader :path, :uri, :params, :config, :sealed_params
|
14
20
|
|
15
21
|
def initialize(path, **config)
|
16
22
|
@config = config
|
17
23
|
@params = {}
|
18
|
-
@
|
24
|
+
@sealed_params = Set.new
|
25
|
+
@path = transform(path)
|
26
|
+
@uri = build_uri
|
19
27
|
end
|
20
28
|
|
21
29
|
PARAMS.each do |param|
|
22
30
|
define_method param do |*args|
|
23
|
-
|
24
|
-
|
25
|
-
else
|
26
|
-
# Flag params don't need to pass in arguments.
|
27
|
-
@params[param] = 1
|
28
|
-
end
|
31
|
+
# Flag params don't need to pass in arguments.
|
32
|
+
params[param] = args.any? ? args.join(',') : 1
|
29
33
|
self
|
30
34
|
end
|
31
35
|
end
|
@@ -36,43 +40,72 @@ module Cloudimage
|
|
36
40
|
|
37
41
|
def to_url(**extra_params)
|
38
42
|
set_uri_params(**extra_params)
|
39
|
-
|
43
|
+
secure_url
|
44
|
+
uri.to_s
|
40
45
|
end
|
41
46
|
|
42
47
|
private
|
43
48
|
|
44
|
-
def
|
49
|
+
def site
|
50
|
+
return "https://#{config[:cname]}" if config[:cname]
|
51
|
+
|
45
52
|
"https://#{config[:token]}.cloudimg.io"
|
46
53
|
end
|
47
54
|
|
48
|
-
def
|
49
|
-
"
|
55
|
+
def api_version
|
56
|
+
"/#{config[:api_version]}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def transform(path)
|
60
|
+
path
|
61
|
+
.then { |input| input.start_with?('/') ? input : "/#{input}" }
|
62
|
+
.then(&method(:apply_aliases))
|
63
|
+
end
|
64
|
+
|
65
|
+
def apply_aliases(path)
|
66
|
+
config[:aliases][default_alias] = ''
|
67
|
+
|
68
|
+
path.dup.tap do |input|
|
69
|
+
config[:aliases].each do |source, target|
|
70
|
+
input.sub!(source, target)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def default_alias
|
76
|
+
config[:include_api_version] ? "#{site}#{api_version}/" : "#{site}/"
|
77
|
+
end
|
78
|
+
|
79
|
+
def request_uri
|
80
|
+
uri.request_uri.delete_prefix(api_version)
|
50
81
|
end
|
51
82
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
83
|
+
def build_uri
|
84
|
+
if config[:include_api_version]
|
85
|
+
Addressable::URI.parse(site + api_version + path)
|
86
|
+
else
|
87
|
+
Addressable::URI.parse(site + path)
|
88
|
+
end
|
55
89
|
end
|
56
90
|
|
57
91
|
def set_uri_params(**extra_params)
|
92
|
+
seal_params(*extra_params.delete(:seal_params))
|
58
93
|
url_params = params.merge(**extra_params)
|
59
94
|
return unless url_params.any?
|
60
95
|
|
61
96
|
uri.query_values = url_params
|
62
97
|
end
|
63
98
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
return url if config[:salt].nil?
|
99
|
+
def secure_url
|
100
|
+
return uri.to_s if config[:salt].nil?
|
68
101
|
|
69
|
-
|
70
|
-
end
|
102
|
+
security = Security.new(uri, **config)
|
71
103
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
104
|
+
if config[:sign_urls]
|
105
|
+
security.sign_url(request_uri)
|
106
|
+
else
|
107
|
+
security.seal_url(path, sealed_params)
|
108
|
+
end
|
76
109
|
end
|
77
110
|
end
|
78
111
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudimage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Klimo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: github_changelog_generator
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.15.2
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.15.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.13'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.13'
|
27
55
|
description: Fast and easy image resizing, transformation, and acceleration in the
|
28
56
|
Cloud.
|
29
57
|
email:
|
@@ -40,9 +68,12 @@ files:
|
|
40
68
|
- lib/cloudimage.rb
|
41
69
|
- lib/cloudimage/client.rb
|
42
70
|
- lib/cloudimage/custom_helpers.rb
|
71
|
+
- lib/cloudimage/invalidation.rb
|
43
72
|
- lib/cloudimage/params.rb
|
73
|
+
- lib/cloudimage/refinements.rb
|
74
|
+
- lib/cloudimage/security.rb
|
75
|
+
- lib/cloudimage/srcset.rb
|
44
76
|
- lib/cloudimage/uri.rb
|
45
|
-
- lib/cloudimage/version.rb
|
46
77
|
homepage: https://github.com/scaleflex/cloudimage-rb
|
47
78
|
licenses:
|
48
79
|
- MIT
|