aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--interface/css/rspamd.css4
-rw-r--r--interface/index.html18
-rw-r--r--interface/js/app/history.js57
-rw-r--r--interface/js/app/rspamd.js21
4 files changed, 93 insertions, 7 deletions
diff --git a/interface/css/rspamd.css b/interface/css/rspamd.css
index 9f97a668b..54310049b 100644
--- a/interface/css/rspamd.css
+++ b/interface/css/rspamd.css
@@ -420,8 +420,10 @@ table#symbolsTable input[type="number"] {
display: none;
}
+#history-from,
+#history-count,
#history_page_size {
- width: 6em !important;
+ width: 6em;
text-align: center;
}
diff --git a/interface/index.html b/interface/index.html
index 30181e788..165eae200 100644
--- a/interface/index.html
+++ b/interface/index.html
@@ -124,6 +124,16 @@
</div>
</div>
</div>
+
+ <div class="card mt-1">
+ <div class="card-body">
+ <h6 class="card-title fw-bolder">History rows per load</h6>
+ <div class="input-group input-group-sm was-validated">
+ <input type="number" id="settings-history-count" class="form-control" min="1" step="1" placeholder="1000">
+ <button id="settings-history-count-restore" class="btn btn-secondary">Restore default</button>
+ </div>
+ </div>
+ </div>
</div>
</div>
</form>
@@ -681,9 +691,13 @@
<option value="score">Score value</option>
<option value="name">Name</option>
</select>
- <label for="history_page_size" class="ms-2">Rows per page:</label>
+ <label for="history-from" class="ms-3" title="Start from this row number">Offset:</label>
+ <input type="number" id="history-from" class="form-control ms-1" value="0" min="0" step="1" title="Start from this row number">
+ <label for="history-count" class="ms-2" title="Number of rows to load">Count:</label>
+ <input type="number" id="history-count" class="form-control ms-1" value="1000" min="1" step="1" title="Number of rows to load">
+ <label for="history_page_size" class="ms-2">Rows/page:</label>
<input id="history_page_size" class="form-control ms-1" value="25" min="1" type="number">
- <button class="btn btn-outline-secondary btn-sm ms-2 d-flex align-items-center dropdown-toggle ft-columns-btn" type="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" disabled>
+ <button class="btn btn-outline-secondary btn-sm ms-3 d-flex align-items-center dropdown-toggle ft-columns-btn" type="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" disabled>
<i class="fas fa-columns me-1"></i>Columns
</button>
<div class="dropdown-menu ft-columns-dropdown p-2"></div>
diff --git a/interface/js/app/history.js b/interface/js/app/history.js
index 185922087..bf1dbae53 100644
--- a/interface/js/app/history.js
+++ b/interface/js/app/history.js
@@ -30,6 +30,12 @@ define(["jquery", "app/common", "app/libft", "footable"],
const ui = {};
let prevVersion = null;
+ // History range: offset and count
+ const histFromDef = 0;
+ const historyCountDef = 1000;
+ let histFrom = histFromDef;
+ let histCount = parseInt(localStorage.getItem("historyCount"), 10) || historyCountDef;
+
function process_history_legacy(data) {
const items = [];
@@ -152,7 +158,8 @@ define(["jquery", "app/common", "app/libft", "footable"],
ui.getHistory = function () {
$("#refresh, #updateHistory").attr("disabled", true);
- common.query("history", {
+ const histTo = histFrom - 1 + histCount;
+ common.query(`history?from=${histFrom}&to=${histTo}`, {
success: function (req_data) {
function differentVersions(neighbours_data) {
const dv = neighbours_data.some((e) => e.version !== neighbours_data[0].version);
@@ -192,8 +199,10 @@ define(["jquery", "app/common", "app/libft", "footable"],
// Is there a way to get an event when the table is destroyed?
setTimeout(() => {
libft.initHistoryTable(data, items, "history", get_history_columns(data), false,
- () => $("#refresh, #updateHistory, #history .ft-columns-dropdown .btn-dropdown-apply")
- .removeAttr("disabled"));
+ () => {
+ $("#history .ft-columns-dropdown .btn-dropdown-apply").removeAttr("disabled");
+ ui.updateHistoryControlsState();
+ });
}, 200);
}
prevVersion = version;
@@ -201,7 +210,7 @@ define(["jquery", "app/common", "app/libft", "footable"],
libft.destroyTable("history");
}
},
- error: () => $("#refresh, #updateHistory").removeAttr("disabled"),
+ error: () => ui.updateHistoryControlsState(),
errorMessage: "Cannot receive history",
});
};
@@ -282,6 +291,46 @@ define(["jquery", "app/common", "app/libft", "footable"],
});
};
+ ui.updateHistoryControlsState = function () {
+ const from = parseInt($("#history-from").val(), 10);
+ const count = parseInt($("#history-count").val(), 10);
+ const valid = !(isNaN(from) || from < 0 || isNaN(count) || count < 1);
+
+ if (valid) {
+ $("#refresh, #updateHistory").removeAttr("disabled").removeClass("disabled");
+ } else {
+ $("#refresh, #updateHistory").attr("disabled", true).addClass("disabled");
+ }
+ };
+
+ function validateAndClampInput(el) {
+ const min = el.id === "history-from" ? 0 : 1;
+ let v = parseInt(el.value, 10);
+ if (isNaN(v) || v < min) {
+ v = min;
+ $(el).addClass("is-invalid");
+ } else {
+ $(el).removeClass("is-invalid");
+ }
+ return v;
+ }
+
+ $("#history-from").val(histFrom);
+ $("#history-count").val(histCount);
+ $("#history-from, #history-count").on("input", (e) => {
+ validateAndClampInput(e.currentTarget);
+ ui.updateHistoryControlsState();
+ });
+ $("#history-from, #history-count").on("blur", (e) => {
+ const el = e.currentTarget;
+ const v = validateAndClampInput(el);
+ $(el).val(v).removeClass("is-invalid");
+ ui.updateHistoryControlsState();
+ });
+ $("#history-from,#history-count").on("change", () => {
+ histFrom = parseInt($("#history-from").val(), 10) || histFromDef;
+ histCount = parseInt($("#history-count").val(), 10) || historyCountDef;
+ });
libft.set_page_size("history", $("#history_page_size").val());
libft.bindHistoryTableEventHandlers("history", 8);
diff --git a/interface/js/app/rspamd.js b/interface/js/app/rspamd.js
index cb7fb8ace..4b154c2ae 100644
--- a/interface/js/app/rspamd.js
+++ b/interface/js/app/rspamd.js
@@ -198,6 +198,8 @@ define(["jquery", "app/common", "stickytabs", "visibility",
$(".preset").hide();
$(".history").show();
$(".dynamic").hide();
+
+ module.updateHistoryControlsState();
});
break;
case "#disconnect":
@@ -348,6 +350,8 @@ define(["jquery", "app/common", "stickytabs", "visibility",
let selected_locale = null;
let custom_locale = null;
const localeTextbox = ".popover #settings-popover #locale";
+ const historyCountDef = 1000;
+ const historyCountSelector = ".popover #settings-popover #settings-history-count";
function validateLocale(saveToLocalStorage) {
function toggle_form_group_class(remove, add) {
@@ -406,6 +410,8 @@ define(["jquery", "app/common", "stickytabs", "visibility",
$(localeTextbox).val(custom_locale);
ajaxSetup(localStorage.getItem("ajax_timeout"), true);
+
+ $(historyCountSelector).val(parseInt(localStorage.getItem("historyCount"), 10) || historyCountDef);
});
$(document).on("change", '.popover #settings-popover input:radio[name="locale"]', function () {
selected_locale = this.value;
@@ -423,6 +429,21 @@ define(["jquery", "app/common", "stickytabs", "visibility",
ajaxSetup(null, true, true);
});
+ $(document).on("input", historyCountSelector, (e) => {
+ const v = parseInt($(e.currentTarget).val(), 10);
+ if (v > 0) {
+ localStorage.setItem("historyCount", v);
+ $(e.currentTarget).removeClass("is-invalid");
+ $("#history-count").val(v).trigger("change");
+ } else {
+ $(e.currentTarget).addClass("is-invalid");
+ }
+ });
+ $(document).on("click", ".popover #settings-popover #settings-history-count-restore", () => {
+ localStorage.removeItem("historyCount");
+ $(historyCountSelector).val(historyCountDef);
+ });
+
// Dismiss Bootstrap popover by clicking outside
$("body").on("click", (e) => {
$(".popover").each(function () {