diff --git a/Gemfile b/Gemfile index 7da8a4c9..28753a42 100644 --- a/Gemfile +++ b/Gemfile @@ -14,8 +14,8 @@ gem "administrate", "0.20.1" # Reduces boot times through caching; required in config/boot.rb gem "bootsnap", "1.18.3", require: false -# Rack middleware for blocking & throttling -gem 'rack-attack' +# Rack middleware for blocking & throttling +gem "rack-attack" # Use Sass to process CSS # gem "sassc-rails" @@ -73,6 +73,8 @@ gem "rack-cors", "2.0.2" gem "rails", "7.1.3.4" # Use Redis adapter to run Action Cable in production gem "redis", "5.2.0" +# User resend.com to send emails +gem "resend" # Sentry SDK for Rails gem "sentry-rails", "5.17.3" # Sentry SDK for Ruby @@ -109,6 +111,8 @@ group :development, :test do end group :development do + gem "brakeman", "6.1.2" + gem "bullet" # To ensure code consistency [https://docs.rubocop.org] gem "rubocop", "1.56.2" gem "rubocop-factory_bot", "!= 2.26.0", require: false @@ -118,8 +122,6 @@ group :development do gem "rubocop-rspec_rails", "!= 2.29.0", require: false # Use console on exceptions pages [https://github.com/rails/web-console] gem "web-console", "4.2.1" - gem "bullet" - gem "brakeman", "6.1.2" # Preview mail in the browser instead of sending. gem "letter_opener", "1.10.0" diff --git a/Gemfile.lock b/Gemfile.lock index 936d7982..b734d288 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -225,6 +225,10 @@ GEM activesupport (>= 6.1) hashery (2.1.2) hashie (5.0.0) + httparty (0.23.2) + csv + mini_mime (>= 1.0.0) + multi_xml (>= 0.5.2) i18n (1.14.5) concurrent-ruby (~> 1.0) importmap-rails (2.0.1) @@ -454,6 +458,8 @@ GEM io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) + resend (0.26.0) + httparty (>= 0.21.0) responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) @@ -645,6 +651,7 @@ DEPENDENCIES rails (= 7.1.3.4) redis (= 5.2.0) reek (= 6.3.0) + resend rspec-rails (= 6.0.3) rspec-sidekiq (= 4.0.2) rubocop (= 1.56.2) diff --git a/app/controllers/api/v1/event_procedures_controller.rb b/app/controllers/api/v1/event_procedures_controller.rb index a88806ef..644cbc8c 100644 --- a/app/controllers/api/v1/event_procedures_controller.rb +++ b/app/controllers/api/v1/event_procedures_controller.rb @@ -6,28 +6,28 @@ class EventProceduresController < ApiController after_action :verify_authorized, except: :index after_action :verify_policy_scoped, only: :index - def index - authorized_scope = policy_scope(EventProcedure) - - listed_event_procedures = EventProcedures::List.result( - scope: authorized_scope, - params: event_procedure_permitted_query_params - ) - - event_procedures = listed_event_procedures.event_procedures - event_procedures_unpaginated = listed_event_procedures.event_procedures_unpaginated - - total_amount_cents = EventProcedures::TotalAmountCents.call( - event_procedures: event_procedures_unpaginated - ) - - render json: { - total: total_amount_cents.total, - total_paid: total_amount_cents.paid, - total_unpaid: total_amount_cents.unpaid, - event_procedures: serialized_event_procedures(event_procedures) - }, status: :ok - end + def index + authorized_scope = policy_scope(EventProcedure) + + listed_event_procedures = EventProcedures::List.result( + scope: authorized_scope, + params: event_procedure_permitted_query_params + ) + + event_procedures = listed_event_procedures.event_procedures + event_procedures_unpaginated = listed_event_procedures.event_procedures_unpaginated + + total_amount_cents = EventProcedures::TotalAmountCents.call( + event_procedures: event_procedures_unpaginated + ) + + render json: { + total: total_amount_cents.total, + total_paid: total_amount_cents.paid, + total_unpaid: total_amount_cents.unpaid, + event_procedures: serialized_event_procedures(event_procedures) + }, status: :ok + end def create authorize(EventProcedure) diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 5cc63a0c..c97d4595 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true class ApplicationMailer < ActionMailer::Base - default from: "from@example.com" layout "mailer" end diff --git a/config/environments/development.rb b/config/environments/development.rb index e6aa2168..132f388c 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -66,10 +66,10 @@ # The Bullet gem helps detect N+1 queries and other inefficiencies in ActiveRecord queries. config.after_initialize do - Bullet.enable = true - Bullet.alert = true + Bullet.enable = true + Bullet.alert = true Bullet.bullet_logger = true - Bullet.console = true + Bullet.console = true end # Raises error for missing translations. @@ -86,6 +86,8 @@ config.action_mailer.default_url_options = { host: "localhost", port: 3000 } + # Use letter_opener to local development or resend to test real emails config.action_mailer.delivery_method = :letter_opener + # config.action_mailer.delivery_method = :resend config.action_mailer.perform_deliveries = true end diff --git a/config/environments/production.rb b/config/environments/production.rb index 22eaf779..d6001e52 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -99,16 +99,5 @@ config.action_mailer.default_url_options = { host: "api.meusprocedimentos.com.br", protocol: "https" } - config.action_mailer.delivery_method = :smtp - config.action_mailer.smtp_settings = { - address: "smtp.sendgrid.net", - port: 587, - domain: "meusprocedimentos.com.br", - user_name: Rails.application.credentials.dig(:smtp, :username), - password: Rails.application.credentials.dig(:smtp, :password), - authentication: "plain", - enable_starttls: true, - open_timeout: 5, - read_timeout: 5 - } + config.action_mailer.delivery_method = :resend end diff --git a/config/initializers/mailer.rb b/config/initializers/mailer.rb new file mode 100644 index 00000000..c8b64044 --- /dev/null +++ b/config/initializers/mailer.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +Resend.api_key = ENV.fetch("RESEND_API_KEY", nil) diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index 0aeb6e1f..2498c273 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Rack::Attack - ### Configure Cache ### # If you don't want to use Rails.cache (Rack::Attack's default), then @@ -11,7 +10,7 @@ class Rack::Attack # safelisting). It must implement .increment and .write like # ActiveSupport::Cache::Store - Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new + Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new ### Throttle Spammy Clients ### @@ -26,7 +25,7 @@ class Rack::Attack # Throttle all requests by IP (60rpm) # # Key: "rack::attack:#{Time.now.to_i/:period}:req/ip:#{req.ip}" - throttle('req/ip', limit: 300, period: 5.minutes) do |req| + throttle("req/ip", limit: 300, period: 5.minutes) do |req| req.ip # unless req.path.start_with?('/assets') end @@ -42,10 +41,8 @@ class Rack::Attack # Throttle POST requests to /login by IP address # # Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}" - throttle('logins/ip', limit: 5, period: 20.seconds) do |req| - if req.path == '/login' && req.post? - req.ip - end + throttle("logins/ip", limit: 5, period: 20.seconds) do |req| + req.ip if req.path == "/login" && req.post? end # Throttle POST requests to /login by email param @@ -56,11 +53,11 @@ class Rack::Attack # throttle logins for another user and force their login requests to be # denied, but that's not very common and shouldn't happen to you. (Knock # on wood!) - throttle('logins/email', limit: 5, period: 20.seconds) do |req| - if req.path == '/login' && req.post? + throttle("logins/email", limit: 5, period: 20.seconds) do |req| + if req.path == "/login" && req.post? # Normalize the email, using the same logic as your authentication process, to # protect against rate limit bypasses. Return the normalized email if present, nil otherwise. - req.params['email'].to_s.downcase.gsub(/\s+/, "").presence + req.params["email"].to_s.downcase.gsub(/\s+/, "").presence end end @@ -78,4 +75,3 @@ class Rack::Attack # ['']] # body # end end - diff --git a/spec/operations/event_procedures/list_spec.rb b/spec/operations/event_procedures/list_spec.rb index 4baaa45d..2050528a 100644 --- a/spec/operations/event_procedures/list_spec.rb +++ b/spec/operations/event_procedures/list_spec.rb @@ -3,7 +3,6 @@ require "rails_helper" RSpec.describe EventProcedures::List, type: :operation do - describe ".result" do it "is successful" do result = described_class.result(scope: EventProcedure.all, params: {}) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 524c7586..f1573a41 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -75,7 +75,7 @@ Rails.root.glob("spec/support/**/*.rb").each { |f| require f } # Clears Rack:Attack cache between specs - config.before(:each) do + config.before do Rack::Attack.cache.store.clear if Rack::Attack.cache.store.respond_to?(:clear) end end diff --git a/spec/support/query_helper.rb b/spec/support/query_helper.rb index 230b9e9b..7697ccd4 100644 --- a/spec/support/query_helper.rb +++ b/spec/support/query_helper.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true module QueryHelper - def count_queries(&block) + def count_queries(&) queries = [] - callback = ->(_name, _start, _finish, _id, payload) do - queries << payload[:sql] unless payload[:name] =~ /SCHEMA|TRANSACTION/ + callback = lambda do |_name, _start, _finish, _id, payload| + queries << payload[:sql] unless /SCHEMA|TRANSACTION/.match?(payload[:name]) end - ActiveSupport::Notifications.subscribed(callback, "sql.active_record", &block) + ActiveSupport::Notifications.subscribed(callback, "sql.active_record", &) queries.count end end