studio-engine 0.77.2 → 0.77.4

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: 6e39db0e8ae3fa259b3e0869c0e33099d1fd840192e9da33ae7d43bc37ce07d7
4
- data.tar.gz: 171d736d4f5a6669446e718130d029499d0170b4102a2e76d5990193feac5901
3
+ metadata.gz: 9497ed872cea58d146d2859f8220ad71e3979b2b1603fb263622081c29e3144e
4
+ data.tar.gz: 7de61f4d57e71e09bf6d5fa32acd0309625e547d52b0d4bbfc45c94f7bd60328
5
5
  SHA512:
6
- metadata.gz: 1a3bd81ed22cc40b1d98864b469e60d97b9e8a4e2178eb518bd409cafbfe2b37c8171dc3daaf064bf3a0a5f432ac8fd758c23288c607ca9d1631bec81f8f5e10
7
- data.tar.gz: 4023eff14f61f6c8bf3be70fc0fb69d5990ff62fe9cb968f652b9cd11b450bbe85b6686d35b46eba2e76b2fddb7d17de895751a2ecb67a4483238352a6106ca9
6
+ metadata.gz: d443cc7d3b914255adef0e681d926c4bb88c92dd1cbe4814195a5a6c55132e95e58550ddb14d6277b717bf6a7778823620c87ab7aa3bba0499373b0db33063b3
7
+ data.tar.gz: e4e6df7c16f17a790b9058030d2f3b1b519ca882ac13069ea69f641b76974ca9fabd47a0de8ec89d9e8333dfbd428a2fdbdf46334c5d4e046ba4ba737ee2c2fe
data/CHANGELOG.md CHANGED
@@ -4,6 +4,27 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.77.4 — 2026-09-28
8
+
9
+ ### Fixed
10
+
11
+ - **An email banner stored on R2 with no public URL no longer breaks sign-in.**
12
+ With `Studio::S3` on an S3-compatible endpoint (Cloudflare R2) and no
13
+ `s3_public_url`, a banner upload on `/admin/emails` used to succeed, after which
14
+ `ImageCache#url` raised `NotConfigured` on every read: the magic-link sign-in
15
+ email, the newsletter and profile emails, and the orphan page all failed.
16
+ - `Studio::EmailCatalog.uploads_available?` is now false in that state, and the
17
+ new `uploads_blocked_reason` (`:no_storage` or `:no_public_url`) lets the page
18
+ name the setting that fixes it: `s3_public_url`, not `s3_bucket_prefix`.
19
+ - `EmailCatalog.url` degrades to `nil` when a stored image cannot be served, so
20
+ `resolved_url` and every host's `url(...) || its_own_asset` fall back to the
21
+ default banner. Other errors still raise, and `ImageCache#url` stays loud
22
+ everywhere else.
23
+ - The orphan page previews through the same safe read.
24
+ Apps on AWS, or on R2 with `s3_public_url` set, behave exactly as before.
25
+
26
+ ## 0.77.3 — 2026-09-28
27
+
7
28
  ## 0.77.2 — 2026-09-28
8
29
 
9
30
  ### Fixed
@@ -23,6 +23,7 @@ module Studio
23
23
  def index
24
24
  @entries = Studio::EmailCatalog.entries
25
25
  @uploads_available = Studio::EmailCatalog.uploads_available?
26
+ @uploads_blocked_reason = Studio::EmailCatalog.uploads_blocked_reason
26
27
 
27
28
  # The list renders every banner and subject AS THEY WOULD ARRIVE, which has
28
29
  # no answer until someone is receiving them.
@@ -44,6 +45,7 @@ module Studio
44
45
  @subject = Studio::EmailCatalog.preview_subject(@key)
45
46
  @preview_error = Studio::EmailCatalog.preview_error(@key)
46
47
  @uploads_available = Studio::EmailCatalog.uploads_available?
48
+ @uploads_blocked_reason = Studio::EmailCatalog.uploads_blocked_reason
47
49
 
48
50
  # WHO this is previewed as. The banner greets by name, so "what does this
49
51
  # email look like" has no answer until someone is receiving it — and the
@@ -241,8 +243,12 @@ module Studio
241
243
  def require_uploads
242
244
  return if Studio::EmailCatalog.uploads_available?
243
245
 
244
- redirect_to admin_emails_path, status: :see_other,
245
- alert: "This app has no object storage configured, so email images can't be changed here yet."
246
+ alert = if Studio::EmailCatalog.uploads_blocked_reason == :no_public_url
247
+ "This app's storage has no public URL yet, so email images can't be changed here."
248
+ else
249
+ "This app has no object storage configured, so email images can't be changed here yet."
250
+ end
251
+ redirect_to admin_emails_path, status: :see_other, alert: alert
246
252
  end
247
253
 
248
254
  # Shown INSIDE the preview iframe when the builder is missing or raised.
@@ -712,6 +712,13 @@ module Studio
712
712
  # caller keeps the behavior it was written against until its app adopts.
713
713
  def url(key)
714
714
  record(key)&.url
715
+ rescue Studio::S3::NotConfigured => e
716
+ # A stored override this app can no longer serve publicly (Studio::S3 on
717
+ # an S3-compatible endpoint with no s3_public_url). Every caller already
718
+ # treats nil as "use the default", so degrade to that instead of raising
719
+ # out of a mailer: this method feeds the magic-link sign-in email.
720
+ warn_unservable(key, e)
721
+ nil
715
722
  end
716
723
 
717
724
  # What ACTUALLY SHIPS on this email — the two-layer resolution. Absolute, so
@@ -845,13 +852,31 @@ module Studio
845
852
  host ? "#{host}#{path}" : path
846
853
  end
847
854
 
855
+ def warn_unservable(key, error)
856
+ message = "[email_catalog] #{key}: stored image not publicly servable, using default (#{error.message})"
857
+ defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger ? Rails.logger.warn(message) : warn(message)
858
+ end
859
+
848
860
  # --- Upload ------------------------------------------------------------
849
861
 
850
862
  # Whether THIS app can accept an upload. False when the host never set
851
863
  # Studio.s3_bucket_prefix — /admin/emails then shows inherited defaults
852
864
  # read-only rather than 500ing on the first upload.
853
865
  def uploads_available?
854
- Studio::S3.configured? && table_ready?
866
+ uploads_blocked_reason.nil?
867
+ end
868
+
869
+ # Why uploads are off, or nil when they are on:
870
+ # :no_storage the host never set Studio.s3_bucket_prefix (or the table
871
+ # is not migrated yet)
872
+ # :no_public_url storage is an S3-compatible endpoint (Cloudflare R2) with
873
+ # no Studio.s3_public_url, so an uploaded image could be
874
+ # stored but never served to an inbox
875
+ def uploads_blocked_reason
876
+ return :no_storage unless Studio::S3.configured? && table_ready?
877
+ return :no_public_url unless Studio::S3.public_url?
878
+
879
+ nil
855
880
  end
856
881
 
857
882
  # Upload bytes to this app's bucket + upsert its ImageCache row (replacing
@@ -76,14 +76,25 @@
76
76
  <% unless @uploads_available %>
77
77
  <div class="mb-6 rounded-xl border border-subtle bg-inset px-4 py-3">
78
78
  <p class="text-sm font-semibold text-heading">Read-only on this app</p>
79
- <p class="text-sm text-body mt-1">
80
- <%= Studio.app_name %> has no object storage configured, so the images below
81
- can be viewed but not replaced yet. They are the shared defaults that ship
82
- with the engine, and they are what this app is sending right now. Set
83
- <code class="font-mono text-2xs bg-surface px-1.5 py-0.5 rounded">config.s3_bucket_prefix</code>
84
- in <code class="font-mono text-2xs bg-surface px-1.5 py-0.5 rounded">config/initializers/studio.rb</code>
85
- to turn on uploads.
86
- </p>
79
+ <% if @uploads_blocked_reason == :no_public_url %>
80
+ <p class="text-sm text-body mt-1">
81
+ <%= Studio.app_name %> stores files where they cannot be served to an inbox yet,
82
+ so the images below can be viewed but not replaced. They are what this app is
83
+ sending right now. Set
84
+ <code class="font-mono text-2xs bg-surface px-1.5 py-0.5 rounded">config.s3_public_url</code>
85
+ in <code class="font-mono text-2xs bg-surface px-1.5 py-0.5 rounded">config/initializers/studio.rb</code>
86
+ to turn on uploads.
87
+ </p>
88
+ <% else %>
89
+ <p class="text-sm text-body mt-1">
90
+ <%= Studio.app_name %> has no object storage configured, so the images below
91
+ can be viewed but not replaced yet. They are the shared defaults that ship
92
+ with the engine, and they are what this app is sending right now. Set
93
+ <code class="font-mono text-2xs bg-surface px-1.5 py-0.5 rounded">config.s3_bucket_prefix</code>
94
+ in <code class="font-mono text-2xs bg-surface px-1.5 py-0.5 rounded">config/initializers/studio.rb</code>
95
+ to turn on uploads.
96
+ </p>
97
+ <% end %>
87
98
  </div>
88
99
  <% end %>
89
100
 
@@ -28,9 +28,13 @@
28
28
  </p>
29
29
 
30
30
  <% if (record = Studio::EmailCatalog.record(@key)) %>
31
- <div class="rounded-lg overflow-hidden border border-subtle w-56 mt-4">
32
- <%= image_tag record.url, class: "w-full h-auto", alt: "Stored artwork for #{@key}" %>
33
- </div>
31
+ <%# EmailCatalog.url, not record.url: it degrades to nil when the stored image
32
+ cannot be served publicly (R2 with no s3_public_url) instead of 500ing. %>
33
+ <% if (preview = Studio::EmailCatalog.url(@key)) %>
34
+ <div class="rounded-lg overflow-hidden border border-subtle w-56 mt-4">
35
+ <%= image_tag preview, class: "w-full h-auto", alt: "Stored artwork for #{@key}" %>
36
+ </div>
37
+ <% end %>
34
38
  <p class="font-mono text-2xs text-muted mt-2 break-all"><%= record.s3_key %></p>
35
39
  <% end %>
36
40
 
@@ -1,3 +1,3 @@
1
1
  module Studio
2
- VERSION = "0.77.2"
2
+ VERSION = "0.77.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: studio-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.77.2
4
+ version: 0.77.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McRitchie