rails_error_dashboard 0.1.15 → 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 +4 -4
- data/app/controllers/rails_error_dashboard/application_controller.rb +6 -0
- data/app/views/layouts/rails_error_dashboard.html.erb +19 -13
- data/app/views/rails_error_dashboard/errors/analytics.html.erb +17 -2
- data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +20 -26
- data/app/views/rails_error_dashboard/errors/show.html.erb +0 -23
- data/lib/rails_error_dashboard/engine.rb +9 -0
- data/lib/rails_error_dashboard/value_objects/error_context.rb +22 -6
- data/lib/rails_error_dashboard/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2bb87db0ac566c74592758f0bb11c4e16f3caff7464d7c428436083adc13edb5
|
|
4
|
+
data.tar.gz: ccead510cbc1f57d1197aafa34e4c43f3f74a59f7b4d56e53ea0b8ebb4fa73e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
7
|
-
|
|
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">
|
|
@@ -1170,17 +1174,19 @@
|
|
|
1170
1174
|
};
|
|
1171
1175
|
|
|
1172
1176
|
// Show flash messages as toasts
|
|
1173
|
-
<% if flash
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
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 %>
|
|
1184
1190
|
<% end %>
|
|
1185
1191
|
});
|
|
1186
1192
|
</script>
|
|
@@ -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:
|
|
144
|
+
colors: platformColors,
|
|
136
145
|
height: "300px",
|
|
137
146
|
legend: "bottom",
|
|
138
147
|
donut: true,
|
|
139
148
|
library: {
|
|
140
149
|
plugins: {
|
|
141
|
-
legend: {
|
|
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
|
});
|
|
@@ -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: (
|
|
305
|
-
|
|
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
|
|
347
|
+
// Apply platform-specific colors
|
|
346
348
|
datasets.forEach(ds => {
|
|
347
|
-
const color = getPlatformColor(ds.platform
|
|
349
|
+
const color = getPlatformColor(ds.platform);
|
|
348
350
|
ds.borderColor = color;
|
|
349
351
|
// Add transparency for background
|
|
350
|
-
ds.backgroundColor = color
|
|
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: (
|
|
386
|
-
|
|
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
|
},
|
|
@@ -30,9 +30,6 @@
|
|
|
30
30
|
<button type="button" class="btn btn-outline-secondary" onclick="downloadErrorJSON()" title="Download error details as JSON">
|
|
31
31
|
<i class="bi bi-download"></i> Export JSON
|
|
32
32
|
</button>
|
|
33
|
-
<button type="button" class="btn btn-outline-primary" onclick="copyErrorURL()" title="Copy shareable link to this error">
|
|
34
|
-
<i class="bi bi-share"></i> Share
|
|
35
|
-
</button>
|
|
36
33
|
<% if @error.resolved? %>
|
|
37
34
|
<span class="badge bg-success fs-6">
|
|
38
35
|
<i class="bi bi-check-circle"></i> Resolved
|
|
@@ -46,26 +43,6 @@
|
|
|
46
43
|
</div>
|
|
47
44
|
|
|
48
45
|
<script>
|
|
49
|
-
function copyErrorURL() {
|
|
50
|
-
const url = window.location.href;
|
|
51
|
-
navigator.clipboard.writeText(url).then(() => {
|
|
52
|
-
// Change button text temporarily
|
|
53
|
-
const button = event.currentTarget;
|
|
54
|
-
const originalHTML = button.innerHTML;
|
|
55
|
-
button.innerHTML = '<i class="bi bi-check"></i> Copied!';
|
|
56
|
-
button.classList.remove('btn-outline-primary');
|
|
57
|
-
button.classList.add('btn-success');
|
|
58
|
-
|
|
59
|
-
setTimeout(() => {
|
|
60
|
-
button.innerHTML = originalHTML;
|
|
61
|
-
button.classList.remove('btn-success');
|
|
62
|
-
button.classList.add('btn-outline-primary');
|
|
63
|
-
}, 2000);
|
|
64
|
-
}).catch(err => {
|
|
65
|
-
alert('Failed to copy URL: ' + err);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
46
|
function downloadErrorJSON() {
|
|
70
47
|
const errorData = {
|
|
71
48
|
id: <%= @error.id %>,
|
|
@@ -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
|
-
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
#
|
|
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]
|
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.
|
|
4
|
+
version: 0.1.16
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anjan Jagirdar
|
|
@@ -379,7 +379,7 @@ metadata:
|
|
|
379
379
|
source_code_uri: https://github.com/AnjanJ/rails_error_dashboard
|
|
380
380
|
changelog_uri: https://github.com/AnjanJ/rails_error_dashboard/blob/main/CHANGELOG.md
|
|
381
381
|
post_install_message: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
|
|
382
|
-
\ Rails Error Dashboard v0.1.
|
|
382
|
+
\ Rails Error Dashboard v0.1.16\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
|
|
383
383
|
First time? Quick start:\n rails generate rails_error_dashboard:install\n rails
|
|
384
384
|
db:migrate\n # Add to config/routes.rb:\n mount RailsErrorDashboard::Engine
|
|
385
385
|
=> '/error_dashboard'\n\n\U0001F504 Upgrading from v0.1.x?\n rails db:migrate\n
|