Class: PresignedUploadService

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
app/services/presigned_upload_service.rb

Constant Summary collapse

BUCKET_NAME =

Wasabi S3 configuration (reusing existing Dragonfly config)

'heatwave-assets-usc1'
REGION =
'us-central-1'
ENDPOINT =
'https://s3.us-central-1.wasabisys.com'

Instance Method Summary collapse

Constructor Details

#initializePresignedUploadService

Returns a new instance of PresignedUploadService.



13
14
15
16
17
18
19
20
21
22
# File 'app/services/presigned_upload_service.rb', line 13

def initialize
  @s3_client = Aws::S3::Client.new(
    access_key_id: S3_DATASTORE_ACCESS_KEY,
    secret_access_key: S3_DATASTORE_SECRET_KEY,
    region: REGION,
    endpoint: ENDPOINT,
    force_path_style: true
  )
  @s3_presigner = Aws::S3::Presigner.new(client: @s3_client)
end

Instance Method Details

#create_upload_from_s3_key(s3_key:, file_name:, content_type:, file_size:, category: 'photo', resource_type: nil, resource_id: nil) ⇒ Object

Create Upload record after successful direct upload



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'app/services/presigned_upload_service.rb', line 49

def create_upload_from_s3_key(s3_key:, file_name:, content_type:, file_size:, category: 'photo', resource_type: nil, resource_id: nil)
  # Extract the Dragonfly UID from the full S3 key
  # S3 key format: secure_assets/development/YYYY/MM/DD/HH/MM/SS/UUID/filename.ext
  # Dragonfly UID format: YYYY/MM/DD/HH/MM/SS/UUID/filename.ext (relative to root_path)
  # Remove secure_assets/env prefix (Dragonfly's root_path will add it back when generating URLs)
  dragonfly_uid = s3_key.gsub(%r{^secure_assets/#{Rails.env}/}, '')

  Rails.logger.info "S3 key: #{s3_key}"
  Rails.logger.info "Extracted Dragonfly UID: #{dragonfly_uid}"

  # Create Upload record
  upload = Upload.new(
    category: category,
    resource_type: resource_type,
    resource_id: resource_id,
    attachment_name: file_name,
    attachment_mime_type: content_type,
    attachment_size: file_size
  )

  # Set expiration date
  upload.expiration_date = 7.days.from_now

  # Create Upload record by bypassing Dragonfly validation
  # Since the file is already in S3, we'll create the record directly
  begin
    # Create the record without validation to bypass attachment requirement
    upload.save(validate: false)

    # Manually set the UID and other fields
    upload.update_columns(
      attachment_uid: dragonfly_uid, # Use the relative path as the UID
      attachment_name: file_name,
      attachment_mime_type: content_type,
      attachment_size: file_size
    )

    Rails.logger.info "Upload record created for S3 key: #{s3_key}, Dragonfly UID: #{dragonfly_uid}, ID: #{upload.id}"
    # Optionally, trigger any post-upload processing
    process_upload(upload)
    upload
  rescue StandardError => e
    Rails.logger.error "Failed to create Upload record: #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}"
    ErrorReporting.error(e, message: "PresignedUploadService#create_upload_from_s3_key failed for key: #{s3_key}")
    nil
  end
end

#generate_presigned_url(file_name:, content_type:, file_size:, category: 'photo', resource_type: nil, resource_id: nil) ⇒ Object

Generate presigned URL for direct upload to Wasabi



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/services/presigned_upload_service.rb', line 25

def generate_presigned_url(file_name:, content_type:, file_size:, category: 'photo', resource_type: nil, resource_id: nil)
  # file_size is passed for consistency but not used in presigned URL generation
  # Could be used for content_length_range validation in the future
  # Generate a unique key for the file
  key = generate_file_key(file_name, category, resource_type, resource_id)

  # Generate presigned URL for PUT request
  presigned_url = @s3_presigner.presigned_url(
    :put_object,
    bucket: BUCKET_NAME,
    key: key,
    expires_in: 3600, # 1 hour
    content_type: content_type
  )

  {
    presigned_url: presigned_url,
    key: key,
    bucket: BUCKET_NAME,
    region: REGION
  }
end