Class: BrainzLab::DevTools::Middleware::ErrorPage
- Inherits:
-
Object
- Object
- BrainzLab::DevTools::Middleware::ErrorPage
- Defined in:
- lib/brainzlab/devtools/middleware/error_page.rb
Instance Method Summary collapse
- #call(env) ⇒ Object
- #collect_debug_data_from_info(env, info, status = 500) ⇒ Object
- #decode_html_entities(str) ⇒ Object
- #extract_exception_from_html(body) ⇒ Object
- #html_response?(headers) ⇒ Boolean
-
#initialize(app) ⇒ ErrorPage
constructor
A new instance of ErrorPage.
- #render_error_page_from_info(info, data, status = 500) ⇒ Object
Constructor Details
#initialize(app) ⇒ ErrorPage
Returns a new instance of ErrorPage.
9 10 11 12 |
# File 'lib/brainzlab/devtools/middleware/error_page.rb', line 9 def initialize(app) @app = app @renderer = Renderers::ErrorPageRenderer.new end |
Instance Method Details
#call(env) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/brainzlab/devtools/middleware/error_page.rb', line 14 def call(env) return @app.call(env) if env['REQUEST_METHOD'] == 'OPTIONS' return @app.call(env) unless should_handle?(env) begin status, headers, body = @app.call(env) # Check if this is an error response that we should intercept if status >= 400 && html_response?(headers) && !json_request?(env) && !api_path?(env) # Check if this looks like Rails' default error page body_content = collect_body(body) if body_content.include?('Action Controller: Exception caught') || body_content.include?('background: #C00') # Extract exception info from the page exception_info = extract_exception_from_html(body_content) if exception_info data = collect_debug_data_from_info(env, exception_info, status) return render_error_page_from_info(exception_info, data, status) end end end [status, headers, body] rescue Exception => e # For JSON/API requests, return a proper JSON error response if json_request?(env) || api_path?(env) capture_to_reflex(e) return json_error_response(e) end # Still capture to Reflex if available capture_to_reflex(e) # Collect debug data and render branded error page data = collect_debug_data(env, e) render_error_page(e, data) end end |
#collect_debug_data_from_info(env, info, status = 500) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/brainzlab/devtools/middleware/error_page.rb', line 96 def collect_debug_data_from_info(env, info, status = 500) context = defined?(BrainzLab::Context) ? BrainzLab::Context.current : nil collector_data = Data::Collector.get_request_data backtrace = (info[:backtrace] || []).map do |line| parsed = parse_backtrace_line(line) parsed[:in_app] = in_app_frame?(parsed[:file]) parsed end # Extract source from the first in-app frame source_extract = extract_source_from_backtrace(info[:backtrace] || []) { exception: nil, exception_class: info[:class_name], exception_message: info[:message], backtrace: backtrace, request: build_request_info(env, context), context: build_context_info(context), sql_queries: collector_data.dig(:database, :queries) || [], environment: collect_environment_info, source_extract: source_extract } end |
#decode_html_entities(str) ⇒ Object
85 86 87 88 89 90 91 92 93 94 |
# File 'lib/brainzlab/devtools/middleware/error_page.rb', line 85 def decode_html_entities(str) return str unless str str.gsub(''', "'") .gsub('"', '"') .gsub('&', '&') .gsub('<', '<') .gsub('>', '>') .gsub(' ', ' ') end |
#extract_exception_from_html(body) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/brainzlab/devtools/middleware/error_page.rb', line 58 def extract_exception_from_html(body) # Try to extract exception class and message from Rails error page if (match = body.match(%r{<h1>([^<]+)</h1>})) error_title = match[1] # Extract the exception message from the page if (msg_match = body.match(%r{<pre[^>]*>([^<]+)</pre>})) = msg_match[1] end # Try to extract backtrace from Rails 8 format # Format: <a class="trace-frames ...">path/to/file.rb:123:in 'method'</a> backtrace = [] body.scan(%r{<a[^>]*class="trace-frames[^"]*"[^>]*>\s*([^<]+)\s*</a>}m) do |trace_match| line = trace_match[0].strip # Decode HTML entities line = line.gsub(''', "'").gsub('"', '"').gsub('&', '&').gsub('<', '<').gsub('>', '>') backtrace << line unless line.empty? end { class_name: decode_html_entities(error_title.strip), message: decode_html_entities(&.strip || error_title.strip), backtrace: backtrace } end end |
#html_response?(headers) ⇒ Boolean
52 53 54 55 56 |
# File 'lib/brainzlab/devtools/middleware/error_page.rb', line 52 def html_response?(headers) # Handle both uppercase and lowercase header names content_type = headers['Content-Type'] || headers['content-type'] || '' content_type.to_s.downcase.include?('text/html') end |
#render_error_page_from_info(info, data, status = 500) ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/brainzlab/devtools/middleware/error_page.rb', line 122 def render_error_page_from_info(info, data, status = 500) # Create a simple exception-like object exception = StandardError.new(info[:message]) exception.define_singleton_method(:class) do Class.new(StandardError) do define_singleton_method(:name) { info[:class_name] } end end data[:exception] = exception html = @renderer.render(exception, data) [ status, { 'Content-Type' => 'text/html; charset=utf-8', 'Content-Length' => html.bytesize.to_s, 'X-Content-Type-Options' => 'nosniff' }, [html] ] end |