hephaestus 0.7.2 → 0.7.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/app/controllers/concerns/hephaestus/responses.rb +107 -0
  4. data/app/controllers/concerns/hephaestus/validates_from_yetto.rb +51 -0
  5. data/app/controllers/hephaestus/application_controller.rb +42 -0
  6. data/app/controllers/hephaestus/root_controller.rb +10 -0
  7. data/app/controllers/hephaestus/settings_controller.rb +20 -0
  8. data/app/controllers/hephaestus/staff_controller.rb +20 -0
  9. data/app/jobs/hephaestus/application_job.rb +12 -0
  10. data/app/jobs/hephaestus/update_yetto_job.rb +39 -0
  11. data/app/models/hephaestus/application_record.rb +8 -0
  12. data/app/serializers/hephaestus/error_serializer.rb +18 -0
  13. data/app/serializers/hephaestus/headers.rb +27 -0
  14. data/app/services/hephaestus/yetto_service.rb +99 -0
  15. data/app/views/layouts/staff.html.erb +7 -0
  16. data/app/views/staff/index.html.erb +1 -0
  17. data/config/database.yml +49 -0
  18. data/config/environments/development.rb +89 -0
  19. data/config/environments/production.rb +96 -0
  20. data/config/environments/staging.rb +94 -0
  21. data/config/environments/test.rb +73 -0
  22. data/config/initializers/application.rb +14 -0
  23. data/config/initializers/cors.rb +19 -0
  24. data/config/initializers/environment.rb +94 -0
  25. data/config/initializers/filter_parameter_logging.rb +23 -0
  26. data/config/initializers/inflections.rb +21 -0
  27. data/config/initializers/litestream.rb +36 -0
  28. data/config/initializers/lograge.rb +27 -0
  29. data/config/initializers/opentelemetry.rb +41 -0
  30. data/config/initializers/sidekiq.rb +13 -0
  31. data/config/initializers/slack_webhook_logger.rb +19 -0
  32. data/config/litestream.yml +12 -0
  33. data/config/queue.yml +18 -0
  34. data/config/recurring.yml +10 -0
  35. data/config/routes.rb +17 -0
  36. data/db/queue_schema.rb +129 -0
  37. data/lib/hephaestus/version.rb +1 -1
  38. metadata +36 -19
  39. data/templates/.dockerignore +0 -39
  40. data/templates/.env.sample +0 -6
  41. data/templates/.github/dependabot.yml +0 -27
  42. data/templates/.github/workflows/automerge.yml +0 -17
  43. data/templates/.github/workflows/deploy.yml +0 -30
  44. data/templates/.github/workflows/licenses.yml +0 -23
  45. data/templates/.github/workflows/lint.yml +0 -32
  46. data/templates/.github/workflows/security.yml +0 -15
  47. data/templates/.github/workflows/sorbet.yml +0 -19
  48. data/templates/.github/workflows/test.yml +0 -21
  49. data/templates/.licensed.yml +0 -43
  50. data/templates/.rubocop.yml +0 -5
  51. data/templates/.ruby-version +0 -1
  52. data/templates/.vscode/extensions.json +0 -9
  53. data/templates/.vscode/launch.json +0 -13
  54. data/templates/.vscode/settings.json +0 -52
  55. data/templates/test/integration/.keep +0 -0
  56. data/templates/test/mailers/.keep +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37b0e62f3dc4c629a495222466581d493eb8b918bf0b359f3c71512921405c9f
4
- data.tar.gz: 0f73a528625033524258f6d679cffbb209c4190baf465268c924efd7c83bcd52
3
+ metadata.gz: d7c2f9d61793dbe5625865f819df27d6c9d63af6a8b62d24baad0179cb18292a
4
+ data.tar.gz: 2b3b19c2cf731b161b8d669e82613ef734b3724580ffaed458912e16ea2dee4e
5
5
  SHA512:
6
- metadata.gz: 1222c3ba07b47953f5a472e0bcdc32fdd48db56c34bed9aa9d9a9f5c7f6552a6b5604009845cfbd758ee2d776db6a3c8f63a46478e90c0338c99335f1dfa033f
7
- data.tar.gz: 6cb716b8f868316d5a186881f094133725302b164b06ddeddcc4f0fbe90a3534a2f58e97a0974a1f04cd22071c300bcf97dfecbe8c4f67d961a57e47a3364d32
6
+ metadata.gz: 0dab68747b8859e1f6d0b24bb2d5d3243f4bb0278602d132eaa4a822d18b7ffaf69b9f683edf1fe1af807bfced0e009010433095725c083582decf8b0a4116f8
7
+ data.tar.gz: aaf595de123fbb62eb0582e05d94f3f437c2e0eca9564c563ec6485ba65aede1f690c8d91b266be5235c5755770136b7c46a7646c0b49006131dff1978b78c13
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # [v0.7.2.1] - 16-11-2024
2
+ ## What's Changed
3
+ * include all files in the gem by @gjtorikian in https://github.com/yettoapp/hephaestus/pull/31
4
+
5
+
6
+ **Full Changelog**: https://github.com/yettoapp/hephaestus/compare/v0.7.2...v0.7.2.1
1
7
  # [v0.7.2] - 16-11-2024
2
8
  ## What's Changed
3
9
  * [skip test] Release v0.7.1 by @github-actions in https://github.com/yettoapp/hephaestus/pull/28
@@ -0,0 +1,107 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ module Responses
6
+ def no_content
7
+ head(:no_content)
8
+ end
9
+
10
+ def okay
11
+ render(
12
+ json: {
13
+ message: "OK",
14
+ }.to_json,
15
+ status: :ok,
16
+ )
17
+ end
18
+
19
+ def created
20
+ render(
21
+ json: {
22
+ message: "Created",
23
+ }.to_json,
24
+ status: :created,
25
+ )
26
+ end
27
+
28
+ def bad_request
29
+ render(
30
+ json: {
31
+ errors: [
32
+ {
33
+ message: "Bad Request",
34
+ },
35
+ ],
36
+ }.to_json,
37
+ status: :bad_request,
38
+ )
39
+ end
40
+
41
+ def forbidden
42
+ render(
43
+ json: {
44
+ errors: [
45
+ {
46
+ message: "Forbidden",
47
+ },
48
+ ],
49
+ }.to_json,
50
+ status: :forbidden,
51
+ )
52
+ end
53
+
54
+ def not_acceptable
55
+ render(
56
+ json: ::Hephaestus::ErrorSerializer.format("Not Acceptable").to_json,
57
+ status: :not_acceptable,
58
+ )
59
+ end
60
+
61
+ def not_found
62
+ render(
63
+ json: ::Hephaestus::ErrorSerializer.format("Not Found").to_json,
64
+ status: :not_found,
65
+ )
66
+ end
67
+
68
+ def service_unavailable(msg)
69
+ render(
70
+ json: {
71
+ errors: [
72
+ {
73
+ message: "Service Unavailable: #{msg}",
74
+ },
75
+ ],
76
+ }.to_json,
77
+ status: :service_unavailable,
78
+ )
79
+ end
80
+
81
+ def bad_gateway
82
+ render(
83
+ json: {
84
+ errors: [
85
+ {
86
+ message: "Bad Gateway",
87
+ },
88
+ ],
89
+ }.to_json,
90
+ status: :bad_gateway,
91
+ )
92
+ end
93
+
94
+ def internal_server_error
95
+ render(
96
+ json: {
97
+ errors: [
98
+ {
99
+ message: "Internal Server Error",
100
+ },
101
+ ],
102
+ }.to_json,
103
+ status: :internal_server_error,
104
+ )
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,51 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ module ValidatesFromYetto
6
+ SHA256_DIGEST = OpenSSL::Digest.new("sha256")
7
+
8
+ extend ActiveSupport::Concern
9
+
10
+ include Hephaestus::Responses
11
+
12
+ included do
13
+ before_action :from_yetto?
14
+ end
15
+
16
+ def from_yetto?
17
+ return bad_request if request.headers.blank?
18
+
19
+ yetto_signature = request.headers.fetch(Hephaestus::Headers::HEADER_SIGNATURE, "")
20
+
21
+ return bad_request unless yetto_signature.start_with?("sha256=")
22
+
23
+ hmac_header = yetto_signature.split("sha256=").last
24
+ body = request.env.fetch("RAW_POST_DATA", "")
25
+
26
+ calculated_hmac = OpenSSL::HMAC.hexdigest(SHA256_DIGEST, Hephaestus::YETTO_SIGNING_SECRET, body)
27
+
28
+ return true if ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header)
29
+
30
+ bad_request
31
+ end
32
+
33
+ def from_yetto_inline?
34
+ return bad_request if request.headers.blank?
35
+
36
+ yetto_signature = request.headers.fetch(Hephaestus::Headers::HEADER_SIGNATURE, "")
37
+
38
+ return bad_request unless yetto_signature.start_with?("sha256=")
39
+
40
+ hmac_header = yetto_signature.split("sha256=").last
41
+ body = params["encrypted_payload"]
42
+
43
+ @payload = T.let(ActiveSupport::MessageEncryptor.new(Hephaestus::YETTO_SIGNING_SECRET, url_safe: true, serializer: :json).decrypt_and_verify(body), T.nilable(String))
44
+ calculated_hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), Hephaestus::YETTO_SIGNING_SECRET, @payload)
45
+
46
+ return true if ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header)
47
+
48
+ bad_request
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,42 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "opentelemetry-semantic_conventions"
5
+
6
+ module Hephaestus
7
+ class ApplicationController < ActionController::Base
8
+ include ActionController::MimeResponds
9
+
10
+ include Hephaestus::Headers
11
+ include Hephaestus::Responses
12
+
13
+ rescue_from ActionController::UnknownFormat, with: :not_acceptable
14
+ protect_from_forgery with: :null_session
15
+
16
+ def ensure_json_request
17
+ return if request.format.json?
18
+
19
+ not_acceptable
20
+ end
21
+
22
+ # Yetto sends a unique state parameter as part of the OAuth installation process.
23
+ # This decodes that into something the plug can use.
24
+ def parse_state(state)
25
+ decoded_state = Base64.urlsafe_decode64(state)
26
+ state = JSON.parse(decoded_state)
27
+
28
+ {
29
+ version: state["version"],
30
+ salt: state["salt"],
31
+ nonce: state["nonce"] || "",
32
+ plug_installation_id: state["plug_installation_id"],
33
+ membership_id: state["membership_id"], # optional, used for conversation.viewed events
34
+ redirect_to: state["redirect_to"],
35
+ }
36
+ rescue ArgumentError # invalid base64
37
+ {}
38
+ rescue JSON::ParserError # invalid JSON
39
+ {}
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ class RootController < ApplicationController
6
+ def index
7
+ okay
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ class SettingsController < ApplicationController
6
+ before_action :ensure_json_request
7
+
8
+ def new
9
+ set_step
10
+ render("settings/new")
11
+ end
12
+
13
+ def edit
14
+ end
15
+
16
+ def set_step
17
+ @step = params.fetch(:step, 1).to_i || 1
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ class StaffController < ApplicationController
6
+ layout "staff"
7
+
8
+ class << self
9
+ def staff_request?(request)
10
+ return true if Rails.env.development?
11
+
12
+ true
13
+ end
14
+ end
15
+
16
+ def index
17
+ render404 unless StaffController.staff_request?(request)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ class ApplicationJob < ActiveJob::Base
6
+ # Automatically retry jobs that encountered a deadlock
7
+ # retry_on ActiveRecord::Deadlocked
8
+
9
+ # Most jobs are safe to ignore if the underlying records are no longer available
10
+ # discard_on ActiveJob::DeserializationError
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ # Send updated data to Yetto to store in the database
5
+ # This can be used to update installation data or message data
6
+ module Hephaestus
7
+ class UpdateYettoJob < ApplicationJob
8
+ queue_as :high_priority_update_yetto
9
+
10
+ def perform(params)
11
+ type = params.delete(:type)
12
+ params.deep_symbolize_keys!
13
+
14
+ inbox_id = params.fetch(:inbox, {}).fetch(:id, nil)
15
+ plug_installation_id = params.fetch(:plug_installation, {}).fetch(:id, nil)
16
+ conversation_id = params.fetch(:conversation, {}).fetch(:id, nil)
17
+ message_id = params.fetch(:message, {}).fetch(:id, nil)
18
+
19
+ response = case type
20
+ when "update_plug_installation"
21
+ YettoService.update_plug_installation(plug_installation_id, params[:payload])
22
+ when "create_conversation"
23
+ YettoService.create_conversation(plug_installation_id, inbox_id, params[:payload])
24
+ when "add_message_to_conversation"
25
+ YettoService.add_message_to_conversation(plug_installation_id, conversation_id, params[:payload])
26
+ when "create_message_reply"
27
+ YettoService.create_message_reply(plug_installation_id, message_id, params[:payload])
28
+ when "update_message"
29
+ YettoService.update_message(plug_installation_id, message_id, params[:payload])
30
+ end
31
+
32
+ response, type = Rails.configuration.enhance_update_yetto_job.call(params, type, response) if Rails.configuration.respond_to?(:enhance_update_yetto_job)
33
+
34
+ if response.present? && response.unavailable?
35
+ logger.error("Could not #{type} for #{plug_installation_id}: #{response.parsed_json_body}")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,8 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ class ApplicationRecord # < ActiveRecord::Base
6
+ # self.abstract_class = true
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ class ErrorSerializer
6
+ class << self
7
+ def format(message)
8
+ {
9
+ errors: [
10
+ {
11
+ message: message,
12
+ },
13
+ ],
14
+ }.to_json
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module Hephaestus
5
+ module Headers
6
+ YETTO_DELIVERY_ID = "HTTP_X_YETTO_DELIVERY_ID"
7
+
8
+ HEADER_EVENT = "HTTP_X_YETTO_EVENT"
9
+ EVENT_AFTER_CREATE = "created"
10
+ EVENT_AFTER_UPDATE = "updated"
11
+ EVENT_AFTER_RENAME = "renamed"
12
+ EVENT_AFTER_DESTROY = "destroyed"
13
+
14
+ # for conversations
15
+ EVENT_AFTER_VIEW = "viewed"
16
+
17
+ HEADER_RECORD_TYPE = "HTTP_X_YETTO_RECORD_TYPE"
18
+ RECORD_TYPE_PLUG_INSTALLATION = "plug_installation"
19
+ RECORD_TYPE_MESSAGE = "message"
20
+ RECORD_TYPE_INBOX = "inbox"
21
+ RECORD_TYPE_ORGANIZATION = "organization"
22
+
23
+ RECORD_TYPE_CONVERSATION = "conversation"
24
+
25
+ HEADER_SIGNATURE = "HTTP_X_YETTO_SIGNATURE"
26
+ end
27
+ end
@@ -0,0 +1,99 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "jwt"
5
+ require "openssl"
6
+ require "httpsensible"
7
+
8
+ class Object
9
+ # Relies on the fix implemented in https://github.com/rails/rails/pull/39966 to ensure
10
+ # that blank values don't result in queries with empty values, eg. `?filter[metadata]=`, when
11
+ # we prefer (and accept!) `?filter[metadata]`.
12
+ def to_query(key)
13
+ blank? ? CGI.escape(key.to_param).to_s : "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
14
+ end
15
+ end
16
+
17
+ module Hephaestus
18
+ class YettoService
19
+ # Version is set by the consuming plug
20
+ YETTO_API_VERSION_TLD = "#{Hephaestus::PROTOCOL}#{Hephaestus::YETTO_API_URL}/#{Rails.configuration.yetto_api_version}"
21
+
22
+ class << self
23
+ def yetto_client
24
+ @yetto_client ||= Httpsensible::Client.new(user_agent: "#{Rails.application.class.module_parent.name}/#{Hephaestus::Engine::GIT_SHA}")
25
+ end
26
+
27
+ def encoded_jwt
28
+ Httpsensible::JWT.encode_jwt(Hephaestus::YETTO_PLUG_PEM, Hephaestus::YETTO_PLUG_ID)
29
+ end
30
+
31
+ def perform_token_exchange(plug_installation_id)
32
+ response = yetto_client.with_headers({ "Authorization" => "Bearer #{encoded_jwt}" }).post("#{YETTO_API_VERSION_TLD}/plug/installations/#{plug_installation_id}/access_tokens")
33
+ body = response.parsed_json_body
34
+ body["token"]
35
+ end
36
+
37
+ def get_plug_installation(plug_installation_id)
38
+ token = perform_token_exchange(plug_installation_id)
39
+ yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).get("#{YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}")
40
+ end
41
+
42
+ def update_plug_installation(plug_installation_id, params)
43
+ plug_installation = {}
44
+
45
+ plug_installation[:settings] = params.fetch(:settings, {})
46
+ plug_installation[:credentials] = params.fetch(:credentials, {})
47
+
48
+ token = perform_token_exchange(plug_installation_id)
49
+ yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).patch("#{YETTO_API_VERSION_TLD}/installations/#{plug_installation_id}", json: plug_installation)
50
+ end
51
+
52
+ def get_messages_in_inbox(plug_installation_id, inbox_id, filter: {})
53
+ token = perform_token_exchange(plug_installation_id)
54
+
55
+ yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).get("#{YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/messages#{to_filter_query(filter)}")
56
+ end
57
+
58
+ def get_messages_in_conversation(plug_installation_id, conversation_id, filter: {})
59
+ token = perform_token_exchange(plug_installation_id)
60
+
61
+ yetto_client.with_headers("Authorization" => "Bearer #{token}").get("#{YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages#{to_filter_query(filter)}")
62
+ end
63
+
64
+ def update_message(plug_installation_id, message_id, params)
65
+ token = perform_token_exchange(plug_installation_id)
66
+
67
+ yetto_client.with_headers("Authorization" => "Bearer #{token}").patch("#{YETTO_API_VERSION_TLD}/messages/#{message_id}", json: params)
68
+ end
69
+
70
+ def create_conversation(plug_installation_id, inbox_id, params)
71
+ token = perform_token_exchange(plug_installation_id)
72
+
73
+ yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).post("#{YETTO_API_VERSION_TLD}/inboxes/#{inbox_id}/conversations", json: params)
74
+ end
75
+
76
+ def create_message_reply(plug_installation_id, message_id, params)
77
+ token = perform_token_exchange(plug_installation_id)
78
+
79
+ yetto_client.with_headers("Authorization" => "Bearer #{token}").post("#{YETTO_API_VERSION_TLD}/messages/#{message_id}/replies", json: params)
80
+ end
81
+
82
+ def add_message_to_conversation(plug_installation_id, conversation_id, params)
83
+ token = perform_token_exchange(plug_installation_id)
84
+
85
+ yetto_client.with_headers({ "Authorization" => "Bearer #{token}" }).post("#{YETTO_API_VERSION_TLD}/conversations/#{conversation_id}/messages", json: params)
86
+ end
87
+
88
+ def get_plug_installations(filter: {})
89
+ yetto_client.with_headers({ "Authorization" => "Bearer #{encoded_jwt}" }).get("#{YETTO_API_VERSION_TLD}/plug/installations#{to_filter_query(filter)}")
90
+ end
91
+
92
+ private def to_filter_query(hash)
93
+ return "" if hash.nil?
94
+
95
+ "?#{hash.to_query("filter")}"
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,7 @@
1
+ <div class="container">
2
+ <h1>Staff page</h1>
3
+ <div class="row">
4
+ <div class="col-md-12">
5
+ <%= yield %>
6
+ </div>
7
+ </div>
@@ -0,0 +1 @@
1
+ Hello, staff!
@@ -0,0 +1,49 @@
1
+ # SQLite. Versions 3.8.0 and up are supported.
2
+ # gem install sqlite3
3
+ #
4
+ # Ensure the SQLite 3 gem is defined in your Gemfile
5
+ # gem "sqlite3"
6
+ #
7
+ default: &default
8
+ adapter: sqlite3
9
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10
+ timeout: 5000
11
+
12
+ development:
13
+ primary:
14
+ <<: *default
15
+ database: storage/development.sqlite3
16
+ queue:
17
+ <<: *default
18
+ database: storage/development_queue.sqlite3
19
+ migrations_paths: db/queue_migrate
20
+
21
+
22
+ # Warning: The database defined as "test" will be erased and
23
+ # re-generated from your development database when you run "rake".
24
+ # Do not set this db to the same as development or production.
25
+ test:
26
+ primary:
27
+ <<: *default
28
+ database: storage/test.sqlite3
29
+ queue:
30
+ <<: *default
31
+ database: storage/test_queue.sqlite3
32
+ migrations_paths: db/queue_migrate
33
+
34
+
35
+
36
+ # SQLite3 write its data on the local filesystem, as such it requires
37
+ # persistent disks. If you are deploying to a managed service, you should
38
+ # make sure it provides disk persistence, as many don't.
39
+ #
40
+ # Similarly, if you deploy your application as a Docker container, you must
41
+ # ensure the database is located in a persisted volume.
42
+ production:
43
+ primary:
44
+ <<: *default
45
+ database: /mnt/data/production.sqlite3
46
+ queue:
47
+ <<: *default
48
+ database: /mnt/data/production_queue.sqlite3
49
+ migrations_paths: db/queue_migrate
@@ -0,0 +1,89 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require "active_support/core_ext/integer/time"
5
+
6
+ Rails.application.configure do
7
+ # Settings specified here will take precedence over those in config/application.rb.
8
+
9
+ # In the development environment your application's code is reloaded any time
10
+ # it changes. This slows down response time but is perfect for development
11
+ # since you don't have to restart the web server when you make code changes.
12
+ config.enable_reloading = true
13
+
14
+ # Do not eager load code on boot.
15
+ config.eager_load = false
16
+
17
+ # Show full error reports.
18
+ config.consider_all_requests_local = true
19
+
20
+ # Enable server timing.
21
+ config.server_timing = true
22
+
23
+ # Enable/disable caching. By default caching is disabled.
24
+ # Run rails dev:cache to toggle caching.
25
+ if Rails.root.join("tmp/caching-dev.txt").exist?
26
+ config.action_controller.perform_caching = true
27
+ config.action_controller.enable_fragment_cache_logging = true
28
+
29
+ config.cache_store = :memory_store
30
+ config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" }
31
+ else
32
+ config.action_controller.perform_caching = false
33
+
34
+ config.cache_store = :null_store
35
+ end
36
+
37
+ if defined?(ActionMailer)
38
+ # Don't care if the mailer can't send.
39
+ config.action_mailer.raise_delivery_errors = false
40
+
41
+ # Disable caching for Action Mailer templates even if Action Controller
42
+ # caching is enabled.
43
+ config.action_mailer.perform_caching = false
44
+
45
+ config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
46
+ end
47
+
48
+ # Print deprecation notices to the Rails logger.
49
+ config.active_support.deprecation = :log
50
+
51
+ # Raise exceptions for disallowed deprecations.
52
+ config.active_support.disallowed_deprecation = :raise
53
+
54
+ # Tell Active Support which deprecation messages to disallow.
55
+ config.active_support.disallowed_deprecation_warnings = []
56
+
57
+ # Highlight code that enqueued background job in logs.
58
+ config.active_job.verbose_enqueue_logs = true
59
+
60
+ # Raises error for missing translations.
61
+ # config.i18n.raise_on_missing_translations = true
62
+
63
+ # Annotate rendered view with file names.
64
+ config.action_view.annotate_rendered_view_with_filenames = true
65
+
66
+ # Raise error when a before_action's only/except options reference missing actions.
67
+ config.action_controller.raise_on_missing_callback_actions = true
68
+
69
+ config.active_job.queue_adapter = :solid_queue
70
+ config.solid_queue.silence_polling = true
71
+ config.solid_queue.connects_to = { database: { writing: :queue } }
72
+
73
+ # Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
74
+ # config.generators.apply_rubocop_autocorrect_after_generate!
75
+
76
+ # establish a DEBUG environment
77
+ if ENV.fetch("DEBUG", false) && defined?(Rails::Server)
78
+ require "debug/open_nonstop"
79
+ end
80
+
81
+ # Load dotenv only in development environment
82
+ Dotenv::Rails.overwrite = true
83
+
84
+ # Let dev server run on GitHub Codespaces
85
+ config.hosts << /[a-z0-9\-]+\.githubpreview\.dev/
86
+
87
+ # Let dev server run on ngrok domains
88
+ config.hosts << /[a-z0-9\-]+\.ngrok\.io/
89
+ end