Browse Source

Merge pull request #4849 from moisseev/webui

[WebUI] Add file input and drop area to scan tab
pull/4852/head
Vsevolod Stakhov 2 months ago
parent
commit
9483f70b87
No account linked to committer's email address
4 changed files with 94 additions and 18 deletions
  1. 2
    0
      interface/css/rspamd.css
  2. 6
    2
      interface/index.html
  3. 6
    1
      interface/js/app/libft.js
  4. 80
    15
      interface/js/app/upload.js

+ 2
- 0
interface/css/rspamd.css View File

@@ -394,6 +394,8 @@ table#symbolsTable input[type="number"] {
text-align: center;
}

.outline-dashed-primary { outline: 2px dashed var(--bs-primary); }

.scorebar-spam {
background-color: rgba(240 0 0 / 0.1) !important;
}

+ 6
- 2
interface/index.html View File

@@ -378,16 +378,20 @@

<div class="tab-pane" id="scan">
<div class="card bg-light shadow my-3">
<div class="card-header text-secondary py-2">
<div class="card-header text-secondary py-1 d-flex align-items-center">
<span class="icon me-3"><i class="fas fa-envelope"></i></span>
<span class="h6 fw-bolder my-2">Scan suspected message</span>
<div class="d-flex input-group-sm align-items-center ms-auto">
<label for="formFile" class="col-auto col-form-label-sm me-1">Choose a file:</label>
<input class="form-control form-control-sm btn btn-secondary" id="formFile" type="file">
</div>
</div>
<div class="card-body">
<div class="row">
<form class="col-lg-12" id="scanForm">
<div class="mb-0">
<label class="form-label" for="scanMsgSource">Message source:</label>
<textarea class="form-control" id="scanMsgSource" rows="10" placeholder="Paste raw message source"></textarea>
<textarea class="form-control" id="scanMsgSource" rows="10" placeholder='Paste raw message source, drag and drop files here or use "Browse..." button.'></textarea>
</div>
<div class="collapse row mt-3" id="scanOptions">
<div class="col-lg-6">

+ 6
- 1
interface/js/app/libft.js View File

@@ -62,6 +62,11 @@ define(["jquery", "app/common", "footable"],
wordBreak: "break-all",
whiteSpace: "normal"
}
}, {
name: "file",
title: "File name",
breakpoints: "xs",
sortValue: (val) => ((typeof val === "undefined") ? "" : val)
}, {
name: "ip",
title: "IP address",
@@ -171,7 +176,7 @@ define(["jquery", "app/common", "footable"],
}].filter((col) => {
switch (table) {
case "history":
return true;
return (col.name !== "file");
case "scan":
return ["ip", "sender_mime", "rcpt_mime_short", "rcpt_mime", "subject", "size", "user"]
.every((name) => col.name !== name);

+ 80
- 15
interface/js/app/upload.js View File

@@ -28,6 +28,9 @@ define(["jquery", "app/common", "app/libft"],
($, common, libft) => {
"use strict";
const ui = {};
let files = null;
let filesIdx = null;
let scanTextHeaders = {};

function cleanTextUpload(source) {
$("#" + source + "TextSource").val("");
@@ -77,7 +80,19 @@ define(["jquery", "app/common", "app/libft"],
.prop("disabled", (disable || $.trim($("textarea").val()).length === 0));
}

function scanText(data, headers) {
function setFileInputFiles(i) {
const dt = new DataTransfer();
if (arguments.length) dt.items.add(files[i]);
$("#formFile").prop("files", dt.files);
}

function readFile(callback, i) {
const reader = new FileReader();
reader.readAsText(files[(arguments.length === 1) ? 0 : i]);
reader.onload = () => callback(reader.result);
}

function scanText(data) {
enable_disable_scan_btn(true);
common.query("checkv2", {
data: data,
@@ -85,7 +100,7 @@ define(["jquery", "app/common", "app/libft"],
processData: false,
},
method: "POST",
headers: headers,
headers: scanTextHeaders,
success: function (neighbours_status) {
const json = neighbours_status[0].data;
if (json.action) {
@@ -95,17 +110,29 @@ define(["jquery", "app/common", "app/libft"],
const {items} = o;
common.symbols.scan.push(o.symbols[0]);

if (files) items[0].file = files[filesIdx].name;

if (Object.prototype.hasOwnProperty.call(common.tables, "scan")) {
common.tables.scan.rows.load(items, true);
} else {
require(["footable"], () => {
libft.initHistoryTable(data, items, "scan", libft.columns_v2("scan"), true,
() => {
enable_disable_scan_btn();
$("#cleanScanHistory").removeAttr("disabled");
$("html, body").animate({
scrollTop: $("#scanResult").offset().top
}, 1000);
if (files && filesIdx < files.length - 1) {
readFile((result) => {
if (filesIdx === files.length - 1) {
$("#scanMsgSource").val(result);
setFileInputFiles(filesIdx);
}
scanText(result);
}, ++filesIdx);
} else {
enable_disable_scan_btn();
$("#cleanScanHistory").removeAttr("disabled");
$("html, body").animate({
scrollTop: $("#scanResult").offset().top
}, 1000);
}
});
});
}
@@ -180,6 +207,10 @@ define(["jquery", "app/common", "app/libft"],
enable_disable_scan_btn();
$("textarea").on("input", () => {
enable_disable_scan_btn();
if (files) {
files = null;
setFileInputFiles();
}
});

$("#scanClean").on("click", () => {
@@ -193,22 +224,26 @@ define(["jquery", "app/common", "app/libft"],
$(this).closest(".card").slideUp();
});

function getScanTextHeaders() {
scanTextHeaders = ["IP", "User", "From", "Rcpt", "Helo", "Hostname"].reduce((o, header) => {
const value = $("#scan-opt-" + header.toLowerCase()).val();
if (value !== "") o[header] = value;
return o;
}, {});
if ($("#scan-opt-pass-all").prop("checked")) scanTextHeaders.Pass = "all";
}

$("[data-upload]").on("click", function () {
const source = $(this).data("upload");
const data = $("#scanMsgSource").val();
let headers = {};
if ($.trim(data).length > 0) {
if (source === "scan") {
headers = ["IP", "User", "From", "Rcpt", "Helo", "Hostname"].reduce((o, header) => {
const value = $("#scan-opt-" + header.toLowerCase()).val();
if (value !== "") o[header] = value;
return o;
}, {});
if ($("#scan-opt-pass-all").prop("checked")) headers.Pass = "all";
scanText(data, headers);
getScanTextHeaders();
scanText(data);
} else if (source === "compute-fuzzy") {
getFuzzyHashes(data);
} else {
let headers = {};
if (source === "fuzzy") {
headers = {
flag: $("#fuzzyFlagText").val(),
@@ -223,5 +258,35 @@ define(["jquery", "app/common", "app/libft"],
return false;
});

const dragoverClassList = "outline-dashed-primary bg-primary-subtle";
$("#scanMsgSource")
.on("dragenter dragover dragleave drop", (e) => {
e.preventDefault();
e.stopPropagation();
})
.on("dragenter dragover", () => {
$("#scanMsgSource").addClass(dragoverClassList);
})
.on("dragleave drop", () => {
$("#scanMsgSource").removeClass(dragoverClassList);
})
.on("drop", (e) => {
({files} = e.originalEvent.dataTransfer);
filesIdx = 0;

if (files.length === 1) {
setFileInputFiles(0);
enable_disable_scan_btn();
readFile((result) => {
$("#scanMsgSource").val(result);
enable_disable_scan_btn();
});
// eslint-disable-next-line no-alert
} else if (files.length < 10 || confirm("Are you sure you want to scan " + files.length + " files?")) {
getScanTextHeaders();
readFile((result) => scanText(result));
}
});

return ui;
});

Loading…
Cancel
Save