-
-
Save ahoward/d25e2a39dbd2cd1c12dcb4510cea9531 to your computer and use it in GitHub Desktop.
Revisions
-
ahoward revised this gist
Jul 11, 2025 . 3 changed files with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes.File renamed without changes.File renamed without changes. -
ahoward renamed this gist
Jul 11, 2025 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
ahoward revised this gist
Jul 11, 2025 . 3 changed files with 142 additions and 0 deletions.There are no files selected for viewing
File renamed without changes.This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ # file: config/initializers/s3.rb require Rails.root.join('lib/s3.rb') S3.config = Rails.application.credentials.fetch(:aws) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,137 @@ # file: lib/s3.rb require 'aws-sdk-s3' require 'mime/types' module S3 def config @config ||= Hash.new end def config=(config) @config = config end def config_for(key) config.fetch(key.to_s) { config.fetch(key.to_sym) } end def region config_for(:region) end def access_key_id config_for(:access_key_id) end def secret_access_key config_for(:secret_access_key) end def bucket config_for(:bucket) end def client Aws::S3::Client.new( region:, access_key_id:, secret_access_key:, ) end def url_for(key) "https://#{bucket}.s3.amazonaws.com/#{key}" end def write(key, body, **kws) kws[:content_type] ||= MIME::Types.type_for(key).first.to_s kws[:content_disposition] ||= 'inline' args = { bucket:, key:, body:, **kws } obj = client.put_object(**args) url = url_for(key) { key:, url: }.update(args) end alias_method :put, :write def read(key, &block) obj = client.get_object(bucket:, key: key) if block while (chunk = obj.body.read(1024)) block.call(chunk) end else obj.body.string end end alias_method :get, :read def list(prefix = '', limit: nil, &block) args = { bucket:, prefix: } accum = [] n = 0 client.list_objects_v2(**args).each do |response| response.contents.each do |obj| key = obj.key block ? block.call(key) : accum.push(key) n += 1 break if limit && (n >= limit) end break if limit && (n >= limit) end block ? nil : accum end alias_method :ls, :list def delete(key, *keys) keys.unshift(key) keys.flatten! keys.compact! to_delete = keys.map { { key: } } to_delete.each_slice(1000) do |objects| args = { bucket:, delete: { objects: } } client.delete_objects(args) end keys end alias_method :rm, :delete def exist(key) begin client.head_object(bucket:, key: key) true rescue Aws::S3::Errors::NotFound => e false end end alias_method :exist?, :exist alias_method :exists?, :exist def fetch(key, &block) begin read(key) rescue Aws::S3::Errors::NoSuchKey => _error block.call.tap { |obj| write(key, obj) } end end extend self end -
ahoward created this gist
Jul 11, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,76 @@ # file: app/controllers/concerns/s3_errors.rb =begin TL;DR; class ApplicationController include S3Errors end =end module S3Errors extend ActiveSupport::Concern included do rescue_from StandardError, with: :upload_error_to_s3 end def S3Errors.rotate!(cutoff: nil) cutoff ||= 1.week.ago.beginning_of_day re = %r{errors/(\d{4})/(\d{2})/(\d{2})/} n = 0 S3.list('errors/') do |key| if((match = key.match(re))) yyyy, mm, dd = match[1].to_i, match[2].to_i, match[3].to_i date = Date.new(yyyy, mm, dd) if date < cutoff.to_date S3.delete(key) n += 1 end end end return n end AutoRotater = Thread.new do loop do begin sleep 1.minute S3Errors.rotate! rescue => error Rails.logger.error(error) end sleep 59.minutes end end private def upload_error_to_s3(error) uuid = SecureRandom.uuid_v7.to_s now = Time.now.utc path = "errors/#{now.strftime('%Y/%m/%d')}/#{uuid}.json" data = { class: error.class.name, message: error.message, backtrace: error.backtrace, created_at: now.iso8601(2) } json = data.to_json S3.write(path, json) raise error end end