side_bro 0.2.2 → 0.2.3
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/README.md +4 -0
- data/lib/side_bro/version.rb +1 -1
- data/lib/side_bro/web/application.rb +6 -2
- data/lib/side_bro/web/helpers.rb +7 -0
- data/web/assets/javascripts/dashboard.js +68 -127
- data/web/assets/stylesheets/style.css +271 -472
- data/web/views/_nav.html.erb +1 -2
- data/web/views/dashboard.html.erb +11 -9
- data/web/views/dead.html.erb +1 -1
- data/web/views/layout.html.erb +3 -4
- data/web/views/queue.html.erb +21 -14
- data/web/views/retry.html.erb +1 -1
- data/web/views/scheduled_job_info.html.erb +1 -1
- metadata +2 -3
- data/web/views/_footer.html.erb +0 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c5694dd3d4c5847cffd789314b1f343d15246c0c80758173153026fad0544e8
|
|
4
|
+
data.tar.gz: c80886ec41afd7f842e78982b4ade79d55d9657fa7061056f99c3e9186000509
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06f6f13e9152e3e7992a353d22a9d518f81562f627ccd7336a9718bba5039333a9217aa3ab31b0ec1b86c09776a80b83a3eb2f9392305cffcad304934b957fa1
|
|
7
|
+
data.tar.gz: a4ba97aa3c4fac898b94852dbd81400f8c194efd0e2c07762c926c26c0dc52f0c4c7624f8cce64f501e953701220a7aaed430cbbe5967e6c19b6cabcf644eee5
|
data/README.md
CHANGED
data/lib/side_bro/version.rb
CHANGED
|
@@ -126,11 +126,15 @@ module SideBro
|
|
|
126
126
|
@filter_job = params["filter_job"].to_s.strip
|
|
127
127
|
@filter_args = params["filter_args"].to_s.strip
|
|
128
128
|
@filtering = !@filter_job.empty? || !@filter_args.empty?
|
|
129
|
+
@search = params["q"].to_s.strip
|
|
130
|
+
@searching = !@search.empty?
|
|
131
|
+
needle = @search.downcase
|
|
129
132
|
|
|
130
|
-
if @filtering
|
|
133
|
+
if @filtering || @searching
|
|
131
134
|
@jobs = @queue.lazy.select { |j|
|
|
132
135
|
(@filter_job.empty? || job_display_class(j).include?(@filter_job)) &&
|
|
133
|
-
(@filter_args.empty? || display_args(j).include?(@filter_args))
|
|
136
|
+
(@filter_args.empty? || display_args(j).include?(@filter_args)) &&
|
|
137
|
+
(!@searching || [j.jid, job_display_class(j), display_args(j)].any? { |f| f.to_s.downcase.include?(needle) })
|
|
134
138
|
}.first(@per_page)
|
|
135
139
|
@total = nil
|
|
136
140
|
else
|
data/lib/side_bro/web/helpers.rb
CHANGED
|
@@ -30,6 +30,13 @@ module SideBro
|
|
|
30
30
|
script.end_with?("/") ? script : "#{script}/"
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# Appends the file mtime so browsers refetch an asset when it changes
|
|
34
|
+
def asset_path(path)
|
|
35
|
+
file = File.join(SideBro::Web::ASSETS_PATH, path)
|
|
36
|
+
version = File.exist?(file) ? File.mtime(file).to_i : SideBro::VERSION
|
|
37
|
+
"#{root_path}assets/#{path}?v=#{version}"
|
|
38
|
+
end
|
|
39
|
+
|
|
33
40
|
def locale
|
|
34
41
|
session[:locale] || "en"
|
|
35
42
|
end
|
|
@@ -41,68 +41,71 @@
|
|
|
41
41
|
var prevProcessed = null;
|
|
42
42
|
var prevFailed = null;
|
|
43
43
|
|
|
44
|
+
// Draws in real pixels so text and lines are never stretched
|
|
45
|
+
function sizeSvg(svg) {
|
|
46
|
+
var W = Math.max(200, Math.round(svg.clientWidth));
|
|
47
|
+
var H = Math.max(120, Math.round(svg.clientHeight));
|
|
48
|
+
svg.setAttribute("viewBox", "0 0 " + W + " " + H);
|
|
49
|
+
return { W: W, H: H };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Flat area chart: thin line over a translucent fill, four edge-aligned x labels
|
|
53
|
+
var PAD_T = 8, PAD_B = 26;
|
|
54
|
+
|
|
55
|
+
function drawAreaChart(svg, series, labels) {
|
|
56
|
+
var size = sizeSvg(svg);
|
|
57
|
+
var W = size.W, H = size.H, ch = H - PAD_T - PAD_B;
|
|
58
|
+
var len = series[0].data.length;
|
|
59
|
+
var all = [];
|
|
60
|
+
series.forEach(function (s) { all = all.concat(s.data); });
|
|
61
|
+
var maxY = Math.max(1, Math.max.apply(null, all)) * 1.15;
|
|
62
|
+
var x = function (i) { return (i / Math.max(len - 1, 1)) * W; };
|
|
63
|
+
var y = function (v) { return PAD_T + (1 - v / maxY) * ch; };
|
|
64
|
+
var base = PAD_T + ch;
|
|
65
|
+
|
|
66
|
+
var body = "";
|
|
67
|
+
series.forEach(function (s) {
|
|
68
|
+
var line = "";
|
|
69
|
+
s.data.forEach(function (v, i) { line += (i === 0 ? "M" : "L") + x(i).toFixed(1) + "," + y(v).toFixed(1) + " "; });
|
|
70
|
+
var area = line + "L" + W + "," + base + " L0," + base + " Z";
|
|
71
|
+
body += "<path d=\"" + area + "\" fill=\"" + s.color + "\" fill-opacity=\"0.16\"/>";
|
|
72
|
+
body += "<path d=\"" + line + "\" stroke=\"" + s.color + "\" stroke-width=\"1.5\" fill=\"none\" stroke-linejoin=\"round\"/>";
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
var text = "";
|
|
76
|
+
labels.forEach(function (l, j) {
|
|
77
|
+
var anchor = j === 0 ? "start" : (j === labels.length - 1 ? "end" : "middle");
|
|
78
|
+
var xx = (j / (labels.length - 1)) * W;
|
|
79
|
+
text += "<text x=\"" + xx + "\" y=\"" + (H - 6) + "\" font-size=\"10\" fill=\"#4f567c\" text-anchor=\"" + anchor + "\" font-family=\"Geist Mono\">" + l + "</text>";
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
svg.innerHTML = body + text;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function clock(d) {
|
|
86
|
+
return [d.getHours(), d.getMinutes(), d.getSeconds()].map(function (n) { return n < 10 ? "0" + n : n; }).join(":");
|
|
87
|
+
}
|
|
88
|
+
|
|
44
89
|
// ---- live chart ----
|
|
45
90
|
function renderLiveChart() {
|
|
46
91
|
var svg = document.getElementById("liveChart");
|
|
47
92
|
if (!svg) return;
|
|
48
|
-
var W = 1200, H = 280, PAD_L = 54, PAD_R = 20, PAD_T = 18, PAD_B = 28;
|
|
49
|
-
var cw = W - PAD_L - PAD_R, ch = H - PAD_T - PAD_B;
|
|
50
93
|
var interval = getInterval();
|
|
51
94
|
var totalSec = (N - 1) * interval;
|
|
52
95
|
var chartWindow = document.getElementById("chartWindow");
|
|
53
96
|
if (chartWindow) chartWindow.textContent = totalSec >= 60 ? Math.round(totalSec / 60) + " min" : totalSec + "s";
|
|
54
|
-
var all = seriesProcessed.concat(seriesFailed);
|
|
55
|
-
var maxY = Math.max(12, Math.ceil(Math.max.apply(null, all)));
|
|
56
|
-
var x = function (i) { return PAD_L + (i / (N - 1)) * cw; };
|
|
57
|
-
var y = function (v) { return PAD_T + (1 - v / maxY) * ch; };
|
|
58
|
-
|
|
59
|
-
function makePath(s) {
|
|
60
|
-
var d = "";
|
|
61
|
-
s.forEach(function (v, i) { d += (i === 0 ? "M" : "L") + x(i).toFixed(1) + "," + y(v).toFixed(1) + " "; });
|
|
62
|
-
return d;
|
|
63
|
-
}
|
|
64
|
-
function makeArea(s) {
|
|
65
|
-
var d = "M" + x(0) + "," + y(0) + " ";
|
|
66
|
-
s.forEach(function (v, i) { d += "L" + x(i).toFixed(1) + "," + y(v).toFixed(1) + " "; });
|
|
67
|
-
d += "L" + x(N - 1) + "," + y(0) + " Z";
|
|
68
|
-
return d;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
var grid = "";
|
|
72
|
-
for (var i = 0; i <= 4; i++) {
|
|
73
|
-
var yy = PAD_T + (i / 4) * ch;
|
|
74
|
-
var val = Math.round(maxY - (i / 4) * maxY);
|
|
75
|
-
grid += "<line x1=\"" + PAD_L + "\" y1=\"" + yy + "\" x2=\"" + (W - PAD_R) + "\" y2=\"" + yy + "\" stroke=\"#1f1638\" stroke-dasharray=\"3 4\"/>";
|
|
76
|
-
grid += "<text x=\"" + (PAD_L - 8) + "\" y=\"" + (yy + 4) + "\" font-size=\"11\" fill=\"#6a5f8a\" text-anchor=\"end\" font-family=\"JetBrains Mono\">" + val + "</text>";
|
|
77
|
-
}
|
|
78
|
-
var xLabels = "";
|
|
79
|
-
for (var j = 0; j <= 4; j++) {
|
|
80
|
-
var xx = PAD_L + (j / 4) * cw;
|
|
81
|
-
var sec = Math.round(((4 - j) / 4) * totalSec);
|
|
82
|
-
xLabels += "<text x=\"" + xx + "\" y=\"" + (H - 8) + "\" font-size=\"11\" fill=\"#6a5f8a\" text-anchor=\"middle\" font-family=\"JetBrains Mono\">" + (sec === 0 ? "now" : "-" + sec + "s") + "</text>";
|
|
83
|
-
}
|
|
84
97
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"<filter id=\"glow\"><feGaussianBlur stdDeviation=\"2\"/></filter>" +
|
|
92
|
-
"</defs>" +
|
|
93
|
-
grid + xLabels +
|
|
94
|
-
"<path d=\"" + makeArea(seriesProcessed) + "\" fill=\"url(#gradProc)\"/>" +
|
|
95
|
-
"<path d=\"" + makePath(seriesProcessed) + "\" stroke=\"#4be3ff\" stroke-width=\"2\" fill=\"none\" filter=\"url(#glow)\" opacity=\"0.6\"/>" +
|
|
96
|
-
"<path d=\"" + makePath(seriesProcessed) + "\" stroke=\"#4be3ff\" stroke-width=\"2\" fill=\"none\"/>" +
|
|
97
|
-
"<path d=\"" + makePath(seriesFailed) + "\" stroke=\"#ff5470\" stroke-width=\"2\" fill=\"none\"/>" +
|
|
98
|
-
"<circle cx=\"" + x(N - 1) + "\" cy=\"" + y(seriesProcessed[N - 1]) + "\" r=\"4\" fill=\"#4be3ff\"/>" +
|
|
99
|
-
"<circle cx=\"" + x(N - 1) + "\" cy=\"" + y(seriesProcessed[N - 1]) + "\" r=\"8\" fill=\"#4be3ff\" opacity=\"0.25\"/>";
|
|
98
|
+
var now = Date.now();
|
|
99
|
+
var labels = [0, 1, 2, 3].map(function (j) { return clock(new Date(now - ((3 - j) / 3) * totalSec * 1000)); });
|
|
100
|
+
drawAreaChart(svg, [
|
|
101
|
+
{ data: seriesFailed, color: "#f2708a" },
|
|
102
|
+
{ data: seriesProcessed, color: "#7c6cf6" }
|
|
103
|
+
], labels);
|
|
100
104
|
|
|
101
105
|
var tip = document.getElementById("chartTip");
|
|
102
106
|
svg.onmousemove = function (e) {
|
|
103
107
|
var rect = svg.getBoundingClientRect();
|
|
104
|
-
var
|
|
105
|
-
var idx = Math.max(0, Math.min(N - 1, Math.round(((px - PAD_L) / cw) * (N - 1))));
|
|
108
|
+
var idx = Math.max(0, Math.min(N - 1, Math.round(((e.clientX - rect.left) / rect.width) * (N - 1))));
|
|
106
109
|
document.getElementById("tipProc").textContent = seriesProcessed[idx].toFixed(1);
|
|
107
110
|
document.getElementById("tipFail").textContent = seriesFailed[idx].toFixed(1);
|
|
108
111
|
var ageSec = (N - 1 - idx) * getInterval();
|
|
@@ -117,101 +120,33 @@
|
|
|
117
120
|
svg.onmouseleave = function () { tip.style.display = "none"; };
|
|
118
121
|
}
|
|
119
122
|
|
|
120
|
-
function formatVal(v) {
|
|
121
|
-
if (v >= 1000000) return (v / 1000000).toFixed(1).replace(/\.0$/, "") + "M";
|
|
122
|
-
if (v >= 1000) return Math.round(v / 1000) + "K";
|
|
123
|
-
return String(v);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
123
|
// ---- history chart ----
|
|
127
124
|
var currentRange = "month";
|
|
128
125
|
|
|
129
126
|
function renderHistoryChart() {
|
|
130
127
|
var svg = document.getElementById("historyChart");
|
|
131
128
|
if (!svg) return;
|
|
132
|
-
var W = 1200, H = 320, PAD_L = 60, PAD_R = 30, PAD_T = 20, PAD_B = 40;
|
|
133
|
-
var cw = W - PAD_L - PAD_R, ch = H - PAD_T - PAD_B;
|
|
134
|
-
|
|
135
129
|
var histData = window.HISTORY_DATA;
|
|
136
130
|
if (!histData) return;
|
|
137
131
|
|
|
138
|
-
|
|
139
|
-
var allPoints = histData.processed;
|
|
140
|
-
var allFailed = histData.failed;
|
|
141
|
-
var allLabels = histData.labels;
|
|
142
|
-
var total = allPoints.length;
|
|
143
|
-
|
|
132
|
+
var total = histData.processed.length;
|
|
144
133
|
var sliceLen = total;
|
|
145
134
|
if (currentRange === "week") sliceLen = Math.min(7, total);
|
|
146
135
|
else if (currentRange === "month") sliceLen = Math.min(30, total);
|
|
147
136
|
else if (currentRange === "3m") sliceLen = Math.min(90, total);
|
|
148
137
|
else if (currentRange === "6m") sliceLen = Math.min(180, total);
|
|
149
138
|
|
|
150
|
-
var points =
|
|
151
|
-
var failed =
|
|
152
|
-
var labels =
|
|
139
|
+
var points = histData.processed.slice(0, sliceLen);
|
|
140
|
+
var failed = histData.failed.slice(0, sliceLen);
|
|
141
|
+
var labels = histData.labels.slice(0, sliceLen);
|
|
153
142
|
if (points.length === 0) return;
|
|
154
143
|
|
|
155
|
-
var
|
|
156
|
-
var
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
points.forEach(function (v, i) {
|
|
162
|
-
line += (i === 0 ? "M" : "L") + x(i).toFixed(1) + "," + y(v).toFixed(1) + " ";
|
|
163
|
-
area += "L" + x(i).toFixed(1) + "," + y(v).toFixed(1) + " ";
|
|
164
|
-
});
|
|
165
|
-
area += "L" + x(points.length - 1) + "," + y(0) + " Z";
|
|
166
|
-
failed.forEach(function (v, i) {
|
|
167
|
-
failLine += (i === 0 ? "M" : "L") + x(i).toFixed(1) + "," + y(v).toFixed(1) + " ";
|
|
168
|
-
failArea += "L" + x(i).toFixed(1) + "," + y(v).toFixed(1) + " ";
|
|
169
|
-
});
|
|
170
|
-
failArea += "L" + x(failed.length - 1) + "," + y(0) + " Z";
|
|
171
|
-
|
|
172
|
-
var grid = "";
|
|
173
|
-
for (var i = 0; i <= 4; i++) {
|
|
174
|
-
var yy = PAD_T + (i / 4) * ch;
|
|
175
|
-
var val = Math.round(maxY - (i / 4) * maxY);
|
|
176
|
-
grid += "<line x1=\"" + PAD_L + "\" y1=\"" + yy + "\" x2=\"" + (W - PAD_R) + "\" y2=\"" + yy + "\" stroke=\"#1f1638\" stroke-dasharray=\"2 4\"/>";
|
|
177
|
-
if (i > 0) grid += "<text x=\"" + (PAD_L - 10) + "\" y=\"" + (yy + 4) + "\" font-size=\"11\" fill=\"#6a5f8a\" text-anchor=\"end\" font-family=\"JetBrains Mono\">" + formatVal(val) + "</text>";
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// label thinning
|
|
181
|
-
var labelStep = Math.max(1, Math.floor(labels.length / 6));
|
|
182
|
-
var xLabels = "";
|
|
183
|
-
labels.forEach(function (l, i) {
|
|
184
|
-
if (i % labelStep !== 0 && i !== labels.length - 1) return;
|
|
185
|
-
var xx = x(i);
|
|
186
|
-
xLabels += "<line x1=\"" + xx + "\" y1=\"" + PAD_T + "\" x2=\"" + xx + "\" y2=\"" + (H - PAD_B) + "\" stroke=\"#1f1638\" stroke-dasharray=\"2 4\"/>";
|
|
187
|
-
xLabels += "<text x=\"" + xx + "\" y=\"" + (H - 14) + "\" font-size=\"12\" fill=\"#a98cff\" text-anchor=\"middle\" font-family=\"JetBrains Mono\">" + l + "</text>";
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
svg.innerHTML =
|
|
191
|
-
"<defs>" +
|
|
192
|
-
"<linearGradient id=\"gradHist\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\">" +
|
|
193
|
-
"<stop offset=\"0%\" stop-color=\"#a98cff\" stop-opacity=\"0.35\"/>" +
|
|
194
|
-
"<stop offset=\"60%\" stop-color=\"#a98cff\" stop-opacity=\"0.05\"/>" +
|
|
195
|
-
"<stop offset=\"100%\" stop-color=\"#a98cff\" stop-opacity=\"0\"/>" +
|
|
196
|
-
"</linearGradient>" +
|
|
197
|
-
"<linearGradient id=\"gradHistLine\" x1=\"0\" x2=\"1\" y1=\"0\" y2=\"0\">" +
|
|
198
|
-
"<stop offset=\"0%\" stop-color=\"#a98cff\"/>" +
|
|
199
|
-
"<stop offset=\"60%\" stop-color=\"#ff5cd6\"/>" +
|
|
200
|
-
"<stop offset=\"100%\" stop-color=\"#7a3dff\"/>" +
|
|
201
|
-
"</linearGradient>" +
|
|
202
|
-
"<linearGradient id=\"gradFail\" x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\">" +
|
|
203
|
-
"<stop offset=\"0%\" stop-color=\"#ff5470\" stop-opacity=\"0.25\"/>" +
|
|
204
|
-
"<stop offset=\"100%\" stop-color=\"#ff5470\" stop-opacity=\"0\"/>" +
|
|
205
|
-
"</linearGradient>" +
|
|
206
|
-
"</defs>" +
|
|
207
|
-
grid + xLabels +
|
|
208
|
-
"<path d=\"" + area + "\" fill=\"url(#gradHist)\"/>" +
|
|
209
|
-
"<path d=\"" + failArea + "\" fill=\"url(#gradFail)\"/>" +
|
|
210
|
-
"<path d=\"" + line + "\" stroke=\"url(#gradHistLine)\" stroke-width=\"2.4\" fill=\"none\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/>" +
|
|
211
|
-
"<path d=\"" + failLine + "\" stroke=\"#ff5470\" stroke-width=\"1.6\" fill=\"none\" stroke-linejoin=\"round\" stroke-linecap=\"round\"/>" +
|
|
212
|
-
points.map(function (v, i) {
|
|
213
|
-
return "<circle cx=\"" + x(i).toFixed(1) + "\" cy=\"" + y(v).toFixed(1) + "\" r=\"2.4\" fill=\"#170f29\" stroke=\"#a98cff\" stroke-width=\"1.5\"/>";
|
|
214
|
-
}).join("");
|
|
144
|
+
var last = labels.length - 1;
|
|
145
|
+
var picked = [0, 1, 2, 3].map(function (j) { return labels[Math.round((j / 3) * last)]; });
|
|
146
|
+
drawAreaChart(svg, [
|
|
147
|
+
{ data: failed, color: "#f2708a" },
|
|
148
|
+
{ data: points, color: "#7c6cf6" }
|
|
149
|
+
], picked);
|
|
215
150
|
}
|
|
216
151
|
|
|
217
152
|
// ---- live polling via fetch ----
|
|
@@ -259,4 +194,10 @@
|
|
|
259
194
|
renderLiveChart();
|
|
260
195
|
renderHistoryChart();
|
|
261
196
|
restartTimer();
|
|
197
|
+
|
|
198
|
+
var resizeTimer = null;
|
|
199
|
+
window.addEventListener("resize", function () {
|
|
200
|
+
clearTimeout(resizeTimer);
|
|
201
|
+
resizeTimer = setTimeout(function () { renderLiveChart(); renderHistoryChart(); }, 100);
|
|
202
|
+
});
|
|
262
203
|
})();
|