305 lines
8.4 KiB
JavaScript
305 lines
8.4 KiB
JavaScript
|
/**
|
||
|
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
|
||
|
* Copyright (C) 2020 Aleksey Andreev (liuch)
|
||
|
*
|
||
|
* Available at:
|
||
|
* https://github.com/liuch/dmarc-srg
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify it
|
||
|
* under the terms of the GNU General Public License as published by the Free
|
||
|
* Software Foundation, either version 3 of the License.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||
|
* more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License along with
|
||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
class Logs {
|
||
|
constructor() {
|
||
|
this._table = null;
|
||
|
this._scroll = null;
|
||
|
this._element = document.getElementById("main-block");
|
||
|
this._fetching = false;
|
||
|
this._sort = { column: "", direction: "" };
|
||
|
}
|
||
|
|
||
|
display() {
|
||
|
this._make_scroll_container();
|
||
|
this._make_table();
|
||
|
this._scroll.appendChild(this._table.element());
|
||
|
this._element.appendChild(this._scroll);
|
||
|
this._table.focus();
|
||
|
}
|
||
|
|
||
|
update() {
|
||
|
this._table.clear();
|
||
|
let that = this;
|
||
|
let fr_cnt = -1;
|
||
|
let again = function() {
|
||
|
let fc = that._table.frames_count()
|
||
|
if (fr_cnt < fc && that._scroll.scrollHeight <= that._scroll.clientHeight * 1.5) {
|
||
|
fr_cnt = fc;
|
||
|
that._fetch_list().then(function(frame) {
|
||
|
if (frame && frame.more()) {
|
||
|
again();
|
||
|
}
|
||
|
else {
|
||
|
that._table.focus();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
that._table.focus();
|
||
|
}
|
||
|
};
|
||
|
again();
|
||
|
}
|
||
|
|
||
|
title() {
|
||
|
return "Logs";
|
||
|
}
|
||
|
|
||
|
_fetch_list() {
|
||
|
this._table.display_status("wait");
|
||
|
this._fetching = true;
|
||
|
|
||
|
let pos = this._table.last_row_index() + 1;
|
||
|
|
||
|
let uparams = new URLSearchParams();
|
||
|
uparams.set("position", pos);
|
||
|
if (this._sort.column && this._sort.direction) {
|
||
|
uparams.set("order", this._sort.column);
|
||
|
uparams.set("direction", this._sort.direction);
|
||
|
}
|
||
|
|
||
|
let that = this;
|
||
|
return window.fetch("logs.php?" + uparams.toString(), {
|
||
|
method: "GET",
|
||
|
cache: "no-store",
|
||
|
headers: HTTP_HEADERS,
|
||
|
credentials: "same-origin"
|
||
|
}).then(function(resp) {
|
||
|
if (!resp.ok)
|
||
|
throw new Error("Failed to fetch the logs");
|
||
|
return resp.json();
|
||
|
}).then(function(data) {
|
||
|
that._table.display_status(null);
|
||
|
Common.checkResult(data);
|
||
|
if (data.sorted_by) {
|
||
|
let cname = data.sorted_by.column;
|
||
|
let dir = data.sorted_by.direction;
|
||
|
if (that._sort.column !== cname || that._sort.direction !== dir) {
|
||
|
that._sort.column = cname;
|
||
|
that._sort.direction = dir;
|
||
|
that._table.set_sorted(cname, dir);
|
||
|
}
|
||
|
}
|
||
|
let d = { more: data.more };
|
||
|
d.rows = data.items.map(function(it) {
|
||
|
return new ITableRow(that._make_row_data(it));
|
||
|
});
|
||
|
let fr = new ITableFrame(d, pos);
|
||
|
that._table.add_frame(fr);
|
||
|
return fr;
|
||
|
}).catch(function(err) {
|
||
|
Common.displayError(err);
|
||
|
that._table.display_status("error");
|
||
|
}).finally(function() {
|
||
|
that._fetching = false;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_make_scroll_container() {
|
||
|
let that = this;
|
||
|
let el = document.createElement("div");
|
||
|
el.setAttribute("class", "main-table-container");
|
||
|
el.addEventListener("scroll", function() {
|
||
|
if (!that._fetching && el.scrollTop + el.clientHeight >= el.scrollHeight * 0.95) {
|
||
|
if (that._table.frames_count() === 0 || that._table.more()) {
|
||
|
that._fetch_list();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
this._scroll = el;
|
||
|
}
|
||
|
|
||
|
_make_table() {
|
||
|
this._table = new ITable({
|
||
|
class: "main-table small-cards",
|
||
|
onclick: function(row) {
|
||
|
let data = row.userdata();
|
||
|
if (data) {
|
||
|
this._display_item_dialog(data);
|
||
|
}
|
||
|
}.bind(this),
|
||
|
onsort: function(col) {
|
||
|
let dir = col.sorted() && "toggle" || "descent";
|
||
|
this._table.set_sorted(col.name(), dir);
|
||
|
this._sort.column = col.name();
|
||
|
this._sort.direction = col.sorted();
|
||
|
this.update();
|
||
|
}.bind(this),
|
||
|
onfocus: function(el) {
|
||
|
scroll_to_element(el, this._scroll);
|
||
|
}.bind(this)
|
||
|
});
|
||
|
[
|
||
|
{ content: "", class: "cell-status" },
|
||
|
{ content: "Domain", name: "domain" },
|
||
|
{ content: "Source" },
|
||
|
{ content: "Event time", sortable: true, name: "event_time" },
|
||
|
{ content: "Message" }
|
||
|
].forEach(function(col) {
|
||
|
let c = this._table.add_column(col);
|
||
|
if (c.name() === this._sort.column) {
|
||
|
c.sort(this._sort.direction);
|
||
|
}
|
||
|
}, this);
|
||
|
}
|
||
|
|
||
|
_make_row_data(d) {
|
||
|
let rd = { cells: [], userdata: { id: d.id } };
|
||
|
rd.cells.push(new LogsResultCell(d.success));
|
||
|
rd.cells.push({ content: d.domain, label: "Domain" });
|
||
|
rd.cells.push({ content: d.source, label: "Source" });
|
||
|
rd.cells.push({ content: (new Date(d.event_time)).toUIString(), label: "Event time" });
|
||
|
rd.cells.push({ content: d.message, label: "Message" });
|
||
|
return rd;
|
||
|
}
|
||
|
|
||
|
_display_item_dialog(data) {
|
||
|
let dlg = new LogItemDialog(data);
|
||
|
this._element.appendChild(dlg.element());
|
||
|
let that = this;
|
||
|
dlg.show().finally(function() {
|
||
|
dlg.element().remove();
|
||
|
that._table.focus();
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class LogsResultCell extends ITableCell {
|
||
|
constructor(success, props) {
|
||
|
props = props || {};
|
||
|
let ca = (props.class || "").split(" ");
|
||
|
ca.push(success && "state-green" || "state-red");
|
||
|
props.class = ca.filter(function(s) { return s.length > 0; }).join(" ");
|
||
|
super(success, props);
|
||
|
}
|
||
|
|
||
|
element() {
|
||
|
if (!this._element) {
|
||
|
super.element().setAttribute("data-label", "Result");
|
||
|
}
|
||
|
return this._element;
|
||
|
}
|
||
|
|
||
|
value(target) {
|
||
|
if (target === "dom") {
|
||
|
let div = document.createElement("div");
|
||
|
div.setAttribute("class", "state-background status-indicator");
|
||
|
if (!this.title) {
|
||
|
div.setAttribute("title", this._content && "Ok" || "Failed");
|
||
|
}
|
||
|
return div;
|
||
|
}
|
||
|
return this._content;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class LogItemDialog extends ModalDialog {
|
||
|
constructor(data) {
|
||
|
super({ title: "Log record", buttons: [ "close" ] });
|
||
|
this._data = data;
|
||
|
this._table = null;
|
||
|
this._res_el = null;
|
||
|
this._dom_el = null;
|
||
|
this._time_el = null; // event_time
|
||
|
this._rid_el = null; // external_id
|
||
|
this._file_el = null; // filename
|
||
|
this._sou_el = null; // source
|
||
|
this._msg_el = null; // message
|
||
|
}
|
||
|
|
||
|
_gen_content() {
|
||
|
this._table = document.createElement("div");
|
||
|
this._table.setAttribute("class", "left-titled");
|
||
|
this._content.appendChild(this._table);
|
||
|
|
||
|
this._time_el = this._insert_row("Event time");
|
||
|
this._res_el = this._insert_row("Result");
|
||
|
this._res_el.setAttribute("class", "state-text");
|
||
|
this._dom_el = this._insert_row("Domain");
|
||
|
this._rid_el = this._insert_row("Report Id");
|
||
|
this._file_el = this._insert_row("File name");
|
||
|
this._sou_el = this._insert_row("Source");
|
||
|
this._msg_el = this._insert_row("Message");
|
||
|
|
||
|
this._fetch_data();
|
||
|
}
|
||
|
|
||
|
_insert_row(text) {
|
||
|
let t_el = document.createElement("span");
|
||
|
t_el.appendChild(document.createTextNode(text + ": "));
|
||
|
this._table.appendChild(t_el);
|
||
|
let v_el = document.createElement("span");
|
||
|
this._table.appendChild(v_el);
|
||
|
return v_el;
|
||
|
}
|
||
|
|
||
|
_fetch_data() {
|
||
|
this._content.appendChild(set_wait_status());
|
||
|
let uparams = new URLSearchParams();
|
||
|
uparams.set("id", this._data.id);
|
||
|
|
||
|
let that = this;
|
||
|
window.fetch("logs.php?" + uparams.toString(), {
|
||
|
method: "GET",
|
||
|
cache: "no-store",
|
||
|
headers: HTTP_HEADERS,
|
||
|
credentials: "same-origin"
|
||
|
}).then(function(resp) {
|
||
|
if (!resp.ok)
|
||
|
throw new Error("Failed to fetch the log item");
|
||
|
return resp.json();
|
||
|
}).then(function(data) {
|
||
|
Common.checkResult(data);
|
||
|
that._data.domain = data.domain;
|
||
|
that._data.report_id = data.report_id;
|
||
|
that._data.event_time = new Date(data.event_time);
|
||
|
that._data.filename = data.filename;
|
||
|
that._data.source = data.source;
|
||
|
that._data.success = data.success;
|
||
|
that._data.message = data.message;
|
||
|
that._update_ui();
|
||
|
}).catch(function(err) {
|
||
|
Common.displayError(err);
|
||
|
that._content.appendChild(set_error_status(null, err.message));
|
||
|
}).finally(function() {
|
||
|
that._content.querySelector(".wait-message").remove();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_update_ui() {
|
||
|
this._time_el.textContent = this._data.event_time.toUIString();
|
||
|
if (this._data.success) {
|
||
|
this._res_el.textContent = "Ok";
|
||
|
this._res_el.parentElement.classList.add("state-green");
|
||
|
}
|
||
|
else {
|
||
|
this._res_el.textContent = "Failed";
|
||
|
this._res_el.parentElement.classList.add("state-red");
|
||
|
}
|
||
|
this._dom_el.textContent = this._data.domain || "n/a";
|
||
|
this._rid_el.textContent = this._data.report_id || "n/a";
|
||
|
this._file_el.textContent = this._data.filename || "n/a";
|
||
|
this._sou_el.textContent = this._data.source;
|
||
|
this._msg_el.textContent = this._data.message || "n/a";
|
||
|
}
|
||
|
}
|
||
|
|