@@ -56,16 +56,30 @@ def upload(file, prefix: 'support')
5656 # Generate a pre-signed GET URL for a stored object
5757 #
5858 # @param key [String] the S3 object key
59- # @param expires_in [Integer] expiry in seconds
59+ # @param expires_in [Integer] expiry in seconds (max 604800 for AWS S3 Signature V4)
6060 # @return [String] signed URL
6161 def signed_url ( key , expires_in : SIGNED_URL_EXPIRY )
62+ # AWS S3 Signature V4 caps at 7 days; clamp to be safe
63+ capped = [ expires_in , 604_800 ] . min
6264 signer = Aws ::S3 ::Presigner . new ( client : @client )
63- signer . presigned_url ( :get_object , bucket : @bucket , key : key , expires_in : expires_in )
65+ signer . presigned_url ( :get_object , bucket : @bucket , key : key , expires_in : capped )
6466 rescue StandardError => e
6567 Rails . logger . error ( "[S3UploadService] Failed to generate signed URL for #{ key } : #{ e . message } " )
6668 nil
6769 end
6870
71+ # Build a permanent public URL for the object (requires the bucket to allow public access).
72+ # Supabase format: {project_base}/storage/v1/object/public/{bucket}/{key}
73+ #
74+ # @param key [String] the S3 object key
75+ # @return [String] public URL
76+ def public_url ( key )
77+ # Strip the S3 path suffix to get the project base URL
78+ # SUPABASE_S3_ENDPOINT = https://xxx.storage.supabase.co/storage/v1/s3
79+ base = ENV . fetch ( 'SUPABASE_S3_ENDPOINT' ) . sub ( %r{/s3\z } , '' )
80+ "#{ base } /object/public/#{ @bucket } /#{ key } "
81+ end
82+
6983 private
7084
7185 def validate! ( file )
0 commit comments