rails_error_dashboard 0.14.3 → 0.15.0

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -17
  3. data/app/controllers/rails_error_dashboard/application_controller.rb +28 -4
  4. data/app/controllers/rails_error_dashboard/errors_controller.rb +68 -2
  5. data/app/controllers/rails_error_dashboard/health_controller.rb +38 -0
  6. data/app/helpers/rails_error_dashboard/application_helper.rb +10 -2
  7. data/app/helpers/rails_error_dashboard/backtrace_helper.rb +56 -2
  8. data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +20 -2
  9. data/app/jobs/rails_error_dashboard/cascade_detection_job.rb +30 -0
  10. data/app/jobs/rails_error_dashboard/concerns/plain_channel_message.rb +33 -6
  11. data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +1 -1
  12. data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +1 -1
  13. data/app/jobs/rails_error_dashboard/retention_cleanup_job.rb +26 -0
  14. data/app/jobs/rails_error_dashboard/scheduled_digest_job.rb +6 -1
  15. data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +1 -1
  16. data/app/jobs/rails_error_dashboard/telegram_error_notification_job.rb +25 -0
  17. data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +2 -18
  18. data/app/models/rails_error_dashboard/error_log.rb +43 -12
  19. data/app/models/rails_error_dashboard/missing_translation.rb +43 -0
  20. data/app/views/layouts/rails_error_dashboard.html.erb +24 -1
  21. data/app/views/rails_error_dashboard/errors/_error_info.html.erb +3 -1
  22. data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +3 -1
  23. data/app/views/rails_error_dashboard/errors/_source_code.html.erb +3 -9
  24. data/app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb +28 -0
  25. data/app/views/rails_error_dashboard/errors/boot_errors.html.erb +130 -0
  26. data/app/views/rails_error_dashboard/errors/index.html.erb +1 -1
  27. data/app/views/rails_error_dashboard/errors/missing_translations.html.erb +143 -0
  28. data/app/views/rails_error_dashboard/errors/settings/_value_badge.html.erb +7 -4
  29. data/app/views/rails_error_dashboard/errors/settings.html.erb +19 -5
  30. data/config/locales/de.yml +72 -5
  31. data/config/locales/en.yml +83 -4
  32. data/config/locales/es.yml +72 -5
  33. data/config/locales/fr.yml +72 -5
  34. data/config/locales/it.yml +72 -5
  35. data/config/locales/ja.yml +72 -4
  36. data/config/locales/pl.yml +72 -5
  37. data/config/locales/pt-BR.yml +72 -5
  38. data/config/locales/ru.yml +72 -5
  39. data/config/locales/uk.yml +72 -5
  40. data/config/locales/zh-CN.yml +72 -4
  41. data/config/routes.rb +6 -0
  42. data/db/migrate/20251223000000_create_rails_error_dashboard_complete_schema.rb +10 -2
  43. data/db/migrate/20251225074653_remove_environment_from_error_logs.rb +9 -0
  44. data/db/migrate/20261010000001_create_missing_translations.rb +50 -0
  45. data/lib/generators/rails_error_dashboard/install/install_generator.rb +40 -38
  46. data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +31 -3
  47. data/lib/generators/rails_error_dashboard/uninstall/uninstall_generator.rb +11 -2
  48. data/lib/rails_error_dashboard/commands/assign_error.rb +24 -1
  49. data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +9 -1
  50. data/lib/rails_error_dashboard/commands/flush_missing_translations.rb +103 -0
  51. data/lib/rails_error_dashboard/commands/log_error.rb +6 -3
  52. data/lib/rails_error_dashboard/commands/resolve_error.rb +2 -15
  53. data/lib/rails_error_dashboard/commands/update_error_status.rb +7 -0
  54. data/lib/rails_error_dashboard/configuration.rb +75 -2
  55. data/lib/rails_error_dashboard/engine.rb +52 -5
  56. data/lib/rails_error_dashboard/middleware/rate_limiter.rb +21 -43
  57. data/lib/rails_error_dashboard/plugin.rb +49 -3
  58. data/lib/rails_error_dashboard/plugin_registry.rb +61 -12
  59. data/lib/rails_error_dashboard/queries/boot_errors.rb +76 -0
  60. data/lib/rails_error_dashboard/queries/dashboard_stats.rb +3 -11
  61. data/lib/rails_error_dashboard/queries/errors_list.rb +11 -17
  62. data/lib/rails_error_dashboard/queries/health_status.rb +191 -0
  63. data/lib/rails_error_dashboard/queries/missing_translation_summary.rb +82 -0
  64. data/lib/rails_error_dashboard/services/active_storage_probe.rb +91 -0
  65. data/lib/rails_error_dashboard/services/backtrace_parser.rb +13 -7
  66. data/lib/rails_error_dashboard/services/backtrace_processor.rb +15 -0
  67. data/lib/rails_error_dashboard/services/baseline_alert_payload_builder.rb +30 -0
  68. data/lib/rails_error_dashboard/services/crash_capture.rb +85 -2
  69. data/lib/rails_error_dashboard/services/digest_builder.rb +9 -6
  70. data/lib/rails_error_dashboard/services/error_notification_dispatcher.rb +6 -0
  71. data/lib/rails_error_dashboard/services/exception_filter.rb +4 -1
  72. data/lib/rails_error_dashboard/services/github_link_generator.rb +25 -9
  73. data/lib/rails_error_dashboard/services/missing_translation_handler.rb +152 -0
  74. data/lib/rails_error_dashboard/services/missing_translation_tracker.rb +316 -0
  75. data/lib/rails_error_dashboard/services/resolution_callbacks.rb +44 -0
  76. data/lib/rails_error_dashboard/services/severity_classifier.rb +52 -0
  77. data/lib/rails_error_dashboard/services/solid_queue_config_check.rb +47 -0
  78. data/lib/rails_error_dashboard/services/source_code_reader.rb +23 -3
  79. data/lib/rails_error_dashboard/services/telegram_delivery.rb +134 -0
  80. data/lib/rails_error_dashboard/services/telegram_payload_builder.rb +105 -0
  81. data/lib/rails_error_dashboard/services/webhook_delivery.rb +63 -0
  82. data/lib/rails_error_dashboard/services/webhook_signer.rb +80 -0
  83. data/lib/rails_error_dashboard/version.rb +1 -1
  84. data/lib/rails_error_dashboard.rb +12 -0
  85. data/lib/tasks/error_dashboard.rake +43 -2
  86. metadata +21 -2
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+ require "erb"
5
+
6
+ module RailsErrorDashboard
7
+ module Services
8
+ # Pure algorithm: build the Telegram sendMessage payload for an error.
9
+ #
10
+ # No HTTP calls. Returns { text:, parse_mode:, disable_web_page_preview: };
11
+ # TelegramDelivery adds the chat id, which is configuration, not content.
12
+ #
13
+ # WHY HTML AND NOT MARKDOWNV2: MarkdownV2 reserves eighteen characters,
14
+ # every one of which must be escaped in every value, and a single
15
+ # unescaped "_" in a class name like Net::OpenTimeout_Retry fails the
16
+ # whole message with "can't parse entities". HTML mode needs only <, >
17
+ # and & escaped, which ERB::Util.html_escape does. Telegram allows <b>,
18
+ # <i>, <code>, <pre> and <a href>; nothing else is used here.
19
+ #
20
+ # @example
21
+ # TelegramPayloadBuilder.call(error_log)
22
+ # # => { text: "<b>🚨 Error Alert</b>\n...", parse_mode: "HTML", ... }
23
+ class TelegramPayloadBuilder
24
+ # Telegram rejects a text longer than this. The fields below are all
25
+ # bounded (message 500, location 100, the rest short), so a message
26
+ # built here is a few hundred characters; the fallback exists for the
27
+ # one pathological row and sends the same content unformatted rather
28
+ # than losing the notification to a parse error from a cut tag.
29
+ MAX_TEXT_LENGTH = 4096
30
+ MESSAGE_LENGTH = 500
31
+
32
+ # @param error_log [ErrorLog]
33
+ # @param locale [String] resolved by the enqueueing thread (P4-T1)
34
+ # @return [Hash] sendMessage payload without chat_id
35
+ def self.call(error_log, locale: I18nStore::DEFAULT_LOCALE)
36
+ unknown = NotificationHelpers.unknown(locale)
37
+ not_available = NotificationHelpers.not_available(locale)
38
+
39
+ lines = [
40
+ bold(NotificationHelpers.t("red.notifications.error_alert.heading", locale)),
41
+ # error_type is an exception class name -- verbatim, escaped.
42
+ bold(error_log.error_type),
43
+ code(NotificationHelpers.truncate_message(error_log.message, MESSAGE_LENGTH)),
44
+ "",
45
+ field(:application, NotificationHelpers.app_name(error_log), locale),
46
+ *environment_line(error_log, locale),
47
+ field(:platform, error_log.platform || unknown, locale),
48
+ field(:occurrences, error_log.occurrence_count.to_s, locale),
49
+ field(:controller, error_log.controller_name || not_available, locale),
50
+ field(:action, error_log.action_name || not_available, locale),
51
+ field(:first_seen, NotificationHelpers.format_datetime_or_na(error_log.first_seen_at, locale), locale),
52
+ # Backtrace content is diagnostic output -- never translated.
53
+ field(:location, NotificationHelpers.extract_first_backtrace_line(error_log.backtrace, locale: locale), locale, code: true),
54
+ "",
55
+ link(NotificationHelpers.dashboard_url(error_log),
56
+ NotificationHelpers.t("red.notifications.error_alert.view_in_dashboard", locale))
57
+ ]
58
+
59
+ html_payload(lines.join("\n"))
60
+ end
61
+
62
+ # "<b>Label:</b> value". The colon and the bold are Telegram markup, not
63
+ # language, so they stay here rather than in each translation (same
64
+ # reasoning as NotificationHelpers.field for Slack).
65
+ def self.field(label, value, locale, code: false)
66
+ rendered = code ? code(value) : escape(value)
67
+ "#{bold("#{NotificationHelpers.label(label, locale)}:")} #{rendered}"
68
+ end
69
+
70
+ def self.environment_line(error_log, locale)
71
+ return [] unless error_log.respond_to?(:environment) && error_log.environment.present?
72
+
73
+ [ field(:environment, error_log.environment, locale) ]
74
+ end
75
+
76
+ def self.bold(text)
77
+ "<b>#{escape(text)}</b>"
78
+ end
79
+
80
+ def self.code(text)
81
+ "<code>#{escape(text)}</code>"
82
+ end
83
+
84
+ def self.link(url, text)
85
+ %(<a href="#{escape(url)}">#{escape(text)}</a>)
86
+ end
87
+
88
+ # Plain String, not a SafeBuffer: these go into JSON, not a view.
89
+ def self.escape(value)
90
+ ERB::Util.html_escape(value.to_s).to_str
91
+ end
92
+
93
+ # The payload for an already-formatted HTML text, degrading to plain
94
+ # text when it would not fit.
95
+ def self.html_payload(text)
96
+ if text.length > MAX_TEXT_LENGTH
97
+ plain = CGI.unescapeHTML(text.gsub(/<[^>]+>/, ""))
98
+ return { text: plain[0, MAX_TEXT_LENGTH], disable_web_page_preview: true }
99
+ end
100
+
101
+ { text: text, parse_mode: "HTML", disable_web_page_preview: true }
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+
6
+ module RailsErrorDashboard
7
+ module Services
8
+ # One POST to a custom webhook URL (config.webhook_urls), signed when
9
+ # config.webhook_signing_secret is set.
10
+ #
11
+ # WHY ONE PLACE: three jobs post to webhook_urls (per-error notifications,
12
+ # storm/burst-summary messages, baseline alerts) and each carried its own
13
+ # copy of the HTTP code. The signature must be computed over the exact
14
+ # bytes on the wire, so the body is serialised once here and the same
15
+ # string is both signed and sent. Slack, Discord and PagerDuty keep their
16
+ # own senders: their payloads are not ours to sign.
17
+ #
18
+ # Errors propagate. Every caller already rescues around its send and logs
19
+ # in its own words (the specs pin those messages), so swallowing here would
20
+ # only hide the failure from them.
21
+ class WebhookDelivery
22
+ OPEN_TIMEOUT = 5
23
+ READ_TIMEOUT = 10
24
+ USER_AGENT = "RailsErrorDashboard/1.0"
25
+
26
+ class << self
27
+ # @param url [String]
28
+ # @param payload [Hash] serialised with #to_json
29
+ # @param headers [Hash{String => String}] merged over the defaults
30
+ # @return [HTTParty::Response, Net::HTTPResponse]
31
+ def post(url, payload, headers: {})
32
+ body = payload.to_json
33
+ all_headers = { "Content-Type" => "application/json", "User-Agent" => USER_AGENT }
34
+ .merge(headers)
35
+ .merge(signature_headers(body))
36
+
37
+ if defined?(HTTParty)
38
+ HTTParty.post(url, body: body, headers: all_headers, timeout: READ_TIMEOUT)
39
+ else
40
+ uri = URI(url)
41
+ http = Net::HTTP.new(uri.host, uri.port)
42
+ http.use_ssl = uri.scheme == "https"
43
+ http.open_timeout = OPEN_TIMEOUT
44
+ http.read_timeout = READ_TIMEOUT
45
+ # request_uri, not path: a webhook URL that carries a query string
46
+ # (a token, a channel id) used to lose it on this branch.
47
+ request = Net::HTTP::Post.new(uri.request_uri, all_headers)
48
+ request.body = body
49
+ http.request(request)
50
+ end
51
+ end
52
+
53
+ # Signature headers for +body+, or {} when no secret is configured.
54
+ def signature_headers(body)
55
+ secret = RailsErrorDashboard.configuration.webhook_signing_secret
56
+ return {} if secret.blank?
57
+
58
+ WebhookSigner.headers_for(body, secret: secret)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+
5
+ module RailsErrorDashboard
6
+ module Services
7
+ # HMAC-SHA256 signatures for OUTBOUND webhooks (the ones RED sends to
8
+ # config.webhook_urls). Inbound verification for GitHub/GitLab/Codeberg/
9
+ # Linear lives in WebhooksController and is unrelated.
10
+ #
11
+ # Scheme, chosen to match what Stripe, Svix and GitHub receivers already
12
+ # know how to check:
13
+ #
14
+ # X-Error-Dashboard-Timestamp: 1718000000 (Unix seconds)
15
+ # X-Error-Dashboard-Signature-256: sha256=<hex>
16
+ #
17
+ # hex = HMAC_SHA256(secret, "#{timestamp}.#{raw_body}")
18
+ #
19
+ # The timestamp is part of the signed string, not just a header, so a
20
+ # captured request cannot be replayed later with a fresh timestamp. The
21
+ # receiver verifies the digest over the RAW body it received (before
22
+ # parsing JSON) and rejects timestamps outside its tolerance window.
23
+ #
24
+ # Pure functions, no I/O, nothing here can raise on well-typed input.
25
+ class WebhookSigner
26
+ TIMESTAMP_HEADER = "X-Error-Dashboard-Timestamp"
27
+ SIGNATURE_HEADER = "X-Error-Dashboard-Signature-256"
28
+ PREFIX = "sha256="
29
+
30
+ # Default replay window for verifiers, in seconds. Five minutes is the
31
+ # value Stripe and Svix document; it absorbs clock skew without leaving
32
+ # a captured request useful for long.
33
+ DEFAULT_TOLERANCE = 300
34
+
35
+ class << self
36
+ # Headers to merge into the outbound request.
37
+ #
38
+ # @param body [String] the exact bytes being sent
39
+ # @param secret [String]
40
+ # @param timestamp [Integer] Unix seconds; defaults to now
41
+ # @return [Hash{String => String}]
42
+ def headers_for(body, secret:, timestamp: Time.now.to_i)
43
+ ts = timestamp.to_i
44
+ {
45
+ TIMESTAMP_HEADER => ts.to_s,
46
+ SIGNATURE_HEADER => PREFIX + digest(body, secret: secret, timestamp: ts)
47
+ }
48
+ end
49
+
50
+ # @return [String] lowercase hex HMAC over "#{timestamp}.#{body}"
51
+ def digest(body, secret:, timestamp:)
52
+ OpenSSL::HMAC.hexdigest("SHA256", secret.to_s, "#{timestamp.to_i}.#{body}")
53
+ end
54
+
55
+ # Receiver-side check, offered so a Ruby receiver (and this gem's own
56
+ # specs and docs) need not re-derive the scheme.
57
+ #
58
+ # @param body [String] raw request body
59
+ # @param timestamp [String, Integer] value of X-Error-Dashboard-Timestamp
60
+ # @param signature [String] value of X-Error-Dashboard-Signature-256
61
+ # @param secret [String]
62
+ # @param tolerance [Integer, nil] max age in seconds; nil skips the window check
63
+ # @param now [Integer] injectable clock for tests
64
+ # @return [Boolean]
65
+ def valid?(body, timestamp:, signature:, secret:, tolerance: DEFAULT_TOLERANCE, now: Time.now.to_i)
66
+ return false if secret.to_s.empty? || signature.to_s.empty?
67
+
68
+ ts = Integer(timestamp.to_s, 10)
69
+ return false if tolerance && (now - ts).abs > tolerance
70
+
71
+ expected = PREFIX + digest(body, secret: secret, timestamp: ts)
72
+ ActiveSupport::SecurityUtils.secure_compare(expected, signature.to_s)
73
+ rescue ArgumentError, TypeError
74
+ # A timestamp that is not an integer is a bad signature, not an error.
75
+ false
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsErrorDashboard
2
- VERSION = "0.14.3"
2
+ VERSION = "0.15.0"
3
3
  end
@@ -44,6 +44,7 @@ require "rails_error_dashboard/services/localized_time_formatter"
44
44
  require "rails_error_dashboard/services/notification_helpers"
45
45
  require "rails_error_dashboard/services/slack_payload_builder"
46
46
  require "rails_error_dashboard/services/discord_payload_builder"
47
+ require "rails_error_dashboard/services/telegram_payload_builder"
47
48
  require "rails_error_dashboard/services/pagerduty_payload_builder"
48
49
  require "rails_error_dashboard/services/webhook_payload_builder"
49
50
  require "rails_error_dashboard/services/baseline_alert_payload_builder"
@@ -88,6 +89,13 @@ require "rails_error_dashboard/services/variable_serializer"
88
89
  require "rails_error_dashboard/services/local_variable_capturer"
89
90
  require "rails_error_dashboard/services/swallowed_exception_tracker"
90
91
  require "rails_error_dashboard/services/rack_attack_tracker"
92
+ require "rails_error_dashboard/services/webhook_signer"
93
+ require "rails_error_dashboard/services/webhook_delivery"
94
+ require "rails_error_dashboard/services/telegram_delivery"
95
+ require "rails_error_dashboard/services/resolution_callbacks"
96
+ require "rails_error_dashboard/services/missing_translation_tracker"
97
+ require "rails_error_dashboard/services/missing_translation_handler"
98
+ require "rails_error_dashboard/services/active_storage_probe"
91
99
  require "rails_error_dashboard/services/ai_agent_classifier"
92
100
  require "rails_error_dashboard/services/crash_capture"
93
101
  require "rails_error_dashboard/services/diagnostic_dump_generator"
@@ -136,6 +144,7 @@ require "rails_error_dashboard/commands/upsert_cascade_pattern"
136
144
  require "rails_error_dashboard/commands/upsert_baseline"
137
145
  require "rails_error_dashboard/commands/flush_swallowed_exceptions"
138
146
  require "rails_error_dashboard/commands/flush_rack_attack_events"
147
+ require "rails_error_dashboard/commands/flush_missing_translations"
139
148
  require "rails_error_dashboard/commands/scrub_invalid_encoding"
140
149
  require "rails_error_dashboard/commands/backfill_resolved_at"
141
150
  require "rails_error_dashboard/queries/uninstall_plan"
@@ -158,6 +167,9 @@ require "rails_error_dashboard/queries/job_health_summary"
158
167
  require "rails_error_dashboard/queries/database_health_summary"
159
168
  require "rails_error_dashboard/queries/swallowed_exception_summary"
160
169
  require "rails_error_dashboard/queries/rack_attack_summary"
170
+ require "rails_error_dashboard/queries/health_status"
171
+ require "rails_error_dashboard/queries/missing_translation_summary"
172
+ require "rails_error_dashboard/queries/boot_errors"
161
173
  require "rails_error_dashboard/queries/llm_health_summary"
162
174
  require "rails_error_dashboard/queries/release_timeline"
163
175
  require "rails_error_dashboard/error_reporter"
@@ -34,6 +34,25 @@ namespace :error_dashboard do
34
34
  end
35
35
  checks_passed += 1
36
36
 
37
+ # 2b. Separate database: the database.yml entry for this environment. Without
38
+ # it the engine skips connects_to and RED uses the main database, which
39
+ # usually has no RED tables, so nothing is recorded.
40
+ if config.use_separate_database
41
+ print " Error database entry... "
42
+ db_name = config.database || :error_dashboard
43
+ if config.separate_database_entry?
44
+ puts "OK"
45
+ checks_passed += 1
46
+ else
47
+ puts "FAILED"
48
+ puts " config/database.yml has no '#{db_name}' entry for the '#{Rails.env}' environment."
49
+ puts " RED is using the main database instead, so errors are not recorded in the error database."
50
+ puts " Add '#{db_name}:' under '#{Rails.env}:' with migrations_paths: db/error_dashboard_migrate"
51
+ puts " (see docs/guides/DATABASE_OPTIONS.md), then restart."
52
+ checks_failed += 1
53
+ end
54
+ end
55
+
37
56
  # 3. Database connection
38
57
  print " Database connection... "
39
58
  begin
@@ -218,6 +237,28 @@ namespace :error_dashboard do
218
237
  end
219
238
  end
220
239
 
240
+ # 10. json 3.0 breaks Rails before 8.1.4 and 7.2.4: dashboard pages return
241
+ # 500 and captures can fail, with no error message. The bug is in Rails.
242
+ json_version = Gem.loaded_specs["json"]&.version
243
+ if json_version && json_version >= Gem::Version.new("3")
244
+ print " json gem... "
245
+ rails_version = Gem::Version.new(Rails.version)
246
+ series = rails_version.segments.first(2)
247
+ json3_ok = (series == [ 8, 1 ] && rails_version >= Gem::Version.new("8.1.4")) ||
248
+ (series == [ 7, 2 ] && rails_version >= Gem::Version.new("7.2.4")) ||
249
+ (series <=> [ 8, 1 ]) == 1
250
+ if json3_ok
251
+ puts "OK (json #{json_version}, Rails #{Rails.version})"
252
+ checks_passed += 1
253
+ else
254
+ puts "WARNING"
255
+ puts " json #{json_version} breaks Rails #{Rails.version} (fixed in Rails 8.1.4 and 7.2.4):"
256
+ puts " dashboard pages return 500 and errors can fail to be captured."
257
+ puts " Add gem \"json\", \"< 3\" to your Gemfile and run bundle update json, or upgrade Rails."
258
+ warnings += 1
259
+ end
260
+ end
261
+
221
262
  # Summary
222
263
  puts "\n" + "-" * 70
223
264
  puts " Results: #{checks_passed} passed, #{checks_failed} failed, #{warnings} warnings"
@@ -324,7 +365,7 @@ namespace :error_dashboard do
324
365
 
325
366
  # Confirm before proceeding
326
367
  print "\nProceed with backfill? (y/N): "
327
- confirmation = $stdin.gets.chomp.downcase
368
+ confirmation = $stdin.gets.to_s.chomp.downcase
328
369
 
329
370
  unless confirmation == "y" || confirmation == "yes"
330
371
  puts "\n✗ Backfill cancelled"
@@ -569,7 +610,7 @@ namespace :error_dashboard do
569
610
  end
570
611
 
571
612
  print "\n Proceed with deletion? (y/N): "
572
- confirmation = $stdin.gets.chomp.downcase
613
+ confirmation = $stdin.gets.to_s.chomp.downcase
573
614
 
574
615
  unless confirmation == "y" || confirmation == "yes"
575
616
  puts "\n Cleanup cancelled"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_error_dashboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.3
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anjan Jagirdar
@@ -268,6 +268,7 @@ files:
268
268
  - Rakefile
269
269
  - app/controllers/rails_error_dashboard/application_controller.rb
270
270
  - app/controllers/rails_error_dashboard/errors_controller.rb
271
+ - app/controllers/rails_error_dashboard/health_controller.rb
271
272
  - app/controllers/rails_error_dashboard/locales_controller.rb
272
273
  - app/controllers/rails_error_dashboard/webhooks_controller.rb
273
274
  - app/helpers/rails_error_dashboard/application_helper.rb
@@ -281,6 +282,7 @@ files:
281
282
  - app/jobs/rails_error_dashboard/async_error_logging_job.rb
282
283
  - app/jobs/rails_error_dashboard/baseline_alert_job.rb
283
284
  - app/jobs/rails_error_dashboard/baseline_calculation_job.rb
285
+ - app/jobs/rails_error_dashboard/cascade_detection_job.rb
284
286
  - app/jobs/rails_error_dashboard/close_linked_issue_job.rb
285
287
  - app/jobs/rails_error_dashboard/concerns/localized_job.rb
286
288
  - app/jobs/rails_error_dashboard/concerns/plain_channel_message.rb
@@ -297,6 +299,7 @@ files:
297
299
  - app/jobs/rails_error_dashboard/storm_flush_job.rb
298
300
  - app/jobs/rails_error_dashboard/storm_notification_job.rb
299
301
  - app/jobs/rails_error_dashboard/swallowed_exception_flush_job.rb
302
+ - app/jobs/rails_error_dashboard/telegram_error_notification_job.rb
300
303
  - app/jobs/rails_error_dashboard/webhook_error_notification_job.rb
301
304
  - app/mailers/rails_error_dashboard/application_mailer.rb
302
305
  - app/mailers/rails_error_dashboard/digest_mailer.rb
@@ -311,6 +314,7 @@ files:
311
314
  - app/models/rails_error_dashboard/error_occurrence.rb
312
315
  - app/models/rails_error_dashboard/event_count.rb
313
316
  - app/models/rails_error_dashboard/event_timing_gap.rb
317
+ - app/models/rails_error_dashboard/missing_translation.rb
314
318
  - app/models/rails_error_dashboard/rack_attack_event.rb
315
319
  - app/models/rails_error_dashboard/storm_event.rb
316
320
  - app/models/rails_error_dashboard/storm_flush_batch.rb
@@ -344,6 +348,7 @@ files:
344
348
  - app/views/rails_error_dashboard/errors/actioncable_health_summary.html.erb
345
349
  - app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb
346
350
  - app/views/rails_error_dashboard/errors/analytics.html.erb
351
+ - app/views/rails_error_dashboard/errors/boot_errors.html.erb
347
352
  - app/views/rails_error_dashboard/errors/cache_health_summary.html.erb
348
353
  - app/views/rails_error_dashboard/errors/correlation.html.erb
349
354
  - app/views/rails_error_dashboard/errors/database_health_summary.html.erb
@@ -352,6 +357,7 @@ files:
352
357
  - app/views/rails_error_dashboard/errors/index.html.erb
353
358
  - app/views/rails_error_dashboard/errors/job_health_summary.html.erb
354
359
  - app/views/rails_error_dashboard/errors/llm_health_summary.html.erb
360
+ - app/views/rails_error_dashboard/errors/missing_translations.html.erb
355
361
  - app/views/rails_error_dashboard/errors/n_plus_one_summary.html.erb
356
362
  - app/views/rails_error_dashboard/errors/overview.html.erb
357
363
  - app/views/rails_error_dashboard/errors/platform_comparison.html.erb
@@ -425,6 +431,7 @@ files:
425
431
  - db/migrate/20260919000001_create_event_counts.rb
426
432
  - db/migrate/20260920000001_add_buckets_incomplete_to_storm_events.rb
427
433
  - db/migrate/20260920000002_create_event_timing_gaps.rb
434
+ - db/migrate/20261010000001_create_missing_translations.rb
428
435
  - lib/generators/rails_error_dashboard/install/install_generator.rb
429
436
  - lib/generators/rails_error_dashboard/install/templates/README
430
437
  - lib/generators/rails_error_dashboard/install/templates/initializer.rb
@@ -444,6 +451,7 @@ files:
444
451
  - lib/rails_error_dashboard/commands/drop_all_tables.rb
445
452
  - lib/rails_error_dashboard/commands/find_or_create_application.rb
446
453
  - lib/rails_error_dashboard/commands/find_or_increment_error.rb
454
+ - lib/rails_error_dashboard/commands/flush_missing_translations.rb
447
455
  - lib/rails_error_dashboard/commands/flush_rack_attack_events.rb
448
456
  - lib/rails_error_dashboard/commands/flush_storm_counts.rb
449
457
  - lib/rails_error_dashboard/commands/flush_swallowed_exceptions.rb
@@ -486,6 +494,7 @@ files:
486
494
  - lib/rails_error_dashboard/queries/active_storage_summary.rb
487
495
  - lib/rails_error_dashboard/queries/analytics_stats.rb
488
496
  - lib/rails_error_dashboard/queries/baseline_stats.rb
497
+ - lib/rails_error_dashboard/queries/boot_errors.rb
489
498
  - lib/rails_error_dashboard/queries/cache_health_summary.rb
490
499
  - lib/rails_error_dashboard/queries/co_occurring_errors.rb
491
500
  - lib/rails_error_dashboard/queries/critical_alerts.rb
@@ -497,8 +506,10 @@ files:
497
506
  - lib/rails_error_dashboard/queries/errors_list.rb
498
507
  - lib/rails_error_dashboard/queries/event_volume.rb
499
508
  - lib/rails_error_dashboard/queries/filter_options.rb
509
+ - lib/rails_error_dashboard/queries/health_status.rb
500
510
  - lib/rails_error_dashboard/queries/job_health_summary.rb
501
511
  - lib/rails_error_dashboard/queries/llm_health_summary.rb
512
+ - lib/rails_error_dashboard/queries/missing_translation_summary.rb
502
513
  - lib/rails_error_dashboard/queries/mttr_stats.rb
503
514
  - lib/rails_error_dashboard/queries/n_plus_one_summary.rb
504
515
  - lib/rails_error_dashboard/queries/platform_comparison.rb
@@ -510,6 +521,7 @@ files:
510
521
  - lib/rails_error_dashboard/queries/swallowed_exception_summary.rb
511
522
  - lib/rails_error_dashboard/queries/uninstall_plan.rb
512
523
  - lib/rails_error_dashboard/queries/user_impact_summary.rb
524
+ - lib/rails_error_dashboard/services/active_storage_probe.rb
513
525
  - lib/rails_error_dashboard/services/ai_agent_classifier.rb
514
526
  - lib/rails_error_dashboard/services/analytics_cache_manager.rb
515
527
  - lib/rails_error_dashboard/services/backtrace_parser.rb
@@ -550,6 +562,8 @@ files:
550
562
  - lib/rails_error_dashboard/services/local_variable_capturer.rb
551
563
  - lib/rails_error_dashboard/services/localized_time_formatter.rb
552
564
  - lib/rails_error_dashboard/services/markdown_error_formatter.rb
565
+ - lib/rails_error_dashboard/services/missing_translation_handler.rb
566
+ - lib/rails_error_dashboard/services/missing_translation_tracker.rb
553
567
  - lib/rails_error_dashboard/services/n_plus_one_detector.rb
554
568
  - lib/rails_error_dashboard/services/notification_helpers.rb
555
569
  - lib/rails_error_dashboard/services/notification_throttler.rb
@@ -559,6 +573,7 @@ files:
559
573
  - lib/rails_error_dashboard/services/platform_detector.rb
560
574
  - lib/rails_error_dashboard/services/priority_score_calculator.rb
561
575
  - lib/rails_error_dashboard/services/rack_attack_tracker.rb
576
+ - lib/rails_error_dashboard/services/resolution_callbacks.rb
562
577
  - lib/rails_error_dashboard/services/rspec_generator.rb
563
578
  - lib/rails_error_dashboard/services/sensitive_data_filter.rb
564
579
  - lib/rails_error_dashboard/services/severity_classifier.rb
@@ -573,9 +588,13 @@ files:
573
588
  - lib/rails_error_dashboard/services/storm_protection/gate.rb
574
589
  - lib/rails_error_dashboard/services/swallowed_exception_tracker.rb
575
590
  - lib/rails_error_dashboard/services/system_health_snapshot.rb
591
+ - lib/rails_error_dashboard/services/telegram_delivery.rb
592
+ - lib/rails_error_dashboard/services/telegram_payload_builder.rb
576
593
  - lib/rails_error_dashboard/services/url_safety.rb
577
594
  - lib/rails_error_dashboard/services/variable_serializer.rb
595
+ - lib/rails_error_dashboard/services/webhook_delivery.rb
578
596
  - lib/rails_error_dashboard/services/webhook_payload_builder.rb
597
+ - lib/rails_error_dashboard/services/webhook_signer.rb
579
598
  - lib/rails_error_dashboard/subscribers/action_cable_subscriber.rb
580
599
  - lib/rails_error_dashboard/subscribers/active_storage_subscriber.rb
581
600
  - lib/rails_error_dashboard/subscribers/breadcrumb_subscriber.rb
@@ -601,7 +620,7 @@ metadata:
601
620
  funding_uri: https://github.com/sponsors/AnjanJ
602
621
  post_install_message: |
603
622
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
604
- RED (Rails Error Dashboard) v0.14.3
623
+ RED (Rails Error Dashboard) v0.15.0
605
624
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
606
625
 
607
626
  First install: