hephaestus 0.7.1 → 0.7.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -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/support/hephaestus/webmocks/yetto_webmock.rb +1 -1
  38. data/lib/hephaestus/version.rb +1 -1
  39. data/templates/app/services/yetto_service.rb +1 -1
  40. metadata +36 -19
  41. data/templates/.dockerignore +0 -39
  42. data/templates/.env.sample +0 -6
  43. data/templates/.github/dependabot.yml +0 -27
  44. data/templates/.github/workflows/automerge.yml +0 -17
  45. data/templates/.github/workflows/deploy.yml +0 -30
  46. data/templates/.github/workflows/licenses.yml +0 -23
  47. data/templates/.github/workflows/lint.yml +0 -32
  48. data/templates/.github/workflows/security.yml +0 -15
  49. data/templates/.github/workflows/sorbet.yml +0 -19
  50. data/templates/.github/workflows/test.yml +0 -21
  51. data/templates/.licensed.yml +0 -43
  52. data/templates/.rubocop.yml +0 -5
  53. data/templates/.ruby-version +0 -1
  54. data/templates/.vscode/extensions.json +0 -9
  55. data/templates/.vscode/launch.json +0 -13
  56. data/templates/.vscode/settings.json +0 -52
  57. data/templates/test/integration/.keep +0 -0
  58. data/templates/test/mailers/.keep +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ddb2acb833029f86546e1ba193985470e19f06b8b1914876372ce16c0b08a4e
4
- data.tar.gz: 413397588e5473881666644b45bfcf090bc63adab199a77e51e0a3cec1226ef1
3
+ metadata.gz: d7c2f9d61793dbe5625865f819df27d6c9d63af6a8b62d24baad0179cb18292a
4
+ data.tar.gz: 2b3b19c2cf731b161b8d669e82613ef734b3724580ffaed458912e16ea2dee4e
5
5
  SHA512:
6
- metadata.gz: 13ca6d123fde10d85b3a9f90d64f12c75d395667df8a35fbd27b37e07fd952cd8497049f4ce753afe2a27b217cfc49e2fb3e57964cf2e27688f24dfb3d1beaf2
7
- data.tar.gz: 981ff3a8586e4445bc8ad96cd90554887b0c90e476a223f68c70bf78f57c9233ad972d3d44a78248c8352287f1be0e84ca0ce8c0d7ff202282f38d49a70d79a9
6
+ metadata.gz: 0dab68747b8859e1f6d0b24bb2d5d3243f4bb0278602d132eaa4a822d18b7ffaf69b9f683edf1fe1af807bfced0e009010433095725c083582decf8b0a4116f8
7
+ data.tar.gz: aaf595de123fbb62eb0582e05d94f3f437c2e0eca9564c563ec6485ba65aede1f690c8d91b266be5235c5755770136b7c46a7646c0b49006131dff1978b78c13
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
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
7
+ # [v0.7.2] - 16-11-2024
8
+ ## What's Changed
9
+ * [skip test] Release v0.7.1 by @github-actions in https://github.com/yettoapp/hephaestus/pull/28
10
+ * Move secrets into Hephaestus by @gjtorikian in https://github.com/yettoapp/hephaestus/pull/29
11
+
12
+
13
+ **Full Changelog**: https://github.com/yettoapp/hephaestus/compare/v0.7.1...v0.7.2
14
+ # [v0.7.1] - 16-11-2024
15
+ ## What's Changed
16
+ * Run as an engine by @gjtorikian in https://github.com/yettoapp/hephaestus/pull/27
17
+
18
+
19
+ **Full Changelog**: https://github.com/yettoapp/hephaestus/compare/v0.7.0...v0.7.1
1
20
  # [v0.7.0] - 15-11-2024
2
21
  ## What's Changed
3
22
  * Run as an engine by @gjtorikian in https://github.com/yettoapp/hephaestus/pull/25
@@ -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