Class: PresignedUploadService

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

Overview

Service object: presigned upload service.

Constant Summary collapse

BUCKET_NAME =

Wasabi S3 configuration (reusing existing Dragonfly config)

'heatwave-assets-usc1'
REGION =

Region.

'us-central-1'
ENDPOINT =

Endpoint.

'https://s3.us-central-1.wasabisys.com'

Instance Method Summary collapse

Constructor Details

#initializePresignedUploadService

Returns a new instance of PresignedUploadService.



16
17
18
19
20
21
22
23
24
25
# File 'app/services/presigned_upload_service.rb', line 16

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



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
96
97
98
99
# File 'app/services/presigned_upload_service.rb', line 52

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
  )

  # RMA uploads must never expire — they are permanently attached to the RMA record.
  # Only set expiration on orphaned uploads (no resource_type) as a safety-net cleanup.
  upload.expiration_date = 7.days.from_now unless resource_type == 'Rma'

  # 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



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

def generate_presigned_url(file_name:, content_type:, file_size:, category: 'photo', resource_type: nil, resource_id: nil) # rubocop:disable Lint/UnusedMethodArgument
  # 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