2
2
require "shrine/storage/file_system"
3
3
require "shrine/storage/s3"
4
4
require 'faster_s3_url/shrine/storage'
5
+ require 'scihist_digicoll/shrine_storage/cloudfront_s3_storage'
5
6
6
7
7
8
module ScihistDigicoll
@@ -236,15 +237,37 @@ def self.persistent_redis_connection!
236
237
define_key :s3_bucket_derivatives
237
238
define_key :s3_bucket_derivatives_video
238
239
239
- # Note: the values for :s3_bucket_derivatives_host and :s3_bucket_derivatives_video_host
240
- # can be obtained by running `terraform output`.
241
- define_key :s3_bucket_derivatives_host # Cloudfront hostname for regular derivs bucket
242
- define_key :s3_bucket_derivatives_video_host # Ditto, for video derivs bucket
243
-
244
240
define_key :s3_bucket_uploads
245
241
define_key :s3_bucket_on_demand_derivatives
246
242
define_key :s3_bucket_dzi
247
243
244
+ # if we are using CloudFront in front of a bucket, we set Cloudfront Distro hostname
245
+ # in variables corresponding to bucket name env, with `_host` on the end. These
246
+ # are all assumed cloudfront if set.
247
+ #
248
+ # All of these cloudfront hostnames are somewhat opaque, and can be found
249
+ # from `terraform output` output from existing infrastructure, either staging
250
+ # or prod.
251
+
252
+ define_key :s3_bucket_originals_host
253
+ define_key :s3_bucket_originals_video_host # not currently used as we don't provide access to originals
254
+ define_key :s3_bucket_derivatives_host
255
+ define_key :s3_bucket_derivatives_video_host
256
+ define_key :s3_bucket_on_demand_derivatives_host
257
+ define_key :s3_bucket_dzi_host
258
+
259
+ # If we are using Cloudfront with restricted buckets, the key-pair id and RSA public
260
+ # key need to be set.
261
+ define_key :cloudfront_key_pair_id # from `terraform output`
262
+ # private key can be found in 1password as:
263
+ # `scihist-digicoll-production_private_key.pem` or
264
+ # `scihist-digicoll-staging_private_key.pem`
265
+ #
266
+ # Value needs to be exported from 1Password in `PKCS#8` format, will begin with
267
+ # '-----BEGIN PRIVATE KEY-----' [NOT 'OPENSSH PRIVATE KEY']
268
+ define_key :cloudfront_private_key
269
+
270
+
248
271
define_key :ingest_bucket , default : -> {
249
272
if !Rails . env . production?
250
273
# This bucket isn't actually mounted on workstations, but you can
@@ -361,13 +384,18 @@ def self.solr_collection_name
361
384
# @param bucket_key [String] required. in `production` mode this is the bucket name, otherwise
362
385
# it becomes part of the prefix location.
363
386
#
387
+ # @param public [Boolean] (default false), if FALSE the storage requires signed URLs, and
388
+ # we will set up shrine storage to generate them -- either using
389
+ # AWS access key (direct), or Cloudfront public key (if host is set for
390
+ # Cloudfront distro in front)
391
+ #
364
392
# @param prefix [String] prefix passed to shrine storage, a "directory" within the
365
393
# storage. for dev_s3 and dev_file modes combined with
366
394
# other pre-prefix.
367
395
#
368
- # @param host [String] used only for `production` S3 mode, passed to Shrine storage as
369
- # 'host' param, used for cloudfront CDN and/or other CNAME, alternate
370
- # host used to access S3 bucket.
396
+ # @param host [String] used only for `production` S3 mode, if set we assume a CloudFront CDN distro
397
+ # on top of bucket, and set up url-generation accordingly, including with
398
+ # Cloudfront-style signing (using env for cloudfront public key which will be required)
371
399
#
372
400
# @param s3_storage_options [Hash] passed directly to Shrine storage for S3 modes, additional
373
401
# arbitrary options. Can override other defaults or params.
@@ -377,7 +405,7 @@ def self.solr_collection_name
377
405
# file system). Normally left unset, it will default to
378
406
# env key :storage_mode, which is what you want it to do.
379
407
#
380
- def self . appropriate_shrine_storage ( bucket_key :, mode : lookup! ( :storage_mode ) , prefix : nil ,
408
+ def self . appropriate_shrine_storage ( bucket_key :, public : false , mode : lookup! ( :storage_mode ) , prefix : nil ,
381
409
host : nil , s3_storage_options : { } )
382
410
unless %I{ s3_bucket_uploads s3_bucket_originals s3_bucket_originals_video s3_bucket_derivatives
383
411
s3_bucket_derivatives_video
@@ -400,14 +428,29 @@ def self.appropriate_shrine_storage(bucket_key:, mode: lookup!(:storage_mode), p
400
428
region : lookup ( :aws_region )
401
429
} . merge ( s3_storage_options ) )
402
430
elsif mode == "production"
403
- FasterS3Url ::Shrine ::Storage . new ( **{
404
- bucket : lookup! ( bucket_key ) ,
405
- host : host ,
406
- prefix : prefix ,
407
- access_key_id : lookup! ( :aws_access_key_id ) ,
408
- secret_access_key : lookup! ( :aws_secret_access_key ) ,
409
- region : lookup! ( :aws_region )
410
- } . merge ( s3_storage_options ) )
431
+ if host . present?
432
+ # Assumed cloudfront if we have a host!
433
+ ScihistDigicoll ::ShrineStorage ::CloudfrontS3Storage . new ( **{
434
+ bucket : lookup! ( bucket_key ) ,
435
+ host : host ,
436
+ public : public ,
437
+ prefix : prefix ,
438
+ access_key_id : lookup! ( :aws_access_key_id ) ,
439
+ secret_access_key : lookup! ( :aws_secret_access_key ) ,
440
+ region : lookup! ( :aws_region ) ,
441
+ cloudfront_key_pair_id : lookup ( :cloudfront_key_pair_id ) ,
442
+ cloudfront_private_key : lookup ( :cloudfront_private_key )
443
+ } . merge ( s3_storage_options ) )
444
+ else
445
+ FasterS3Url ::Shrine ::Storage . new ( **{
446
+ bucket : lookup! ( bucket_key ) ,
447
+ public : public ,
448
+ prefix : prefix ,
449
+ access_key_id : lookup! ( :aws_access_key_id ) ,
450
+ secret_access_key : lookup! ( :aws_secret_access_key ) ,
451
+ region : lookup! ( :aws_region )
452
+ } . merge ( s3_storage_options ) )
453
+ end
411
454
else
412
455
raise TypeError . new ( "unrecognized storage mode: #{ mode } " )
413
456
end
@@ -423,22 +466,22 @@ def self.shrine_cache_storage
423
466
424
467
def self . shrine_store_storage
425
468
@shrine_store_storage ||=
426
- appropriate_shrine_storage ( bucket_key : :s3_bucket_originals )
469
+ appropriate_shrine_storage ( bucket_key : :s3_bucket_originals , public : false , host : lookup ( :s3_bucket_originals_host ) )
427
470
end
428
471
429
472
# we store video originals in separate location
430
473
def self . shrine_store_video_storage
431
474
@shrine_video_store_storage ||=
432
- appropriate_shrine_storage ( bucket_key : :s3_bucket_originals_video )
475
+ appropriate_shrine_storage ( bucket_key : :s3_bucket_originals_video , public : false , host : lookup ( :s3_bucket_originals_video_host ) )
433
476
end
434
477
435
478
# Note we set shrine S3 storage to public, to upload with public ACLs
436
479
def self . shrine_derivatives_storage
437
480
@shrine_derivatives_storage ||=
438
481
appropriate_shrine_storage ( bucket_key : :s3_bucket_derivatives ,
439
482
host : lookup ( :s3_bucket_derivatives_host ) ,
483
+ public : true ,
440
484
s3_storage_options : {
441
- public : true ,
442
485
upload_options : {
443
486
# derivatives are public and at unique random URLs, so
444
487
# can be cached far-future
@@ -451,8 +494,8 @@ def self.shrine_video_derivatives_storage
451
494
@shrine_derivatives_video_storage ||=
452
495
appropriate_shrine_storage ( bucket_key : :s3_bucket_derivatives_video ,
453
496
host : lookup ( :s3_bucket_derivatives_video_host ) ,
497
+ public : true ,
454
498
s3_storage_options : {
455
- public : true ,
456
499
upload_options : {
457
500
# derivatives are public and at unique random URLs, so
458
501
# can be cached far-future
@@ -467,16 +510,25 @@ def self.shrine_video_derivatives_storage
467
510
def self . shrine_restricted_derivatives_storage
468
511
@shrine_restricted_derivatives_storage ||=
469
512
appropriate_shrine_storage ( bucket_key : :s3_bucket_originals ,
513
+ host : lookup ( :s3_bucket_originals_host ) ,
514
+ public : false ,
470
515
prefix : "restricted_derivatives" )
471
516
end
472
517
473
518
# Note we set shrine S3 storage to public, to upload with public ACLs
474
519
def self . shrine_on_demand_derivatives_storage
475
520
@shrine_on_demand_derivatives_storage ||=
476
521
appropriate_shrine_storage ( bucket_key : :s3_bucket_on_demand_derivatives ,
522
+ host : lookup ( :s3_bucket_on_demand_derivatives_host ) ,
523
+ public : true ,
477
524
s3_storage_options : {
478
- public : true
479
- } )
525
+ upload_options : {
526
+ # these have fingerprints in their URLs, so they are
527
+ # cacheable forever. only started setting this in Jul 2024
528
+ cache_control : "max-age=31536000, public"
529
+ }
530
+ }
531
+ )
480
532
end
481
533
482
534
def self . shrine_combined_audio_derivatives_storage
@@ -485,16 +537,22 @@ def self.shrine_combined_audio_derivatives_storage
485
537
appropriate_shrine_storage ( bucket_key : :s3_bucket_derivatives ,
486
538
host : lookup ( :s3_bucket_derivatives_host ) ,
487
539
prefix : "combined_audio_derivatives" ,
540
+ public : true ,
488
541
s3_storage_options : {
489
- public : true
542
+ upload_options : {
543
+ # these have fingerprints in their URLs, so they are
544
+ # cacheable forever. only started setting this in Jul 2024
545
+ cache_control : "max-age=31536000, public"
546
+ }
490
547
} )
491
548
end
492
549
493
550
def self . shrine_dzi_storage
494
551
@shrine_dzi_storage ||=
495
552
appropriate_shrine_storage ( bucket_key : :s3_bucket_dzi ,
553
+ host : lookup ( :s3_bucket_dzi_host ) ,
554
+ public : true ,
496
555
s3_storage_options : {
497
- public : true ,
498
556
upload_options : {
499
557
# our DZI's are all public right now, and at unique-to-content
500
558
# URLs, cache forever.
0 commit comments