rails_error_dashboard 0.1.14 → 0.1.16

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1f6863e7b28220e7b384b6a2f27d88e2f996187fdff0947683eda7cbf28055d
4
- data.tar.gz: 00f620f673e8b900c242e3dce814c1068619c2ad5345e8cf37cf22b2753d65ee
3
+ metadata.gz: 2bb87db0ac566c74592758f0bb11c4e16f3caff7464d7c428436083adc13edb5
4
+ data.tar.gz: ccead510cbc1f57d1197aafa34e4c43f3f74a59f7b4d56e53ea0b8ebb4fa73e2
5
5
  SHA512:
6
- metadata.gz: bc9f07b4ff7765d9446f2e323da276abd1e5e1c35c4ed2c68af813a6ccf853d70fc0c4109ae41609913f554f6633cd89a887065c8f7adcd4a843116440e697c9
7
- data.tar.gz: 2defe3dbcf36a20b83e1faf1da908fe4a731072ea981dc6bee568ecf8bfde45ec0eaa46aa1ae77726e7bb5ae34b96bdd429b515280e54beeea581804120d4855
6
+ metadata.gz: 330ec515a3f833517c66926f1c77a3bd9c6cf7db23a5d9e63d2ae796c1fb2fd672dba56839cb7e70cd055e8d6ec46ff8d0974f56af4580463d0bcfa6f3c5b9c5
7
+ data.tar.gz: f19ea45f7eadce30679c0cf0e0e8f117f603fa2d06158783477ad2a6e0451e91a09cb6913eb6ec2a4798ea57d655622d496adce5a4f8cf378a74304ae92cf19c
@@ -2,6 +2,12 @@ module RailsErrorDashboard
2
2
  class ApplicationController < ActionController::Base
3
3
  include Pagy::Backend
4
4
 
5
+ # Enable features that are disabled in API-only mode
6
+ # These are ONLY enabled for Error Dashboard routes, not the entire app
7
+ include ActionController::Cookies
8
+ include ActionController::Flash
9
+ include ActionController::RequestForgeryProtection
10
+
5
11
  layout "rails_error_dashboard"
6
12
 
7
13
  protect_from_forgery with: :exception
@@ -3,8 +3,12 @@
3
3
  <head>
4
4
  <title><%= content_for?(:page_title) ? "#{content_for(:page_title)} | " : "" %><%= Rails.application.class.module_parent_name %> - Error Dashboard</title>
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
- <%= csrf_meta_tags %>
7
- <%= csp_meta_tag %>
6
+ <% if respond_to?(:csrf_meta_tags) %>
7
+ <%= csrf_meta_tags %>
8
+ <% end %>
9
+ <% if respond_to?(:csp_meta_tag) %>
10
+ <%= csp_meta_tag %>
11
+ <% end %>
8
12
 
9
13
  <!-- Bootstrap CSS -->
10
14
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
@@ -783,12 +787,30 @@
783
787
  <button class="btn btn-link text-white d-md-none me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
784
788
  <i class="bi bi-list fs-4"></i>
785
789
  </button>
786
- <a class="navbar-brand" href="<%= main_app.root_path %>" title="Back to <%= Rails.application.class.module_parent_name %>">
787
- <i class="bi bi-bug-fill"></i>
788
- <span class="d-none d-sm-inline"><%= Rails.application.class.module_parent_name %></span>
789
- <span class="d-none d-md-inline text-white-50 mx-2">|</span>
790
- <span class="d-none d-md-inline">Error Dashboard</span>
791
- </a>
790
+ <%
791
+ # Check if main app has a root route defined
792
+ begin
793
+ root_url = main_app.root_path
794
+ has_root = true
795
+ rescue NoMethodError
796
+ has_root = false
797
+ end
798
+ %>
799
+ <% if has_root %>
800
+ <a class="navbar-brand" href="<%= root_url %>" title="Back to <%= Rails.application.class.module_parent_name %>">
801
+ <i class="bi bi-bug-fill"></i>
802
+ <span class="d-none d-sm-inline"><%= Rails.application.class.module_parent_name %></span>
803
+ <span class="d-none d-md-inline text-white-50 mx-2">|</span>
804
+ <span class="d-none d-md-inline">Error Dashboard</span>
805
+ </a>
806
+ <% else %>
807
+ <span class="navbar-brand">
808
+ <i class="bi bi-bug-fill"></i>
809
+ <span class="d-none d-sm-inline"><%= Rails.application.class.module_parent_name %></span>
810
+ <span class="d-none d-md-inline text-white-50 mx-2">|</span>
811
+ <span class="d-none d-md-inline">Error Dashboard</span>
812
+ </span>
813
+ <% end %>
792
814
  </div>
793
815
  <div class="d-flex align-items-center gap-3">
794
816
  <button class="theme-toggle" id="themeToggle">
@@ -867,6 +889,43 @@
867
889
  </div>
868
890
  </div>
869
891
 
892
+ <!-- Keyboard Shortcuts Modal -->
893
+ <div class="modal fade" id="keyboardShortcutsModal" tabindex="-1" aria-labelledby="keyboardShortcutsModalLabel" aria-hidden="true">
894
+ <div class="modal-dialog modal-dialog-centered">
895
+ <div class="modal-content">
896
+ <div class="modal-header">
897
+ <h5 class="modal-title" id="keyboardShortcutsModalLabel">
898
+ <i class="bi bi-keyboard"></i> Keyboard Shortcuts
899
+ </h5>
900
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
901
+ </div>
902
+ <div class="modal-body">
903
+ <div class="list-group list-group-flush">
904
+ <div class="list-group-item d-flex justify-content-between align-items-center">
905
+ <span><i class="bi bi-arrow-clockwise text-primary"></i> Refresh page</span>
906
+ <kbd class="bg-secondary text-white px-2 py-1 rounded">R</kbd>
907
+ </div>
908
+ <div class="list-group-item d-flex justify-content-between align-items-center">
909
+ <span><i class="bi bi-search text-primary"></i> Focus search</span>
910
+ <kbd class="bg-secondary text-white px-2 py-1 rounded">/</kbd>
911
+ </div>
912
+ <div class="list-group-item d-flex justify-content-between align-items-center">
913
+ <span><i class="bi bi-graph-up text-primary"></i> Go to analytics</span>
914
+ <kbd class="bg-secondary text-white px-2 py-1 rounded">A</kbd>
915
+ </div>
916
+ <div class="list-group-item d-flex justify-content-between align-items-center">
917
+ <span><i class="bi bi-question-circle text-primary"></i> Show this help</span>
918
+ <kbd class="bg-secondary text-white px-2 py-1 rounded">?</kbd>
919
+ </div>
920
+ </div>
921
+ </div>
922
+ <div class="modal-footer">
923
+ <small class="text-muted">Press <kbd class="bg-secondary text-white px-2 py-1 rounded">?</kbd> anytime to show shortcuts</small>
924
+ </div>
925
+ </div>
926
+ </div>
927
+ </div>
928
+
870
929
  <!-- Bootstrap JS -->
871
930
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
872
931
 
@@ -1115,17 +1174,19 @@
1115
1174
  };
1116
1175
 
1117
1176
  // Show flash messages as toasts
1118
- <% if flash[:notice] %>
1119
- showToast('<%= j flash[:notice] %>', 'success');
1120
- <% end %>
1121
- <% if flash[:alert] %>
1122
- showToast('<%= j flash[:alert] %>', 'danger');
1123
- <% end %>
1124
- <% if flash[:success] %>
1125
- showToast('<%= j flash[:success] %>', 'success');
1126
- <% end %>
1127
- <% if flash[:error] %>
1128
- showToast('<%= j flash[:error] %>', 'danger');
1177
+ <% if defined?(flash) && flash.present? %>
1178
+ <% if flash[:notice] %>
1179
+ showToast('<%= j flash[:notice] %>', 'success');
1180
+ <% end %>
1181
+ <% if flash[:alert] %>
1182
+ showToast('<%= j flash[:alert] %>', 'danger');
1183
+ <% end %>
1184
+ <% if flash[:success] %>
1185
+ showToast('<%= j flash[:success] %>', 'success');
1186
+ <% end %>
1187
+ <% if flash[:error] %>
1188
+ showToast('<%= j flash[:error] %>', 'danger');
1189
+ <% end %>
1129
1190
  <% end %>
1130
1191
  });
1131
1192
  </script>
@@ -19,6 +19,9 @@
19
19
  </td>
20
20
  <td onclick="window.location='<%= error_path(error) %>';">
21
21
  <code class="text-danger" data-bs-toggle="tooltip" title="<%= error.error_type %>"><%= error.error_type.split('::').last %></code>
22
+ <% if error.recent? %>
23
+ <span class="badge bg-success ms-1" data-bs-toggle="tooltip" title="Error occurred within the last hour">NEW</span>
24
+ <% end %>
22
25
  <% if error.respond_to?(:app_version) && error.app_version.present? %>
23
26
  <br><small class="badge bg-light text-dark" data-bs-toggle="tooltip" title="App version when error occurred">v<%= error.app_version %></small>
24
27
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <%# Timeline view for related errors leading up to this error %>
2
2
  <% if @related_errors.any? %>
3
- <div class="card">
3
+ <div class="card" id="timeline">
4
4
  <div class="card-header bg-white">
5
5
  <h5 class="mb-0">
6
6
  <i class="bi bi-clock-history"></i> Timeline
@@ -131,14 +131,29 @@
131
131
  <script>
132
132
  document.addEventListener('DOMContentLoaded', function() {
133
133
  const colors = window.getChartColors();
134
+ // Distinct, accessible colors for 5 platforms with good contrast
135
+ const platformColors = [
136
+ "#2563EB", // Blue - API
137
+ "#10B981", // Green - Android
138
+ "#F59E0B", // Amber - Background Jobs
139
+ "#8B5CF6", // Purple - Web
140
+ "#EF4444" // Red - iOS
141
+ ];
142
+
134
143
  new Chartkick.PieChart("errors-by-platform-chart", <%= raw @errors_by_platform.to_json %>, {
135
- colors: ["#000000", "#3DDC84", "#3B82F6"],
144
+ colors: platformColors,
136
145
  height: "300px",
137
146
  legend: "bottom",
138
147
  donut: true,
139
148
  library: {
140
149
  plugins: {
141
- legend: { labels: { color: colors.textColor } }
150
+ legend: {
151
+ labels: {
152
+ color: colors.textColor,
153
+ font: { size: 12, weight: 'bold' }
154
+ }
155
+ }
156
+ // Tooltip uses global theme-aware defaults from layout
142
157
  }
143
158
  }
144
159
  });
@@ -530,11 +530,8 @@
530
530
  // '?' - Show keyboard shortcuts help
531
531
  if (e.key === '?') {
532
532
  e.preventDefault();
533
- alert('Keyboard Shortcuts:\n\n' +
534
- 'r - Refresh page\n' +
535
- '/ - Focus search\n' +
536
- 'a - Analytics page\n' +
537
- '? - Show this help');
533
+ const modal = new bootstrap.Modal(document.getElementById('keyboardShortcutsModal'));
534
+ modal.show();
538
535
  }
539
536
  });
540
537
 
@@ -570,4 +567,12 @@
570
567
  });
571
568
  }
572
569
  });
570
+
571
+ // Update browser tab title with unresolved error count
572
+ document.addEventListener('DOMContentLoaded', function() {
573
+ const unresolvedCount = <%= @stats[:unresolved] || 0 %>;
574
+ if (unresolvedCount > 0) {
575
+ document.title = `(${unresolvedCount}) ${document.title}`;
576
+ }
577
+ });
573
578
  </script>
@@ -291,6 +291,19 @@
291
291
  </div>
292
292
 
293
293
  <script>
294
+ // Platform color mapping - consistent with analytics page
295
+ const platformColorMap = {
296
+ 'api': '#2563EB', // Blue
297
+ 'android': '#10B981', // Green
298
+ 'background_jobs': '#F59E0B', // Amber
299
+ 'web': '#8B5CF6', // Purple
300
+ 'ios': '#EF4444' // Red
301
+ };
302
+
303
+ function getPlatformColor(platform) {
304
+ return platformColorMap[platform.toLowerCase()] || '#6B7280'; // Gray fallback
305
+ }
306
+
294
307
  // Error Rate Chart
295
308
  const errorRateCtx = document.getElementById('errorRateChart');
296
309
  if (errorRateCtx) {
@@ -301,16 +314,8 @@
301
314
  datasets: [{
302
315
  label: 'Total Errors',
303
316
  data: <%= raw @error_rate_by_platform.values.to_json %>,
304
- backgroundColor: (function() {
305
- const isDark = document.body.classList.contains('dark-mode');
306
- const config = getCatppuccinChartConfig(isDark);
307
- return config.colors.blue.replace('rgb', 'rgba').replace(')', ', 0.5)');
308
- })(),
309
- borderColor: (function() {
310
- const isDark = document.body.classList.contains('dark-mode');
311
- const config = getCatppuccinChartConfig(isDark);
312
- return config.colors.blue;
313
- })(),
317
+ backgroundColor: 'rgba(37, 99, 235, 0.5)', // Blue with transparency
318
+ borderColor: '#2563EB', // Blue
314
319
  borderWidth: 1
315
320
  }]
316
321
  },
@@ -330,9 +335,6 @@
330
335
  // Daily Trend Chart
331
336
  const dailyTrendCtx = document.getElementById('dailyTrendChart');
332
337
  if (dailyTrendCtx) {
333
- // Use Catppuccin colors from global theme config
334
- const isDark = document.body.classList.contains('dark-mode');
335
-
336
338
  const datasets = <%= raw @daily_trends.map { |platform, data|
337
339
  {
338
340
  label: platform.to_s.capitalize,
@@ -342,12 +344,12 @@
342
344
  }
343
345
  }.to_json %>;
344
346
 
345
- // Apply Catppuccin platform colors
347
+ // Apply platform-specific colors
346
348
  datasets.forEach(ds => {
347
- const color = getPlatformColor(ds.platform, isDark);
349
+ const color = getPlatformColor(ds.platform);
348
350
  ds.borderColor = color;
349
351
  // Add transparency for background
350
- ds.backgroundColor = color.replace('rgb', 'rgba').replace(')', ', 0.1)');
352
+ ds.backgroundColor = color + '1A'; // Add 10% alpha in hex
351
353
  });
352
354
 
353
355
  new Chart(dailyTrendCtx, {
@@ -382,16 +384,8 @@
382
384
  datasets: [{
383
385
  label: 'Hours to Resolve',
384
386
  data: times,
385
- backgroundColor: (function() {
386
- const isDark = document.body.classList.contains('dark-mode');
387
- const config = getCatppuccinChartConfig(isDark);
388
- return config.colors.orange.replace('rgb', 'rgba').replace(')', ', 0.5)');
389
- })(),
390
- borderColor: (function() {
391
- const isDark = document.body.classList.contains('dark-mode');
392
- const config = getCatppuccinChartConfig(isDark);
393
- return config.colors.orange;
394
- })(),
387
+ backgroundColor: 'rgba(245, 158, 11, 0.5)', // Amber with transparency
388
+ borderColor: '#F59E0B', // Amber
395
389
  borderWidth: 1
396
390
  }]
397
391
  },
@@ -26,7 +26,10 @@
26
26
  <% end %>
27
27
  </h2>
28
28
  </div>
29
- <div>
29
+ <div class="d-flex gap-2">
30
+ <button type="button" class="btn btn-outline-secondary" onclick="downloadErrorJSON()" title="Download error details as JSON">
31
+ <i class="bi bi-download"></i> Export JSON
32
+ </button>
30
33
  <% if @error.resolved? %>
31
34
  <span class="badge bg-success fs-6">
32
35
  <i class="bi bi-check-circle"></i> Resolved
@@ -39,6 +42,60 @@
39
42
  </div>
40
43
  </div>
41
44
 
45
+ <script>
46
+ function downloadErrorJSON() {
47
+ const errorData = {
48
+ id: <%= @error.id %>,
49
+ error_type: <%= raw @error.error_type.to_json %>,
50
+ message: <%= raw @error.message.to_json %>,
51
+ backtrace: <%= raw @error.backtrace.to_json %>,
52
+ occurred_at: <%= raw @error.occurred_at.to_json %>,
53
+ first_seen_at: <%= raw @error.first_seen_at.to_json %>,
54
+ last_seen_at: <%= raw @error.last_seen_at.to_json %>,
55
+ occurrence_count: <%= @error.occurrence_count %>,
56
+ resolved: <%= @error.resolved? %>,
57
+ resolved_at: <%= raw @error.resolved_at.to_json %>,
58
+ resolved_by_name: <%= raw @error.resolved_by_name.to_json %>,
59
+ platform: <%= raw @error.platform.to_json %>,
60
+ user_id: <%= raw @error.user_id.to_json %>,
61
+ severity: <%= raw @error.severity.to_json %>,
62
+ priority_level: <%= @error.priority_level || 0 %>,
63
+ <% if @error.respond_to?(:app_version) %>
64
+ app_version: <%= raw @error.app_version.to_json %>,
65
+ <% end %>
66
+ <% if @error.respond_to?(:git_commit) %>
67
+ git_commit: <%= raw @error.git_commit.to_json %>,
68
+ <% end %>
69
+ created_at: <%= raw @error.created_at.to_json %>,
70
+ updated_at: <%= raw @error.updated_at.to_json %>
71
+ };
72
+
73
+ const jsonString = JSON.stringify(errorData, null, 2);
74
+ const blob = new Blob([jsonString], { type: 'application/json' });
75
+ const url = URL.createObjectURL(blob);
76
+ const link = document.createElement('a');
77
+ link.href = url;
78
+ link.download = `error_${errorData.id}_${errorData.error_type.replace(/[^a-zA-Z0-9]/g, '_')}.json`;
79
+ document.body.appendChild(link);
80
+ link.click();
81
+ document.body.removeChild(link);
82
+ URL.revokeObjectURL(url);
83
+
84
+ // Visual feedback
85
+ const button = event.currentTarget;
86
+ const originalHTML = button.innerHTML;
87
+ button.innerHTML = '<i class="bi bi-check"></i> Downloaded!';
88
+ button.classList.remove('btn-outline-secondary');
89
+ button.classList.add('btn-success');
90
+
91
+ setTimeout(() => {
92
+ button.innerHTML = originalHTML;
93
+ button.classList.remove('btn-success');
94
+ button.classList.add('btn-outline-secondary');
95
+ }, 2000);
96
+ }
97
+ </script>
98
+
42
99
  <div class="row g-4">
43
100
  <!-- Error Information -->
44
101
  <div class="col-md-8">
@@ -46,7 +103,12 @@
46
103
  <div class="card mb-4">
47
104
  <div class="card-header bg-danger text-white">
48
105
  <div class="d-flex justify-content-between align-items-center">
49
- <h5 class="mb-0"><i class="bi bi-bug-fill"></i> <%= @error.error_type %></h5>
106
+ <h5 class="mb-0">
107
+ <i class="bi bi-bug-fill"></i> <%= @error.error_type %>
108
+ <% if @error.recent? %>
109
+ <span class="badge bg-success ms-2" data-bs-toggle="tooltip" title="Error occurred within the last hour">NEW</span>
110
+ <% end %>
111
+ </h5>
50
112
  <button class="btn btn-sm btn-outline-light" onclick="copyToClipboard('<%= j @error.error_type %>', this)" title="Copy error type">
51
113
  <i class="bi bi-clipboard"></i>
52
114
  </button>
@@ -402,11 +464,57 @@
402
464
  </div>
403
465
  <div class="mb-3">
404
466
  <label for="body" class="form-label">Comment <span class="text-danger">*</span></label>
405
- <%= text_area_tag :body, nil, class: "form-control", rows: 4, placeholder: "Share your thoughts, findings, or updates...", required: true %>
467
+
468
+ <!-- Quick Templates -->
469
+ <div class="mb-2">
470
+ <small class="text-muted d-block mb-1">
471
+ <i class="bi bi-lightning-fill"></i> Quick templates:
472
+ </small>
473
+ <div class="d-flex flex-wrap gap-1">
474
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('investigating')">
475
+ <i class="bi bi-search"></i> Investigating
476
+ </button>
477
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('found_fix')">
478
+ <i class="bi bi-wrench"></i> Found Fix
479
+ </button>
480
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('need_info')">
481
+ <i class="bi bi-question-circle"></i> Need Info
482
+ </button>
483
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('duplicate')">
484
+ <i class="bi bi-files"></i> Duplicate
485
+ </button>
486
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('cannot_reproduce')">
487
+ <i class="bi bi-x-circle"></i> Cannot Reproduce
488
+ </button>
489
+ </div>
490
+ </div>
491
+
492
+ <%= text_area_tag :body, nil, class: "form-control", rows: 4, placeholder: "Share your thoughts, findings, or updates...", required: true, id: "comment_body" %>
406
493
  </div>
407
494
  <%= submit_tag "Post Comment", class: "btn btn-primary" %>
408
495
  <% end %>
409
496
  </div>
497
+
498
+ <script>
499
+ function insertTemplate(templateType) {
500
+ const textarea = document.getElementById('comment_body');
501
+ const templates = {
502
+ investigating: "šŸ” Investigating this issue now. Will update with findings.",
503
+ found_fix: "āœ… Found the fix!\n\nRoot cause: \nSolution: \nPR: ",
504
+ need_info: "ā„¹ļø Need more information:\n\n- \n- \n\nPlease provide details to help debug this issue.",
505
+ duplicate: "šŸ“‹ This appears to be a duplicate of error #\n\nClosing as duplicate.",
506
+ cannot_reproduce: "āŒ Cannot reproduce this issue.\n\nAttempted:\n- \n- \n\nNeed more details or steps to reproduce."
507
+ };
508
+
509
+ const template = templates[templateType];
510
+ if (template) {
511
+ textarea.value = template;
512
+ textarea.focus();
513
+ // Move cursor to end
514
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length);
515
+ }
516
+ }
517
+ </script>
410
518
  </div>
411
519
  </div>
412
520
  <% end %>
@@ -572,8 +680,16 @@
572
680
 
573
681
  <div class="mb-3">
574
682
  <small class="text-muted d-block mb-1">First Seen</small>
575
- <strong><%= @error.first_seen_at&.strftime("%B %d, %Y") || 'N/A' %></strong><br>
576
- <small><%= @error.first_seen_at&.strftime("%I:%M:%S %p %Z") || 'N/A' %></small>
683
+ <% if @related_errors.any? %>
684
+ <%= link_to "#timeline", class: "text-decoration-none", data: { bs_toggle: "tooltip" }, title: "Jump to timeline" do %>
685
+ <strong><%= @error.first_seen_at&.strftime("%B %d, %Y") || 'N/A' %></strong><br>
686
+ <small><%= @error.first_seen_at&.strftime("%I:%M:%S %p %Z") || 'N/A' %></small>
687
+ <i class="bi bi-arrow-down-circle ms-1"></i>
688
+ <% end %>
689
+ <% else %>
690
+ <strong><%= @error.first_seen_at&.strftime("%B %d, %Y") || 'N/A' %></strong><br>
691
+ <small><%= @error.first_seen_at&.strftime("%I:%M:%S %p %Z") || 'N/A' %></small>
692
+ <% end %>
577
693
  </div>
578
694
 
579
695
  <div class="mb-3">
Binary file
@@ -4,6 +4,15 @@ module RailsErrorDashboard
4
4
 
5
5
  # Initialize the engine
6
6
  initializer "rails_error_dashboard.middleware" do |app|
7
+ # Enable Flash middleware for Error Dashboard routes in API-only apps
8
+ # This ensures flash messages work even when config.api_only = true
9
+ if app.config.api_only
10
+ # Insert Flash middleware ONLY for Error Dashboard routes
11
+ app.middleware.use ActionDispatch::Flash
12
+ app.middleware.use ActionDispatch::Cookies
13
+ app.middleware.use ActionDispatch::Session::CookieStore
14
+ end
15
+
7
16
  # Add error catching middleware if enabled
8
17
  if RailsErrorDashboard.configuration.enable_middleware
9
18
  app.config.middleware.insert_before 0, RailsErrorDashboard::Middleware::ErrorCatcher
@@ -46,7 +46,16 @@ module RailsErrorDashboard
46
46
  end
47
47
 
48
48
  def build_request_url
49
- return @context[:request]&.fullpath if @context[:request]
49
+ # Handle both full Rails requests and API-only requests
50
+ if @context[:request]
51
+ begin
52
+ return @context[:request].fullpath
53
+ rescue NoMethodError
54
+ # Fallback for minimal request objects
55
+ return @context[:request].path rescue nil
56
+ end
57
+ end
58
+
50
59
  return @context[:request_url] if @context[:request_url]
51
60
  return "Background Job: #{@context[:job]&.class}" if @context[:job]
52
61
  return "Sidekiq: #{@context[:job_class]}" if @context[:job_class]
@@ -114,10 +123,17 @@ module RailsErrorDashboard
114
123
  def detect_platform
115
124
  # Check if it's from a mobile request
116
125
  user_agent = extract_user_agent
117
- return Services::PlatformDetector.detect(user_agent) if @context[:request]
118
126
 
119
- # Everything else is API/backend
120
- "API"
127
+ return "API" unless user_agent.present? && @context[:request]
128
+
129
+ # Only detect platform if we have a valid user agent
130
+ begin
131
+ Services::PlatformDetector.detect(user_agent)
132
+ rescue => e
133
+ # Fallback to API if platform detection fails
134
+ Rails.logger.debug("[RailsErrorDashboard] Platform detection failed: #{e.message}")
135
+ "API"
136
+ end
121
137
  end
122
138
 
123
139
  def extract_controller_name
@@ -161,8 +177,8 @@ module RailsErrorDashboard
161
177
  end
162
178
 
163
179
  def extract_session_id
164
- # From Rails session
165
- return @context[:request]&.session&.id if @context[:request]&.session
180
+ # Session is only available in full Rails mode, not API-only
181
+ return @context[:request]&.session&.id if @context[:request]&.respond_to?(:session) && @context[:request]&.session
166
182
 
167
183
  # From explicit context
168
184
  return @context[:session_id] if @context[:session_id]
@@ -1,3 +1,3 @@
1
1
  module RailsErrorDashboard
2
- VERSION = "0.1.14"
2
+ VERSION = "0.1.16"
3
3
  end
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.1.14
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anjan Jagirdar
@@ -312,6 +312,7 @@ files:
312
312
  - app/views/rails_error_dashboard/errors/settings.html.erb
313
313
  - app/views/rails_error_dashboard/errors/show.html.erb
314
314
  - config/routes.rb
315
+ - db/development.sqlite3
315
316
  - db/migrate/20251224000001_create_rails_error_dashboard_error_logs.rb
316
317
  - db/migrate/20251224081522_add_better_tracking_to_error_logs.rb
317
318
  - db/migrate/20251224101217_add_controller_action_to_error_logs.rb
@@ -378,7 +379,7 @@ metadata:
378
379
  source_code_uri: https://github.com/AnjanJ/rails_error_dashboard
379
380
  changelog_uri: https://github.com/AnjanJ/rails_error_dashboard/blob/main/CHANGELOG.md
380
381
  post_install_message: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
381
- \ Rails Error Dashboard v0.1.14\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
382
+ \ Rails Error Dashboard v0.1.16\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
382
383
  First time? Quick start:\n rails generate rails_error_dashboard:install\n rails
383
384
  db:migrate\n # Add to config/routes.rb:\n mount RailsErrorDashboard::Engine
384
385
  => '/error_dashboard'\n\n\U0001F504 Upgrading from v0.1.x?\n rails db:migrate\n