fireimg 1.0.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5250ba61c2e4e5c42e372bf3947a710a941eb6d1a3c208e466f3a42185931056
4
- data.tar.gz: bf9ec77b5168637ad0ca494cc74038b83b37a4b45c82810ab9dcc345b85a6571
3
+ metadata.gz: 5926fd58382eaafd20085a2f21c6857f81b6be3cd832cb73d80cd81497d59ebc
4
+ data.tar.gz: c8ff329a137b3e4a5e2783367f7a082f2c37ef71c5ee4cc77e29dc21c9321652
5
5
  SHA512:
6
- metadata.gz: 6632550c999b8450549eeda9214af9c853c2e49b29931eb2e25be227c86fd579e40f5fcf73a2ecc063e11121cb5e336d2ecb4169e41ea646b201c7d47bac899d
7
- data.tar.gz: f50017794cf2dfd83bfc463850d972eb8f7807356b40c0a59ee5d5c501486d856b3ffeb897513507eb0be6d3d0b6432a5c12fa6fb3afad59f7fefd93ea43174e
6
+ metadata.gz: 3473e296a6204d7c541eac38528ebb7088d77e9127d682237d343975acb895b21272ea780481dc7327515d62f135b99dde6a3b0a9db25ec2d83af3da97fb8fdb
7
+ data.tar.gz: 879d5d4c5cf51e878d1c140a2f291651a7391d8db7c9595879708371cbb2a87af861f525afc3c6cabb3391b845bbde91f15cf4d2adfe7097d4782cba2ee5701c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.0
4
+
5
+ - `Fireimg.upload` / `upload_many` presign then PUT, matching the FireImg CLI
6
+ - `presign` remains for apps that PUT from the client (NeedApp native/web)
7
+
3
8
  ## 1.0.0
4
9
 
5
10
  - Presign uploads (folder keys, optional `sizes` to pre-generate variants)
data/README.md CHANGED
@@ -8,7 +8,7 @@ Source of truth is developed in the private FireImg repo and mirrored here.
8
8
  ## Install
9
9
 
10
10
  ```ruby
11
- gem 'fireimg', '~> 1.0'
11
+ gem 'fireimg', '~> 1.1'
12
12
  ```
13
13
 
14
14
  ```bash
@@ -31,23 +31,35 @@ the block. `FIREIMG_API_BASE` and `FIREIMG_CDN_BASE` have the defaults above.
31
31
 
32
32
  ## Upload
33
33
 
34
+ `Fireimg.upload` does both steps the CLI does: request a presigned URL, then
35
+ PUT the bytes.
36
+
34
37
  ```ruby
35
- result = Fireimg.presign(
38
+ result = Fireimg.upload(
36
39
  filename: 'feed_items/43/uuid.jpg',
37
40
  content_type: 'image/jpeg',
41
+ path: '/tmp/photo.jpg', # or io: / body:
38
42
  sizes: [
39
- { width: 800, quality: 'medium' },
40
- { width: 160, height: 160, fit: 'cover', pos: 'center' }
43
+ { width: 800, quality: 'medium', fmt: 'webp' },
44
+ { width: 160, height: 160, fit: 'cover', pos: 'center', fmt: 'webp' }
41
45
  ]
42
46
  )
43
47
 
44
- # PUT raw bytes to result.upload_url with the same Content-Type.
48
+ result.image_key # => "feed_items/43/uuid.jpg"
49
+ result.cdn_url # => "https://img.fireimg.com/{project}/images/feed_items/43/uuid.jpg"
45
50
  ```
46
51
 
47
- `filename` may include folders. Optional `sizes` (up to 20) are generated
48
- asynchronously after the `PUT` and **replace** the project's default image
49
- params for that upload. Omit `sizes` to keep project defaults. Pass `sizes: []`
50
- to pre-generate only FireImg's 80px dashboard thumbnail.
52
+ `filename` may include folders. Provide the bytes with `path:`, `io:`, or
53
+ `body:`. Optional `sizes` (up to 20) are generated asynchronously after the
54
+ `PUT` and **replace** the project's default image params for that upload. Omit
55
+ `sizes` to keep project defaults. Pass `sizes: []` to pre-generate only
56
+ FireImg's 80px dashboard thumbnail.
57
+
58
+ `Fireimg.upload_many(files:, sizes:)` batches the same way as the CLI
59
+ (up to 20 files per presign request).
60
+
61
+ If the client PUTs directly (NeedApp native/web), use `Fireimg.presign` and
62
+ PUT to `result.upload_url` with the same `Content-Type`.
51
63
 
52
64
  ## Moderate and delete
53
65
 
@@ -46,6 +46,55 @@ module Fireimg
46
46
  rows.first
47
47
  end
48
48
 
49
+ # Presign + PUT, matching `fireimg upload` in the CLI.
50
+ # Provide the bytes with `path:`, `io:`, or `body:`.
51
+ def upload(filename:, content_type:, sizes: nil, **source)
52
+ rows = upload_many(
53
+ files: [source.merge(filename: filename, content_type: content_type)],
54
+ sizes: sizes
55
+ )
56
+ raise Error, 'FireImg upload returned no results' if rows.empty?
57
+
58
+ rows.first
59
+ end
60
+
61
+ def upload_many(files:, sizes: nil)
62
+ list = Array(files)
63
+ raise Error, 'files cannot be empty' if list.empty?
64
+
65
+ prepared = []
66
+ seen = {}
67
+ list.each do |file|
68
+ payload = file_payload(file)
69
+ name = payload['filename']
70
+ raise Error, "duplicate filename #{name.inspect}" if seen.key?(name)
71
+
72
+ seen[name] = true
73
+ prepared << {
74
+ payload: payload,
75
+ content_type: payload['contentType'],
76
+ bytes: read_bytes(file)
77
+ }
78
+ end
79
+
80
+ rows = presign_many(files: prepared.map { |item| item[:payload] }, sizes: sizes)
81
+ by_name = prepared.to_h { |item| [item[:payload]['filename'], item] }
82
+
83
+ rows.map do |row|
84
+ item = by_name[row.filename]
85
+ raise Error, "presign returned unexpected filename #{row.filename.inspect}" if item.nil?
86
+
87
+ put(row.upload_url, item[:content_type], item[:bytes])
88
+ image_key = row.filename.to_s.sub(%r{\A/+}, '')
89
+ UploadResult.new(
90
+ filename: image_key,
91
+ image_key: image_key,
92
+ cdn_url: public_url(image_key),
93
+ s3_key: row.s3_key
94
+ )
95
+ end
96
+ end
97
+
49
98
  def presign_many(files:, sizes: nil)
50
99
  payload = {
51
100
  'slug' => project,
@@ -88,6 +137,42 @@ module Fireimg
88
137
  )
89
138
  end
90
139
 
140
+ def read_bytes(file)
141
+ h = stringify_keys(file)
142
+ if (path = h['path']).to_s.strip != ''
143
+ File.binread(path)
144
+ elsif (io = h['io'])
145
+ io = io.to_io if io.respond_to?(:to_io) && !io.respond_to?(:read)
146
+ io.binmode if io.respond_to?(:binmode)
147
+ io.rewind if io.respond_to?(:rewind)
148
+ io.read
149
+ elsif !h['body'].nil?
150
+ bytes = h['body']
151
+ bytes.respond_to?(:b) ? bytes.b : bytes.to_s
152
+ else
153
+ raise Error, 'path, io, or body is required'
154
+ end
155
+ end
156
+
157
+ def put(upload_url, content_type, body)
158
+ raise Error, 'upload URL is required' if upload_url.to_s.strip.empty?
159
+
160
+ uri = URI.parse(upload_url)
161
+ http = Net::HTTP.new(uri.host, uri.port)
162
+ http.use_ssl = uri.scheme == 'https'
163
+ http.open_timeout = 5
164
+ http.read_timeout = 60
165
+ req = Net::HTTP::Put.new(uri.request_uri)
166
+ req['Content-Type'] = content_type
167
+ req.body = body
168
+ res = http.request(req)
169
+ return true if res.is_a?(Net::HTTPSuccess)
170
+
171
+ message = res.body.to_s.strip
172
+ message = res.message if message.empty?
173
+ raise Error, "FireImg PUT #{res.code}: #{message[0, 200]}"
174
+ end
175
+
91
176
  def query_string(options)
92
177
  params = []
93
178
  w = options[:width]
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fireimg
4
+ # Result of a completed upload (presign + PUT), matching the FireImg CLI.
5
+ UploadResult = Struct.new(:filename, :image_key, :cdn_url, :s3_key, keyword_init: true) do
6
+ def to_h
7
+ {
8
+ 'filename' => filename,
9
+ 'image_key' => image_key,
10
+ 'cdn_url' => cdn_url,
11
+ 's3_key' => s3_key
12
+ }
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fireimg
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/fireimg.rb CHANGED
@@ -5,6 +5,7 @@ require_relative 'fireimg/error'
5
5
  require_relative 'fireimg/configuration'
6
6
  require_relative 'fireimg/size'
7
7
  require_relative 'fireimg/presign_result'
8
+ require_relative 'fireimg/upload_result'
8
9
  require_relative 'fireimg/client'
9
10
 
10
11
  module Fireimg
@@ -40,6 +41,14 @@ module Fireimg
40
41
  client.url(image_key, **options)
41
42
  end
42
43
 
44
+ def upload(...)
45
+ client.upload(...)
46
+ end
47
+
48
+ def upload_many(...)
49
+ client.upload_many(...)
50
+ end
51
+
43
52
  def presign(...)
44
53
  client.presign(...)
45
54
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fireimg
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Firelit Studio
@@ -11,10 +11,10 @@ cert_chain: []
11
11
  date: 2026-08-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
- Server-side Ruby client for FireImg. Request presigned uploads (including
14
+ Server-side Ruby client for FireImg. Upload images (presign + PUT, including
15
15
  folder keys and optional pre-generated sizes), run content moderation, and
16
- build CDN URLs. Matches the FireImg API-key HTTP API and @fireimg/js URL
17
- query parameters.
16
+ build CDN URLs. Matches the FireImg API-key HTTP API, the FireImg CLI upload
17
+ flow, and @fireimg/js URL query parameters.
18
18
  email:
19
19
  - hello@firelit.studio
20
20
  executables: []
@@ -30,6 +30,7 @@ files:
30
30
  - lib/fireimg/error.rb
31
31
  - lib/fireimg/presign_result.rb
32
32
  - lib/fireimg/size.rb
33
+ - lib/fireimg/upload_result.rb
33
34
  - lib/fireimg/version.rb
34
35
  homepage: https://github.com/FirelitStudio/fireimg-ruby
35
36
  licenses: