fileboost 0.1.1 → 0.1.5
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/README.md +0 -12
- data/Rakefile +4 -5
- data/lib/fileboost/config.rb +2 -2
- data/lib/fileboost/engine.rb +0 -6
- data/lib/fileboost/error_handler.rb +1 -51
- data/lib/fileboost/helpers.rb +7 -25
- data/lib/fileboost/signature_generator.rb +2 -27
- data/lib/fileboost/url_builder.rb +25 -42
- data/lib/fileboost/version.rb +1 -1
- data/lib/generators/fileboost/install_generator.rb +2 -2
- metadata +47 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4fec62d9be1b012648ceccd75eaa24a75c64ce4d3ae2e4d3fa060e08960de70
|
4
|
+
data.tar.gz: 67fbe1d13e4ec43b47c9517b3acddff6fc7a35a930b670cd15be781474d4824f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: add42effb51bb5a83eaf754365d703ab6c9161f2c8aa7b7297b1aee99194e02f8b706eae6cf04e2f9137683c13c52dc5aa57451a94a7d5dffaa7246b479508a9
|
7
|
+
data.tar.gz: 56ebbea598301c491c2cfef190c9cdb15716b9621874beb8e5f3a007c4da2ba9c27303cf1784cd89a35daed9039ec81f961849576f588bed975ccebfdd675a15
|
data/README.md
CHANGED
@@ -205,17 +205,6 @@ $ bundle exec rubocop
|
|
205
205
|
6. Push to the branch (`git push origin my-new-feature`)
|
206
206
|
7. Create new Pull Request
|
207
207
|
|
208
|
-
## Fileboost.dev Service
|
209
|
-
|
210
|
-
To use Fileboost, you'll need access to the Fileboost.dev image optimization service at `cdn.fileboost.dev`. The service:
|
211
|
-
|
212
|
-
1. Receives requests at `https://cdn.fileboost.dev/{project_id}/path/to/activestorage/blob`
|
213
|
-
2. Verifies HMAC signatures using your secret token
|
214
|
-
3. Applies transformations based on query parameters
|
215
|
-
4. Returns optimized images
|
216
|
-
|
217
|
-
The service handles ActiveStorage blob URLs and applies image transformations on-the-fly.
|
218
|
-
|
219
208
|
## License
|
220
209
|
|
221
210
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -224,4 +213,3 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
224
213
|
|
225
214
|
- [GitHub Issues](https://github.com/bilalbudhani/fileboost/issues)
|
226
215
|
- [Documentation](https://github.com/bilalbudhani/fileboost/wiki)
|
227
|
-
- [Cloudflare Worker Setup Guide](https://github.com/bilalbudhani/fileboost-worker)
|
data/Rakefile
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
require "bundler/setup"
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rspec/core/rake_task"
|
2
4
|
|
3
|
-
|
4
|
-
load "rails/tasks/engine.rake"
|
5
|
-
|
6
|
-
load "rails/tasks/statistics.rake"
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
6
|
|
8
|
-
|
7
|
+
task default: :spec
|
data/lib/fileboost/config.rb
CHANGED
@@ -11,7 +11,7 @@ module Fileboost
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def valid?
|
14
|
-
project_id.
|
14
|
+
!project_id.empty? && !token.empty?
|
15
15
|
end
|
16
16
|
|
17
17
|
def base_url
|
@@ -26,4 +26,4 @@ module Fileboost
|
|
26
26
|
def self.configure
|
27
27
|
yield(config) if block_given?
|
28
28
|
end
|
29
|
-
end
|
29
|
+
end
|
data/lib/fileboost/engine.rb
CHANGED
@@ -1,57 +1,7 @@
|
|
1
1
|
module Fileboost
|
2
|
-
class ErrorHandler
|
3
|
-
class << self
|
4
|
-
def handle_with_fallback(error_context, &block)
|
5
|
-
begin
|
6
|
-
yield
|
7
|
-
rescue StandardError => e
|
8
|
-
log_error(error_context, e)
|
9
|
-
|
10
|
-
if Fileboost.config.fallback_to_rails
|
11
|
-
yield_fallback if block_given?
|
12
|
-
else
|
13
|
-
nil
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def handle_gracefully(error_context, default_value = nil, &block)
|
19
|
-
begin
|
20
|
-
yield
|
21
|
-
rescue StandardError => e
|
22
|
-
log_error(error_context, e)
|
23
|
-
default_value
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def log_error(context, error)
|
30
|
-
return unless defined?(Rails) && Rails.logger
|
31
|
-
|
32
|
-
Rails.logger.warn(
|
33
|
-
"[Fileboost] Error in #{context}: #{error.class}: #{error.message}"
|
34
|
-
)
|
35
|
-
|
36
|
-
# Log backtrace in development for debugging
|
37
|
-
if Rails.env.development?
|
38
|
-
Rails.logger.debug(
|
39
|
-
"[Fileboost] Backtrace:\n#{error.backtrace.take(5).join("\n")}"
|
40
|
-
)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def yield_fallback
|
45
|
-
# This would be implemented by the calling code
|
46
|
-
# The pattern is to pass a fallback block when needed
|
47
|
-
nil
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
2
|
# Specific exception classes for better error handling
|
53
3
|
class ConfigurationError < StandardError; end
|
54
4
|
class SignatureGenerationError < StandardError; end
|
55
5
|
class UrlBuildError < StandardError; end
|
56
6
|
class AssetPathExtractionError < StandardError; end
|
57
|
-
end
|
7
|
+
end
|
data/lib/fileboost/helpers.rb
CHANGED
@@ -18,9 +18,6 @@ module Fileboost
|
|
18
18
|
# Generate the optimized URL
|
19
19
|
optimized_url = fileboost_url_for(asset, resize: resize_options)
|
20
20
|
|
21
|
-
# Return empty string if no URL could be generated
|
22
|
-
return "" if optimized_url.blank?
|
23
|
-
|
24
21
|
# Use the optimized URL with Rails image_tag for consistency
|
25
22
|
image_tag(optimized_url, **options)
|
26
23
|
end
|
@@ -36,16 +33,10 @@ module Fileboost
|
|
36
33
|
# fileboost_url_for(user.avatar.blob, resize: { w: 1200, h: 400, q: 85 })
|
37
34
|
def fileboost_url_for(asset, **options)
|
38
35
|
# Validate that asset is an ActiveStorage object
|
39
|
-
unless valid_activestorage_asset?(asset)
|
40
|
-
Rails.logger.error("[Fileboost] Invalid asset type #{asset.class}. Only ActiveStorage objects are supported.") if defined?(Rails)
|
41
|
-
return nil
|
42
|
-
end
|
36
|
+
raise ArgumentError, "Invalid asset type #{asset.class}. Only ActiveStorage objects are supported." unless valid_activestorage_asset?(asset)
|
43
37
|
|
44
38
|
# Validate configuration
|
45
|
-
unless Fileboost.config.valid?
|
46
|
-
log_configuration_warning
|
47
|
-
return nil
|
48
|
-
end
|
39
|
+
raise Fileboost::ConfigurationError, "Invalid Fileboost configuration" unless Fileboost.config.valid?
|
49
40
|
|
50
41
|
# Build the optimized URL
|
51
42
|
Fileboost::UrlBuilder.build_url(asset, **options)
|
@@ -71,10 +62,13 @@ module Fileboost
|
|
71
62
|
sizes.each do |size_config|
|
72
63
|
suffix = size_config[:suffix] || size_config["suffix"]
|
73
64
|
size_options = size_config.except(:suffix, "suffix")
|
74
|
-
|
65
|
+
|
66
|
+
# Merge size options into base resize options
|
67
|
+
merged_resize_options = (base_options[:resize] || {}).merge(size_options)
|
68
|
+
combined_options = base_options.merge(resize: merged_resize_options)
|
75
69
|
|
76
70
|
url = fileboost_url_for(asset, **combined_options)
|
77
|
-
urls[suffix] = url if url.
|
71
|
+
urls[suffix] = url if !url.nil? && !url.empty?
|
78
72
|
end
|
79
73
|
|
80
74
|
urls
|
@@ -91,17 +85,5 @@ module Fileboost
|
|
91
85
|
|
92
86
|
false
|
93
87
|
end
|
94
|
-
|
95
|
-
# Log configuration warnings
|
96
|
-
def log_configuration_warning
|
97
|
-
missing_configs = []
|
98
|
-
missing_configs << "project_id" if Fileboost.config.project_id.blank?
|
99
|
-
missing_configs << "token" if Fileboost.config.token.blank?
|
100
|
-
|
101
|
-
Rails.logger.warn(
|
102
|
-
"[Fileboost] Configuration incomplete. Missing: #{missing_configs.join(', ')}. " \
|
103
|
-
"Set FILEBOOST_PROJECT_ID and FILEBOOST_TOKEN environment variables or configure them in your initializer."
|
104
|
-
) if defined?(Rails)
|
105
|
-
end
|
106
88
|
end
|
107
89
|
end
|
@@ -3,42 +3,17 @@ require "base64"
|
|
3
3
|
|
4
4
|
module Fileboost
|
5
5
|
class SignatureGenerator
|
6
|
-
def self.generate(
|
7
|
-
return nil unless project_id.present? && asset_path.present? && Fileboost.config.token.present?
|
8
|
-
|
6
|
+
def self.generate(asset_path:, params: {})
|
9
7
|
# Sort parameters for consistent signature generation
|
10
8
|
sorted_params = params.sort.to_h
|
11
9
|
query_string = sorted_params.map { |k, v| "#{k}=#{v}" }.join("&")
|
12
10
|
|
13
11
|
# Create the signing string: project_id:asset_path:sorted_query_params
|
14
|
-
signing_string = [project_id, asset_path, query_string].join(":")
|
15
|
-
Rails.logger.debug("signature payload #{project_id}, #{asset_path}, #{query_string}, #{Fileboost.config.token}")
|
12
|
+
signing_string = [ Fileboost.config.project_id, asset_path, query_string ].join(":")
|
16
13
|
# Generate HMAC-SHA256 signature for secure authentication with Fileboost.dev
|
17
14
|
digest = OpenSSL::HMAC.digest("SHA256", Fileboost.config.token, signing_string)
|
18
15
|
# Use URL-safe base64 encoding and remove padding for maximum URL compatibility
|
19
16
|
Base64.urlsafe_encode64(digest, padding: false)
|
20
|
-
rescue StandardError => e
|
21
|
-
if defined?(Rails) && Rails.env.development?
|
22
|
-
raise e
|
23
|
-
else
|
24
|
-
Rails.logger.warn("[Fileboost] Failed to generate signature: #{e.message}") if defined?(Rails)
|
25
|
-
nil
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.verify_signature(project_id:, asset_path:, params: {}, signature:)
|
30
|
-
expected_signature = generate(project_id: project_id, asset_path: asset_path, params: params)
|
31
|
-
return false if expected_signature.nil? || signature.nil?
|
32
|
-
|
33
|
-
# Use secure comparison to prevent timing attacks
|
34
|
-
ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
|
35
|
-
rescue StandardError => e
|
36
|
-
if defined?(Rails) && Rails.env.development?
|
37
|
-
raise e
|
38
|
-
else
|
39
|
-
Rails.logger.warn("[Fileboost] Failed to verify signature: #{e.message}") if defined?(Rails)
|
40
|
-
false
|
41
|
-
end
|
42
17
|
end
|
43
18
|
end
|
44
19
|
end
|
@@ -31,44 +31,36 @@ module Fileboost
|
|
31
31
|
RESIZE_PARAMS = %w[w width h height q quality f format b blur br brightness c contrast r rotation fit].freeze
|
32
32
|
|
33
33
|
def self.build_url(asset, **options)
|
34
|
-
|
35
|
-
|
34
|
+
raise ConfigurationError, "Invalid configuration" unless Fileboost.config.valid?
|
35
|
+
|
36
36
|
asset_path = extract_asset_path(asset)
|
37
|
-
|
37
|
+
raise AssetPathExtractionError, "Unable to extract asset path" unless !asset_path.nil? && !asset_path.empty?
|
38
38
|
|
39
39
|
project_id = Fileboost.config.project_id
|
40
40
|
base_url = Fileboost.config.base_url
|
41
|
-
|
41
|
+
|
42
42
|
# Build the full asset URL path for Fileboost.dev service
|
43
43
|
full_path = "/#{project_id}#{asset_path}"
|
44
|
-
|
44
|
+
|
45
45
|
# Extract and normalize transformation parameters
|
46
46
|
transformation_params = extract_transformation_params(options)
|
47
|
-
|
47
|
+
|
48
48
|
# Generate HMAC signature for secure authentication
|
49
49
|
signature = Fileboost::SignatureGenerator.generate(
|
50
|
-
project_id: project_id,
|
51
50
|
asset_path: asset_path,
|
52
51
|
params: transformation_params
|
53
52
|
)
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
|
54
|
+
raise SignatureGenerationError, "Failed to generate signature" unless signature
|
55
|
+
|
57
56
|
# Add signature to parameters
|
58
57
|
all_params = transformation_params.merge("sig" => signature)
|
59
|
-
|
58
|
+
|
60
59
|
# Build final URL
|
61
60
|
uri = URI.join(base_url, full_path)
|
62
61
|
uri.query = URI.encode_www_form(all_params) unless all_params.empty?
|
63
|
-
|
62
|
+
|
64
63
|
uri.to_s
|
65
|
-
rescue StandardError => e
|
66
|
-
if defined?(Rails) && Rails.env.development?
|
67
|
-
raise e
|
68
|
-
else
|
69
|
-
Rails.logger.warn("[Fileboost] Failed to build URL: #{e.message}") if defined?(Rails)
|
70
|
-
nil
|
71
|
-
end
|
72
64
|
end
|
73
65
|
|
74
66
|
private
|
@@ -78,57 +70,48 @@ module Fileboost
|
|
78
70
|
when ActiveStorage::Blob
|
79
71
|
# ActiveStorage Blob
|
80
72
|
Rails.application.routes.url_helpers.rails_blob_path(asset, only_path: true)
|
81
|
-
|
73
|
+
|
82
74
|
when ActiveStorage::Attached
|
83
75
|
# ActiveStorage Attachment (has_one_attached, has_many_attached)
|
84
|
-
if asset.respond_to?(:blob) && asset.blob.
|
76
|
+
if asset.respond_to?(:blob) && !asset.blob.nil?
|
85
77
|
Rails.application.routes.url_helpers.rails_blob_path(asset.blob, only_path: true)
|
86
78
|
else
|
87
|
-
|
88
|
-
nil
|
79
|
+
raise AssetPathExtractionError, "ActiveStorage attachment has no blob"
|
89
80
|
end
|
90
|
-
|
81
|
+
|
91
82
|
when ActiveStorage::VariantWithRecord
|
92
83
|
# ActiveStorage Variant - use blob URL to avoid triggering variant generation
|
93
84
|
Rails.application.routes.url_helpers.rails_blob_path(asset.blob, only_path: true)
|
94
|
-
|
85
|
+
|
95
86
|
else
|
96
87
|
# Only ActiveStorage objects are supported
|
97
|
-
|
98
|
-
nil
|
99
|
-
end
|
100
|
-
rescue StandardError => e
|
101
|
-
if defined?(Rails) && Rails.env.development?
|
102
|
-
raise e
|
103
|
-
else
|
104
|
-
Rails.logger.warn("[Fileboost] Failed to extract asset path: #{e.message}") if defined?(Rails)
|
105
|
-
nil
|
88
|
+
raise AssetPathExtractionError, "Unsupported asset type: #{asset.class}. Only ActiveStorage objects are supported."
|
106
89
|
end
|
107
90
|
end
|
108
91
|
|
109
92
|
def self.extract_transformation_params(options)
|
110
93
|
params = {}
|
111
|
-
|
94
|
+
|
112
95
|
# Only handle nested resize parameter
|
113
96
|
if options[:resize].is_a?(Hash)
|
114
97
|
resize_options = options[:resize]
|
115
98
|
resize_options.each do |key, value|
|
116
99
|
key_str = key.to_s
|
117
|
-
|
100
|
+
|
118
101
|
# Only process valid resize parameters
|
119
102
|
next unless RESIZE_PARAMS.include?(key_str)
|
120
|
-
|
103
|
+
|
121
104
|
# Use alias if available
|
122
105
|
param_key = PARAM_ALIASES[key_str] || key_str
|
123
|
-
|
106
|
+
|
124
107
|
# Convert value to string and validate
|
125
108
|
param_value = normalize_param_value(param_key, value)
|
126
|
-
next if param_value.
|
127
|
-
|
109
|
+
next if param_value.nil? || param_value.empty?
|
110
|
+
|
128
111
|
params[param_key] = param_value
|
129
112
|
end
|
130
113
|
end
|
131
|
-
|
114
|
+
|
132
115
|
params
|
133
116
|
end
|
134
117
|
|
@@ -152,4 +135,4 @@ module Fileboost
|
|
152
135
|
end
|
153
136
|
end
|
154
137
|
end
|
155
|
-
end
|
138
|
+
end
|
data/lib/fileboost/version.rb
CHANGED
@@ -4,7 +4,7 @@ module Fileboost
|
|
4
4
|
module Generators
|
5
5
|
class InstallGenerator < Rails::Generators::Base
|
6
6
|
desc "Create Fileboost initializer file"
|
7
|
-
|
7
|
+
|
8
8
|
def self.source_root
|
9
9
|
@source_root ||= File.expand_path("templates", __dir__)
|
10
10
|
end
|
@@ -24,4 +24,4 @@ module Fileboost
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
|
-
end
|
27
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fileboost
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- bilal
|
@@ -29,28 +29,28 @@ dependencies:
|
|
29
29
|
requirements:
|
30
30
|
- - "~>"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '2'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
39
|
+
version: '2'
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: combustion
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '1
|
46
|
+
version: '1'
|
47
47
|
type: :development
|
48
48
|
prerelease: false
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '1
|
53
|
+
version: '1'
|
54
54
|
- !ruby/object:Gem::Dependency
|
55
55
|
name: rake
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
@@ -79,6 +79,48 @@ dependencies:
|
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: 8.0.0
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rspec
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '3.0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rspec-rails
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '6.0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '6.0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: sqlite3
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '2.0'
|
117
|
+
type: :development
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '2.0'
|
82
124
|
description: Fileboost provides drop-in replacement Rails image helpers with automatic
|
83
125
|
optimization through the Fileboost.dev service. Works exclusively with ActiveStorage
|
84
126
|
objects, features HMAC authentication, and comprehensive transformation support.
|