rails_spotlight 0.2.5 → 0.3.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +58 -5
  3. data/chrome_ext_private_policy.md +7 -3
  4. data/lib/rails_spotlight/app_notifications.rb +3 -1
  5. data/lib/rails_spotlight/channels/handlers/live_console_handler.rb +64 -0
  6. data/lib/rails_spotlight/channels/handlers/logs_handler.rb +38 -0
  7. data/lib/rails_spotlight/channels/handlers.rb +29 -0
  8. data/lib/rails_spotlight/channels/silence_action_cable_broadcaster_logging.rb +26 -0
  9. data/lib/rails_spotlight/channels/spotlight_channel.rb +71 -0
  10. data/lib/rails_spotlight/channels.rb +2 -2
  11. data/lib/rails_spotlight/configuration.rb +21 -4
  12. data/lib/rails_spotlight/event.rb +2 -1
  13. data/lib/rails_spotlight/log_interceptor.rb +37 -29
  14. data/lib/rails_spotlight/middlewares/handlers/base_action_handler.rb +34 -2
  15. data/lib/rails_spotlight/middlewares/handlers/code_analysis_action_handler.rb +89 -0
  16. data/lib/rails_spotlight/middlewares/handlers/console_action_handler.rb +5 -23
  17. data/lib/rails_spotlight/middlewares/handlers/directory_index_action_handler.rb +112 -0
  18. data/lib/rails_spotlight/middlewares/handlers/file_action_handler.rb +18 -4
  19. data/lib/rails_spotlight/middlewares/handlers/meta_action_handler.rb +0 -1
  20. data/lib/rails_spotlight/middlewares/handlers/sql_action_handler.rb +6 -15
  21. data/lib/rails_spotlight/middlewares/handlers/verify_action_handler.rb +6 -2
  22. data/lib/rails_spotlight/middlewares/request_completed.rb +14 -10
  23. data/lib/rails_spotlight/middlewares/request_handler.rb +5 -1
  24. data/lib/rails_spotlight/railtie.rb +12 -4
  25. data/lib/rails_spotlight/render_view_reporter.rb +3 -1
  26. data/lib/rails_spotlight/utils.rb +2 -0
  27. data/lib/rails_spotlight/version.rb +1 -1
  28. data/lib/tasks/init.rake +75 -6
  29. metadata +9 -4
  30. data/lib/rails_spotlight/channels/live_console_channel.rb +0 -62
  31. data/lib/rails_spotlight/channels/request_completed_channel.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e36512b12e263c5853279d061fe2760ad7fff28c754c3362f8c848867845477
4
- data.tar.gz: cc4696e37e250be0413f0286503f8a63fa18a2c43353114720e9580fa4fc646e
3
+ metadata.gz: b9eca5a5464ed6e44b8c65f206f96ead18ff5cc73a0f9395b2b5cc863a28b270
4
+ data.tar.gz: 4bc0b0e190af921c926a1f08bb43954dee8b3aa488d867c885b4af84c3816ab7
5
5
  SHA512:
6
- metadata.gz: dd8e249fcfe8ced528c2f60225b00e8142e93989736d77090b9cc3ad0a9cf8c50dcbc85b9f76f196168ab1e6946f85d594c48935fe0751e8675d612f2d3fffa1
7
- data.tar.gz: 925e741a5c6b755deed002c3833da4487a24a530d025177373fa86fca3a5ad2ef84d1730497c7c2ad128a2c9bf526127166252793b056e587ed43ab6a585dbea
6
+ metadata.gz: 552d1619a7c990dbce5c52fa883a45bcb5d5d58fa3a81a947fd244badbffcab5516607bb739dc73803d90ababd44755d78b9efe159d6d7d785181cef9e390b59
7
+ data.tar.gz: 8173b1bfc3514712a350d08c8441552c8091c4b3559357192cde5de84470aee320dc26be1663ca07aa1fcbe7fb48573001d7d9bffce578886b3c5c75302633d3
data/README.md CHANGED
@@ -39,15 +39,21 @@ file will be created in `config/rails_spotlight.yml`
39
39
  MIDDLEWARE_SKIPPED_PATHS: []
40
40
  NOT_ENCODABLE_EVENT_VALUES:
41
41
  SKIP_RENDERED_IVARS: []
42
+ BLOCK_EDITING_FILES: false
43
+ BLOCK_EDITING_FILES_OUTSIDE_OF_THE_PROJECT: true
44
+ DIRECTORY_INDEX_IGNORE: ['/.git', '**/*.lock', '**/.DS_Store', '/app/assets/images/**', '/app/assets/fonts/**', '/app/assets/builds/**']
45
+ RUBOCOP_CONFIG_PATH: '.rubocop.yml'
42
46
  # Rest of the configuration is required for ActionCable. It will be disabled automatically in when ActionCable is not available.
43
- # LIVE_CONSOLE_ENABLED from version 0.2.3 do not require ActionCable to be enabled.
47
+ AUTO_MOUNT_ACTION_CABLE: false
48
+ ACTION_CABLE_MOUNT_PATH: /cable
49
+ # Required for all action cable features
50
+ USE_ACTION_CABLE: false
44
51
  LIVE_CONSOLE_ENABLED: false
45
52
  # Experimental feature.
46
53
  REQUEST_COMPLETED_BROADCAST_ENABLED: false
47
- AUTO_MOUNT_ACTION_CABLE: false
48
- ACTION_CABLE_MOUNT_PATH: /cable
49
- BLOCK_EDITING_FILES: false
50
- BLOCK_EDITING_FILES_OUTSIDE_OF_THE_PROJECT: true
54
+ LIVE_LOGS_ENABLED: false
55
+ DEFAULT_RS_SRC: default
56
+ FORM_JS_EXECUTION_TOKEN: <%= Digest::MD5.hexdigest(Rails.application.class.respond_to?(:module_parent_name) ? Rails.application.class.module_parent_name : Rails.application.class.parent_name)%>
51
57
  ```
52
58
 
53
59
  ## Additional metrics
@@ -60,6 +66,53 @@ To enable additional rendering metrics like local variables, instance variables,
60
66
  <% end %>
61
67
  ```
62
68
 
69
+ ## Experimental features
70
+ Live logs requires action cable to be enabled.
71
+
72
+ USE_ACTION_CABLE: true
73
+ LIVE_LOGS_ENABLED: true
74
+
75
+ When you want to use different sources for the cable logs you need to update `cable.yml` file.
76
+
77
+ ```yaml
78
+ development:
79
+ adapter: redis
80
+ url: redis://localhost:6379/1
81
+ ```
82
+ **Note:** Redis is required for live logs. so just add it to your Gemfile and run `bundle install`
83
+
84
+ modify yor `development.rb` file
85
+
86
+ ```ruby
87
+ config.action_cable.allowed_request_origins = ['chrome-extension://chjfnpmbgdbipfogflkhleaceacndaop' ]
88
+ ```
89
+
90
+ Now just run your servers with environment variable RS_SRC=my_source_name
91
+ like `RS_SRC=sidekiq bundle exec sidekiq -C config/sidekiq.yml`
92
+ or `RS_SRC=web bundle exec puma -C config/puma.rb`
93
+
94
+ ---
95
+
96
+ Forms (Filling forms with scenarios)
97
+
98
+ To be able use full potential of the forms you need to generate partial that helps with js code injection.
99
+
100
+ Just use `rails rails_spotlight:inject_js_partial` and inject partial to your layout file as instructed in past generation message.
101
+ setup your `FORM_JS_EXECUTION_TOKEN` or use pregnerated one in your extension settings.
102
+
103
+ # Advanced log configuration
104
+ You can add to your Initializers `config/initializers/rails_spotlight.rb` file with the additional configuration for the logger.
105
+
106
+ ```ruby
107
+ unless Rails.env.production?
108
+ # Publish logs to the spotlight (only when implementation is based on the Rails.logger)
109
+ defined?(Logger) && Logger&.extend(RailsSpotlight::LogInterceptor)
110
+ # Publish Hutch logs to the spotlight
111
+ defined?(Hutch::Logging) && Hutch::Logging.logger&.extend(RailsSpotlight::LogInterceptor)
112
+ end
113
+ ```
114
+
115
+
63
116
  ## Troubleshooting
64
117
 
65
118
  Known issue:
@@ -1,6 +1,6 @@
1
1
  ## Privacy Policy for the Rails Spotlight Chrome Extension
2
2
 
3
- **Last Updated:** October 18, 2023
3
+ **Last Updated:** Aug 18, 2024
4
4
 
5
5
  ### 1. Introduction
6
6
 
@@ -30,10 +30,14 @@ We do not sell or share your data with third parties for their promotional purpo
30
30
 
31
31
  We are committed to ensuring that your information is secure. To prevent unauthorized access or disclosure, we have put in place suitable physical, electronic, and managerial procedures to safeguard and secure the information we collect.
32
32
 
33
- ### 7. Changes to This Privacy Policy
33
+ ### 7. Subscription and Refund Policy
34
+
35
+ By purchasing a membership for Rails Spotlight, you gain access to advanced features and contribute to the ongoing development of the tool. Please note that all memberships are non-refundable. Once purchased, no refunds will be issued, so we encourage you to carefully consider your decision before completing the purchase. However, you can cancel your subscription at any time if you no longer wish to use the service. Cancellation will prevent any future charges, but it will not refund the current or past payments.
36
+
37
+ ### 8. Changes to This Privacy Policy
34
38
 
35
39
  We may update our Privacy Policy from time to time. Thus, we advise you to review this document periodically for any changes. We will notify you of any changes by posting the new Privacy Policy in this location. Changes are effective immediately after they are posted.
36
40
 
37
- ### 8. Contact Us
41
+ ### 9. Contact Us
38
42
 
39
43
  If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.
@@ -87,7 +87,9 @@ module RailsSpotlight
87
87
  # send_file.action_controller: Triggered when a file is sent as a response.
88
88
  # redirect_to.action_controller: Triggered when a redirect response is sent.
89
89
  # halted_callback.action_controller: Triggered when a filter or callback halts the request.
90
- # render_collection.action_view: This event is triggered when a collection is rendered using a partial. It includes details about the collection being rendered, such as the collection name and the partial being used to render each item.
90
+ # render_collection.action_view: This event is triggered when a collection is rendered using a partial.
91
+ # It includes details about the collection being rendered,
92
+ # such as the collection name and the partial being used to render each item.
91
93
  end
92
94
 
93
95
  def subscribe(event_name)
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../rails_command_executor'
4
+
5
+ module RailsSpotlight
6
+ module Channels
7
+ module Handlers
8
+ class LiveConsoleHandler
9
+ TYPE = 'console'
10
+
11
+ def initialize(data)
12
+ @data = data
13
+ end
14
+
15
+ attr_reader :data
16
+
17
+ def call
18
+ return unless ::RailsSpotlight.config.live_console_enabled?
19
+ return unless data['type'] == TYPE
20
+
21
+ command = data['command']
22
+ inspect_types = data['inspect_types']
23
+ for_project = Array(data['project'])
24
+
25
+ raise_project_mismatch_error!(for_project) if for_project.present? && !for_project.include?(project)
26
+
27
+ execute_command(command, inspect_types)
28
+ end
29
+
30
+ def executor
31
+ @executor ||= ::RailsSpotlight::RailsCommandExecutor.new
32
+ end
33
+
34
+ def raise_project_mismatch_error!(for_project)
35
+ raise ::RailsSpotlight::Channels::Handlers::ResponseError.new(
36
+ "Project mismatch, The command was intended for the #{for_project} project. This is #{project} project",
37
+ code: :project_mismatch
38
+ )
39
+ end
40
+
41
+ def execute_command(command, inspect_types)
42
+ RailsSpotlight.config.logger && RailsSpotlight.config.logger.info("Executing command: #{command}") # rubocop:disable Style/SafeNavigation
43
+
44
+ executor.execute(command)
45
+ if executor.execution_successful?
46
+ {
47
+ payload: { result: executor.result_as_json(inspect_types: inspect_types) },
48
+ }
49
+ else
50
+ {
51
+ payload: { failed: executor.result_as_json }
52
+ }
53
+ end
54
+ rescue => e # rubocop:disable Style/RescueStandardError
55
+ { error: e.message }
56
+ end
57
+
58
+ def project
59
+ ::RailsSpotlight.config.project_name
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSpotlight
4
+ module Channels
5
+ module Handlers
6
+ class LogsHandler
7
+ TYPE = 'logs'
8
+
9
+ def initialize(data)
10
+ @data = data
11
+ end
12
+
13
+ attr_reader :data
14
+
15
+ def call
16
+ return unless ::RailsSpotlight.config.live_console_enabled?
17
+ return unless data['type'] == TYPE
18
+
19
+ for_project = Array(data['project'])
20
+ raise_project_mismatch_error!(for_project) if for_project.present? && !for_project.include?(project)
21
+
22
+ { payload: data[:payload] }
23
+ end
24
+
25
+ def raise_project_mismatch_error!(for_project)
26
+ raise ::RailsSpotlight::Channels::Handlers::ResponseError.new(
27
+ "Project mismatch, Logs from #{for_project} project cannot be forwarded in #{project} project",
28
+ code: :project_mismatch
29
+ )
30
+ end
31
+
32
+ def project
33
+ ::RailsSpotlight.config.project_name
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'handlers/live_console_handler'
4
+ require_relative 'handlers/logs_handler'
5
+
6
+ module RailsSpotlight
7
+ module Channels
8
+ module Handlers
9
+ ResponseError = Class.new(StandardError) do
10
+ def initialize(message, code: :error)
11
+ @code = code
12
+ super(message)
13
+ end
14
+
15
+ attr_reader :code
16
+ end
17
+ TYPES = [LiveConsoleHandler::TYPE, LogsHandler::TYPE].freeze
18
+
19
+ def self.handle(data)
20
+ case data['type']
21
+ when LiveConsoleHandler::TYPE then LiveConsoleHandler.new(data).call
22
+ # when LogsHandler::TYPE then LogsHandler.new(data).call
23
+ else
24
+ nil
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSpotlight
4
+ module Channels
5
+ module SilenceActionCableBroadcasterLogging
6
+ refine ActionCable::Server::Broadcasting::Broadcaster do
7
+ def broadcast(message)
8
+ if broadcasting == RailsSpotlight::Channels::SpotlightChannel::SPOTLIGHT_CHANNEL
9
+ original_logger = server.logger
10
+ begin
11
+ # Replace the logger with a no-op logger to silence the log
12
+ server.logger = ActiveSupport::Logger.new(nil)
13
+ super(message)
14
+ ensure
15
+ # Restore the original logger after broadcasting
16
+ server.logger = original_logger
17
+ end
18
+ else
19
+ super(message)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'handlers'
4
+ # require_relative 'silence_action_cable_broadcaster_logging'
5
+
6
+ module RailsSpotlight
7
+ module Channels
8
+ class SpotlightChannel < ActionCable::Channel::Base
9
+ def self.broadcast(attrs = {})
10
+ broadcasting = ::RailsSpotlight::Channels::SPOTLIGHT_CHANNEL
11
+ message = {
12
+ type: attrs[:type],
13
+ code: attrs[:code] || 'ok',
14
+ project: ::RailsSpotlight.config.project_name,
15
+ version: ::RailsSpotlight::VERSION,
16
+ payload: attrs[:payload] || {}
17
+ }
18
+ coder = ::ActiveSupport::JSON
19
+ encoded = coder ? coder.encode(message) : message
20
+ ActionCable.server.pubsub.broadcast(broadcasting, encoded)
21
+ # We do not use the following code because it is triggering logs and can cause an infinite loop
22
+ # ActionCable.server.broadcast(
23
+ # ::RailsSpotlight::Channels::SPOTLIGHT_CHANNEL,
24
+ # {
25
+ # type: attrs[:type],
26
+ # code: attrs[:code] || 'ok',
27
+ # project: ::RailsSpotlight.config.project_name,
28
+ # version: ::RailsSpotlight::VERSION,
29
+ # payload: attrs[:payload] || {}
30
+ # }
31
+ # )
32
+ rescue StandardError => e
33
+ RailsSpotlight.config.logger.fatal("#{e.message}\n #{e.backtrace.join("\n ")}")
34
+ end
35
+
36
+ def subscribed
37
+ stream_from ::RailsSpotlight::Channels::SPOTLIGHT_CHANNEL
38
+ publish({ message: "Your #{project} project is now connected to the spotlight channel.", code: :connected, type: :info })
39
+ end
40
+
41
+ def unsubscribed
42
+ # Any cleanup needed when channel is unsubscribed
43
+ end
44
+
45
+ def receive(data)
46
+ return publish({ message: 'Unknown type of request', code: :unknown_type, type: :error }) unless Handlers::TYPES.include?(data['type'])
47
+
48
+ result = Handlers.handle(data)
49
+ publish({ payload: result[:payload], code: result[:code] || :ok, type: data['type'] }) if result[:payload]
50
+ rescue ::RailsSpotlight::Channels::Handlers::ResponseError => e
51
+ publish({ message: e.message, code: e.code, type: :error })
52
+ end
53
+
54
+ private
55
+
56
+ def publish(data)
57
+ connection.transmit identifier: @identifier, message: data.merge(project: project, version: version)
58
+ # we do not use transmit because it is triggering logs and can cause an infinite loop
59
+ # transmit(data.merge(project: project, version: version))
60
+ end
61
+
62
+ def project
63
+ ::RailsSpotlight.config.project_name
64
+ end
65
+
66
+ def version
67
+ ::RailsSpotlight::VERSION
68
+ end
69
+ end
70
+ end
71
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RailsSpotlight
4
4
  module Channels
5
- autoload(:LiveConsoleChannel, 'rails_spotlight/channels/live_console_channel') if defined?(ActionCable)
6
- autoload(:RequestCompletedChannel, 'rails_spotlight/channels/request_completed_channel') if defined?(ActionCable)
5
+ SPOTLIGHT_CHANNEL = 'RailsSpotlight::Channels::SpotlightChannel'.freeze
6
+ autoload(:SpotlightChannel, 'rails_spotlight/channels/spotlight_channel') if defined?(ActionCable)
7
7
  end
8
8
  end
@@ -10,6 +10,10 @@ module RailsSpotlight
10
10
  'ActionDispatch' => ['ActionDispatch::Request', 'ActionDispatch::Response']
11
11
  }.freeze
12
12
 
13
+ DEFAULT_DIRECTORY_INDEX_IGNORE = %w[
14
+ /.git **/*.lock **/.DS_Store /app/assets/images/** /app/assets/fonts/** /app/assets/builds/** **/.keep
15
+ ].freeze
16
+
13
17
  SKIP_RENDERED_IVARS = %i[
14
18
  @_routes
15
19
  @_config
@@ -32,7 +36,9 @@ module RailsSpotlight
32
36
 
33
37
  attr_reader :project_name, :source_path, :logger, :storage_path, :storage_pool_size, :middleware_skipped_paths,
34
38
  :not_encodable_event_values, :action_cable_mount_path,
35
- :block_editing_files, :block_editing_files_outside_of_the_project, :skip_rendered_ivars
39
+ :block_editing_files, :block_editing_files_outside_of_the_project, :skip_rendered_ivars,
40
+ :directory_index_ignore, :rubocop_config_path, :use_action_cable, :default_rs_src,
41
+ :form_js_execution_token
36
42
 
37
43
  def initialize(opts = {}) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
38
44
  @project_name = opts[:project_name] || detect_project_name
@@ -44,27 +50,38 @@ module RailsSpotlight
44
50
  @request_completed_broadcast_enabled = opts[:request_completed_broadcast_enabled].nil? ? false : true?(opts[:request_completed_broadcast_enabled])
45
51
  @middleware_skipped_paths = opts[:middleware_skipped_paths] || []
46
52
  @not_encodable_event_values = DEFAULT_NOT_ENCODABLE_EVENT_VALUES.merge(opts[:not_encodable_event_values] || {})
53
+ @use_action_cable = opts[:use_action_cable].nil? ? false : true?(opts[:use_action_cable])
47
54
  @auto_mount_action_cable = opts[:auto_mount_action_cable].nil? ? false : true?(opts[:auto_mount_action_cable])
48
55
  @action_cable_mount_path = opts[:action_cable_mount_path] || '/cable'
49
56
  @block_editing_files = opts[:block_editing_files].nil? ? false : true?(opts[:block_editing_files])
50
57
  @block_editing_files_outside_of_the_project = opts[:block_editing_files_outside_of_the_project].nil? ? true : true?(opts[:block_editing_files_outside_of_the_project])
51
58
  @skip_rendered_ivars = SKIP_RENDERED_IVARS + (opts[:skip_rendered_ivars] || []).map(&:to_sym)
59
+ @directory_index_ignore = opts[:directory_index_ignore] || DEFAULT_DIRECTORY_INDEX_IGNORE
60
+ @rubocop_config_path = opts[:rubocop_config_path] ? File.join(self.class.rails_root, opts[:rubocop_config_path]) : nil
61
+ @live_logs_enabled = opts[:live_logs_enabled].nil? ? false : true?(opts[:live_logs_enabled])
62
+ @default_rs_src = opts[:default_rs_src] || 'default'
63
+ @form_js_execution_token = opts[:form_js_execution_token] || Digest::MD5.hexdigest(detect_project_name)
52
64
  end
53
65
 
54
66
  def live_console_enabled
55
- @live_console_enabled && action_cable_present?
67
+ @live_console_enabled && use_action_cable && action_cable_present?
68
+ end
69
+
70
+ def live_logs_enabled
71
+ @live_logs_enabled && use_action_cable && action_cable_present?
56
72
  end
57
73
 
58
74
  alias live_console_enabled? live_console_enabled
75
+ alias live_logs_enabled? live_logs_enabled
59
76
 
60
77
  def request_completed_broadcast_enabled
61
- @request_completed_broadcast_enabled && action_cable_present?
78
+ @request_completed_broadcast_enabled && use_action_cable && action_cable_present?
62
79
  end
63
80
 
64
81
  alias request_completed_broadcast_enabled? request_completed_broadcast_enabled
65
82
 
66
83
  def auto_mount_action_cable
67
- @auto_mount_action_cable && action_cable_present?
84
+ @auto_mount_action_cable && use_action_cable && action_cable_present?
68
85
  end
69
86
 
70
87
  alias auto_mount_action_cable? auto_mount_action_cable
@@ -57,8 +57,9 @@ module RailsSpotlight
57
57
  def not_encodable?(value)
58
58
  ::RailsSpotlight.config.not_encodable_event_values.any? do |module_name, class_names|
59
59
  next unless defined?(module_name.constantize)
60
-
61
60
  class_names.any? { |class_name| value.is_a?(class_name.constantize) }
61
+ rescue => e
62
+ puts "Error in not_encodable? method: #{e.message}"
62
63
  end
63
64
  end
64
65
 
@@ -1,45 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'channels'
4
+
3
5
  module RailsSpotlight
4
6
  module LogInterceptor
5
- def debug(message = nil, *args)
6
- push_event(:debug, message)
7
- super
8
- end
7
+ SEVERITY = %w[debug info warn error fatal unknown].freeze
8
+ SEVERITY_MAP = { 0 => 'debug', 1 => 'info', 2 => 'warn', 3 => 'error', 4 => 'fatal', 5 => 'unknown' }.freeze
9
+
10
+ def add(severity, message = nil, progname = nil)
11
+ severity ||= 5
12
+ return true if @logdev.nil? || severity < level
13
+
14
+ progname = @progname if progname.nil?
15
+
16
+ if message.nil?
17
+ if block_given?
18
+ message = yield
19
+ else
20
+ message = progname
21
+ progname = @progname
22
+ end
23
+ end
24
+ return true if _skip_logging?(message)
9
25
 
10
- def info(message = nil, *args)
11
- push_event(:info, message)
12
- super
26
+ _push_event(SEVERITY_MAP[severity], message, progname)
27
+ super(severity, message, progname) if defined?(super)
28
+ true
13
29
  end
14
30
 
15
- def warn(message = nil, *args)
16
- push_event(:warn, message)
17
- super
18
- end
31
+ private
19
32
 
20
- def error(message = nil, *args)
21
- push_event(:error, message)
22
- super
23
- end
33
+ def _skip_logging?(message)
34
+ return false unless ::RailsSpotlight.config.live_console_enabled?
35
+ return false unless message.is_a?(String)
24
36
 
25
- def fatal(message = nil, *args)
26
- push_event(:fatal, message)
27
- super
37
+ message.include?(::RailsSpotlight::Channels::SPOTLIGHT_CHANNEL)
28
38
  end
29
39
 
30
- def unknown(message = nil, *args)
31
- push_event(:unknown, message)
32
- super
33
- end
40
+ def _push_event(level, message, progname = nil)
41
+ callsite = Utils.dev_callsite(caller.drop(1))
42
+ name = progname.is_a?(String) || progname.is_a?(Symbol) ? progname : nil
43
+ AppRequest.current.events << Event.new('rsl.notification.log', 0, 0, 0, callsite.merge(message: message, level: level, progname: name)) if AppRequest.current && callsite
34
44
 
35
- private
45
+ return unless ::RailsSpotlight.config.live_console_enabled?
46
+ return if message.blank?
36
47
 
37
- def push_event(level, message)
38
- callsite = AppRequest.current && Utils.dev_callsite(caller.drop(1))
39
- if callsite
40
- payload = callsite.merge(message: message, level: level)
41
- AppRequest.current.events << Event.new('rsl.notification.log', 0, 0, 0, payload)
42
- end
48
+ id = AppRequest.current ? AppRequest.current.id : nil
49
+ payload = (callsite || {}).merge(msg: message, src: ENV['RS_SRC'] || ::RailsSpotlight.config.default_rs_src, l: level, dt: Time.now.to_f, id: id, pg: name)
50
+ ::RailsSpotlight::Channels::SpotlightChannel.broadcast(type: 'logs', payload: payload)
43
51
  rescue StandardError => e
44
52
  RailsSpotlight.config.logger.fatal("#{e.message}\n #{e.backtrace.join("\n ")}")
45
53
  end
@@ -7,6 +7,7 @@ module RailsSpotlight
7
7
  IncorrectResponseContentType = Class.new(StandardError)
8
8
  NotFound = Class.new(StandardError)
9
9
  UnprocessableEntity = Class.new(StandardError)
10
+ Forbidden = Class.new(StandardError)
10
11
 
11
12
  def initialize(request_id, request, content_type)
12
13
  @request_id = request_id
@@ -17,12 +18,15 @@ module RailsSpotlight
17
18
  attr_reader :request_id, :request, :content_type
18
19
 
19
20
  def call
21
+ validate_project! unless skip_project_validation?
20
22
  execute
21
23
  response
22
24
  rescue NotFound => e
23
25
  not_found_response(e.message)
24
26
  rescue UnprocessableEntity => e
25
27
  unprocessed_response(e.message)
28
+ rescue Forbidden => e
29
+ forbidden_response(e.message)
26
30
  rescue => e # rubocop:disable Style/RescueStandardError
27
31
  internal_server_error_response(e.message)
28
32
  end
@@ -31,6 +35,10 @@ module RailsSpotlight
31
35
 
32
36
  attr_writer :status, :headers
33
37
 
38
+ def skip_project_validation?
39
+ false
40
+ end
41
+
34
42
  def headers
35
43
  @headers ||= {}
36
44
  end
@@ -57,6 +65,10 @@ module RailsSpotlight
57
65
  raise 'Invalid JSON'
58
66
  end
59
67
 
68
+ def body_fetch(*args)
69
+ json_request_body.fetch(*args)
70
+ end
71
+
60
72
  def internal_server_error_response(message)
61
73
  response(500, message_to_body(message))
62
74
  end
@@ -65,6 +77,10 @@ module RailsSpotlight
65
77
  response(422, message_to_body(message))
66
78
  end
67
79
 
80
+ def forbidden_response(message)
81
+ response(403, message_to_body(message))
82
+ end
83
+
68
84
  def not_found_response(message)
69
85
  response(404, message_to_body(message))
70
86
  end
@@ -76,7 +92,8 @@ module RailsSpotlight
76
92
  def response_headers(headers = {})
77
93
  {
78
94
  'Content-Type' => content_type == :json ? 'application/json; charset=utf-8' : 'text/plain; charset=utf-8',
79
- 'X-Rails-Spotlight' => '1.0.0',
95
+ 'X-Rails-Spotlight' => ::RailsSpotlight::VERSION,
96
+ 'X-Rails-Spotlight-Project' => ::RailsSpotlight.config.project_name,
80
97
  'X-Request-Id' => request_id
81
98
  }.merge(headers)
82
99
  end
@@ -85,10 +102,25 @@ module RailsSpotlight
85
102
  body = if overridden_body.present?
86
103
  content_type == :json ? overridden_body.to_json : overridden_body
87
104
  else
88
- content_type == :json ? json_response_body.to_json : text_response_body
105
+ content_type == :json ? json_response_body.merge({ project: ::RailsSpotlight.config.project_name }).to_json : text_response_body
89
106
  end
90
107
  [overridden_status.present? ? overridden_status : status, response_headers(headers), [body]]
91
108
  end
109
+
110
+ def request_spotlight_version
111
+ @request_spotlight_version ||= request.get_header('HTTP_X_RAILS_SPOTLIGHT')
112
+ end
113
+
114
+ def request_for_projects
115
+ @request_for_projects ||= (request.get_header('HTTP_X_FOR_PROJECTS') || '').split(',').map(&:strip)
116
+ end
117
+
118
+ def validate_project!
119
+ return if request_for_projects.blank?
120
+ return if request_for_projects.include?(::RailsSpotlight.config.project_name)
121
+
122
+ raise Forbidden, "Check your settings the current request is not allowed to be executed on the #{::RailsSpotlight.config.project_name} project"
123
+ end
92
124
  end
93
125
  end
94
126
  end