mirror of https://github.com/rspamd/rspamd.git
Rapid spam filtering system
https://rspamd.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
11 KiB
269 lines
11 KiB
/*
|
|
The MIT License (MIT)
|
|
|
|
Copyright (C) 2017 Vsevolod Stakhov <vsevolod@highsecure.ru>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
|
|
/* global FooTable */
|
|
|
|
define(["jquery", "app/common", "footable"],
|
|
($, common) => {
|
|
"use strict";
|
|
const ui = {};
|
|
let altered = {};
|
|
|
|
function clear_altered() {
|
|
$("#save-alert").addClass("d-none");
|
|
altered = {};
|
|
}
|
|
|
|
function saveSymbols(server) {
|
|
$("#save-alert button").attr("disabled", true);
|
|
|
|
const values = [];
|
|
Object.entries(altered).forEach(([key, value]) => values.push({name: key, value: value}));
|
|
|
|
common.query("./savesymbols", {
|
|
success: function () {
|
|
clear_altered();
|
|
common.alertMessage("alert-modal alert-success", "Symbols successfully saved");
|
|
},
|
|
complete: () => $("#save-alert button").removeAttr("disabled"),
|
|
errorMessage: "Save symbols error",
|
|
method: "POST",
|
|
params: {
|
|
data: JSON.stringify(values),
|
|
dataType: "json",
|
|
},
|
|
server: server
|
|
});
|
|
}
|
|
|
|
function process_symbols_data(data) {
|
|
const items = [];
|
|
const lookup = {};
|
|
const freqs = [];
|
|
const distinct_groups = [];
|
|
|
|
data.forEach((group) => {
|
|
group.rules.forEach((item) => {
|
|
const formatter = new Intl.NumberFormat("en", {
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 6,
|
|
useGrouping: false
|
|
});
|
|
item.group = group.group;
|
|
let label_class = "";
|
|
if (item.weight < 0) {
|
|
label_class = "scorebar-ham";
|
|
} else if (item.weight > 0) {
|
|
label_class = "scorebar-spam";
|
|
}
|
|
item.weight = '<input class="form-control input-sm mb-disabled scorebar ' + label_class +
|
|
'" autocomplete="off" type="number" step="0.01" tabindex="1" ' +
|
|
'value="' + formatter.format(item.weight) + '" id="_sym_' + item.symbol + '"></input>';
|
|
if (!item.time) {
|
|
item.time = 0;
|
|
}
|
|
item.time = Number(item.time).toFixed(2) + "s";
|
|
if (!item.frequency) {
|
|
item.frequency = 0;
|
|
}
|
|
freqs.push(item.frequency);
|
|
item.frequency = Number(item.frequency).toFixed(2);
|
|
if (!(item.group in lookup)) {
|
|
lookup[item.group] = 1;
|
|
distinct_groups.push(item.group);
|
|
}
|
|
items.push(item);
|
|
});
|
|
});
|
|
|
|
// For better mean calculations
|
|
const avg_freq = freqs
|
|
.sort((a, b) => Number(a) < Number(b))
|
|
.reduce((f1, acc) => f1 + acc) / (freqs.length !== 0 ? freqs.length : 1.0);
|
|
let mult = 1.0;
|
|
let exp = 0.0;
|
|
|
|
if (avg_freq > 0.0) {
|
|
while (mult * avg_freq < 1.0) {
|
|
mult *= 10;
|
|
exp++;
|
|
}
|
|
}
|
|
$.each(items, (i, item) => {
|
|
item.frequency = Number(item.frequency) * mult;
|
|
|
|
if (exp > 0) {
|
|
item.frequency = item.frequency.toFixed(2) + "e-" + exp;
|
|
} else {
|
|
item.frequency = item.frequency.toFixed(2);
|
|
}
|
|
});
|
|
return [items, distinct_groups];
|
|
}
|
|
// @get symbols into modal form
|
|
ui.getSymbols = function () {
|
|
$("#refresh, #updateSymbols").attr("disabled", true);
|
|
clear_altered();
|
|
common.query("symbols", {
|
|
success: function (json) {
|
|
const [{data}] = json;
|
|
const items = process_symbols_data(data);
|
|
|
|
/* eslint-disable consistent-this, no-underscore-dangle, one-var-declaration-per-line */
|
|
FooTable.groupFilter = FooTable.Filtering.extend({
|
|
construct: function (instance) {
|
|
this._super(instance);
|
|
[,this.groups] = items;
|
|
this.def = "Any group";
|
|
this.$group = null;
|
|
},
|
|
$create: function () {
|
|
this._super();
|
|
const self = this;
|
|
const $form_grp = $("<div/>", {
|
|
class: "form-group"
|
|
}).append($("<label/>", {
|
|
class: "sr-only",
|
|
text: "Group"
|
|
})).prependTo(self.$form);
|
|
|
|
self.$group = $("<select/>", {
|
|
class: "form-select"
|
|
}).on("change", {
|
|
self: self
|
|
}, self._onStatusDropdownChanged).append(
|
|
$("<option/>", {
|
|
text: self.def
|
|
})).appendTo($form_grp);
|
|
|
|
$.each(self.groups, (i, group) => {
|
|
self.$group.append($("<option/>").text(group));
|
|
});
|
|
|
|
common.appendButtonsToFtFilterDropdown(self);
|
|
},
|
|
_onStatusDropdownChanged: function (e) {
|
|
const {self} = e.data;
|
|
const selected = $(this).val();
|
|
if (selected !== self.def) {
|
|
self.addFilter("group", selected, ["group"]);
|
|
} else {
|
|
self.removeFilter("group");
|
|
}
|
|
self.filter();
|
|
},
|
|
draw: function () {
|
|
this._super();
|
|
const group = this.find("group");
|
|
if (group instanceof FooTable.Filter) {
|
|
this.$group.val(group.query.val());
|
|
} else {
|
|
this.$group.val(this.def);
|
|
}
|
|
}
|
|
});
|
|
/* eslint-enable consistent-this, no-underscore-dangle, one-var-declaration-per-line */
|
|
|
|
common.tables.symbols = FooTable.init("#symbolsTable", {
|
|
breakpoints: common.breakpoints,
|
|
cascade: true,
|
|
columns: [
|
|
{sorted: true, direction: "ASC", name: "group", title: "Group"},
|
|
{name: "symbol", title: "Symbol"},
|
|
{name: "description", title: "Description", breakpoints: "md"},
|
|
{name: "weight", title: "Score"},
|
|
{name: "frequency",
|
|
title: "Frequency",
|
|
breakpoints: "md",
|
|
sortValue: function (value) { return Number(value).toFixed(2); }},
|
|
{name: "time", title: "Avg. time", breakpoints: "md"},
|
|
],
|
|
rows: items[0],
|
|
paging: {
|
|
enabled: true,
|
|
limit: 5,
|
|
size: 25
|
|
},
|
|
filtering: {
|
|
enabled: true,
|
|
position: "left",
|
|
connectors: false
|
|
},
|
|
sorting: {
|
|
enabled: true
|
|
},
|
|
components: {
|
|
filtering: FooTable.groupFilter
|
|
},
|
|
on: {
|
|
"ready.ft.table": function () {
|
|
if (common.read_only) {
|
|
$(".mb-disabled").attr("disabled", true);
|
|
}
|
|
},
|
|
"postdraw.ft.table":
|
|
() => $("#refresh, #updateSymbols").removeAttr("disabled")
|
|
}
|
|
});
|
|
},
|
|
error: () => $("#refresh, #updateSymbols").removeAttr("disabled"),
|
|
server: common.getServer()
|
|
});
|
|
};
|
|
|
|
|
|
$("#updateSymbols").on("click", (e) => {
|
|
e.preventDefault();
|
|
$("#refresh, #updateSymbols").attr("disabled", true);
|
|
clear_altered();
|
|
common.query("symbols", {
|
|
success: function (data) {
|
|
const [items] = process_symbols_data(data[0].data);
|
|
common.tables.symbols.rows.load(items);
|
|
},
|
|
error: () => $("#refresh, #updateSymbols").removeAttr("disabled"),
|
|
server: common.getServer()
|
|
});
|
|
});
|
|
|
|
$("#symbolsTable")
|
|
.on("input", ".scorebar", ({target}) => {
|
|
const t = $(target);
|
|
t.removeClass("scorebar-ham scorebar-spam");
|
|
if (target.value < 0) {
|
|
t.addClass("scorebar-ham");
|
|
} else if (target.value > 0) {
|
|
t.addClass("scorebar-spam");
|
|
}
|
|
})
|
|
.on("change", ".scorebar", ({target}) => {
|
|
altered[$(target).attr("id").substring(5)] = parseFloat(target.value);
|
|
$("#save-alert").removeClass("d-none");
|
|
});
|
|
|
|
$("#save-alert button")
|
|
.on("click", ({target}) => saveSymbols($(target).data("save")));
|
|
|
|
return ui;
|
|
});
|