HEX
Server: Apache/2
System: Linux nexus-01 4.18.0-553.120.1.el8_10.x86_64 #1 SMP Mon Apr 20 18:04:27 EDT 2026 x86_64
User: aglcoke (1118)
PHP: 8.2.31
Disabled: mail,exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Upload Files
File: //proc/1/task/1/root/proc/1/root/usr/share/rspamd/www/js/app/history.js
/*
 * Copyright (C) 2017 Vsevolod Stakhov <vsevolod@highsecure.ru>
 */

/* global FooTable */

define(["jquery", "app/common", "app/libft", "footable"],
    ($, common, libft) => {
        "use strict";
        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 = [];

            function compare(e1, e2) { return e1.name.localeCompare(e2.name); }

            common.hide("#selSymOrder_history, label[for='selSymOrder_history']");

            $.each(data, (i, item) => {
                item.time = libft.unix_time_format(item.unix_time);
                libft.preprocess_item(item);
                item.symbols = Object.keys(item.symbols)
                    .map((key) => item.symbols[key])
                    .sort(compare)
                    .map((e) => e.name)
                    .join(", ");
                item.time = {
                    value: libft.unix_time_format(item.unix_time),
                    options: {
                        sortValue: item.unix_time
                    }
                };

                items.push(item);
            });

            return {items: items};
        }

        function columns_legacy() {
            return [{
                name: "id",
                title: "ID",
                style: {
                    width: 300,
                    maxWidth: 300,
                    overflow: "hidden",
                    textOverflow: "ellipsis",
                    wordBreak: "keep-all",
                    whiteSpace: "nowrap"
                }
            }, {
                name: "ip",
                title: "IP address",
                breakpoints: "md",
                style: {width: 150, maxWidth: 150}
            }, {
                name: "action",
                title: "Action",
                style: {width: 110, maxWidth: 110}
            }, {
                name: "score",
                title: "Score",
                style: {maxWidth: 110},
                sortValue: (val) => Number(val.options.sortValue)
            }, {
                name: "symbols",
                title: "Symbols",
                breakpoints: "all",
                style: {width: 550, maxWidth: 550}
            }, {
                name: "size",
                title: "Message size",
                breakpoints: "md",
                style: {width: 120, maxWidth: 120},
                formatter: libft.formatBytesIEC
            }, {
                name: "scan_time",
                title: "Scan time",
                breakpoints: "md",
                style: {maxWidth: 80},
                sortValue: (val) => Number(val)
            }, {
                sorted: true,
                direction: "DESC",
                name: "time",
                title: "Time",
                sortValue: (val) => Number(val.options.sortValue)
            }, {
                name: "user",
                title: "Authenticated user",
                breakpoints: "md",
                style: {width: 200, maxWidth: 200}
            }];
        }

        const columns = {
            2: libft.columns_v2("history"),
            legacy: columns_legacy()
        };

        function process_history_data(data) {
            const process_functions = {
                2: libft.process_history_v2,
                legacy: process_history_legacy
            };
            let pf = process_functions.legacy;

            if (data.version) {
                const strkey = data.version.toString();
                if (process_functions[strkey]) {
                    pf = process_functions[strkey];
                }
            }

            return pf(data, "history");
        }

        function get_history_columns(data) {
            let func = columns.legacy;

            if (data.version) {
                const strkey = data.version.toString();
                if (columns[strkey]) {
                    func = columns[strkey];
                }
            }

            return func;
        }

        ui.getHistory = function () {
            $("#refresh, #updateHistory").attr("disabled", true);
            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);
                        if (dv) {
                            common.logError({
                                server: "Multi-server",
                                endpoint: "history",
                                message: "Neighbours history backend versions do not match. Cannot display history.",
                                errorType: "data_inconsistency"
                            });
                            return true;
                        }
                        return false;
                    }

                    const neighbours_data = req_data
                        .filter((d) => d.status) // filter out unavailable neighbours
                        .map((d) => d.data);
                    if (neighbours_data.length && !differentVersions(neighbours_data)) {
                        let data = {};
                        const [{version}] = neighbours_data;
                        if (version) {
                            data.rows = [].concat.apply([], neighbours_data
                                .map((e) => e.rows));
                            data.version = version;
                            common.hide("#legacy-history-badge");
                        } else {
                            // Legacy version
                            data = [].concat.apply([], neighbours_data);
                            common.show("#legacy-history-badge");
                        }
                        const o = process_history_data(data);
                        const {items} = o;
                        common.symbols.history = o.symbols;

                        if (Object.prototype.hasOwnProperty.call(common.tables, "history") &&
                            version === prevVersion) {
                            common.tables.history.rows.load(items);
                        } else {
                            libft.destroyTable("history").then(() => {
                                libft.initHistoryTable(data, items, "history", get_history_columns(data), false,
                                    () => {
                                        $("#history .ft-columns-dropdown .btn-dropdown-apply").removeAttr("disabled");
                                        ui.updateHistoryControlsState();
                                        if (version) libft.bindFuzzyHashButtons("history");
                                    });
                            });
                        }
                        prevVersion = version;
                    } else {
                        libft.destroyTable("history");
                    }
                },
                error: () => ui.updateHistoryControlsState(),
                errorMessage: "Cannot receive history",
            });
        };

        function initErrorsTable(rows) {
            common.tables.errors = FooTable.init("#errorsLog", {
                breakpoints: common.breakpoints,
                cascade: true,
                columns: [
                    {sorted: true,
                        direction: "DESC",
                        name: "ts",
                        title: "Time",
                        style: {width: 300, maxWidth: 300},
                        sortValue: (val) => Number(val.options.sortValue)},
                    {name: "type",
                        title: "Worker type",
                        breakpoints: "md",
                        style: {width: 150, maxWidth: 150}},
                    {name: "pid",
                        title: "PID",
                        breakpoints: "md",
                        style: {width: 110, maxWidth: 110}},
                    {name: "module", title: "Module"},
                    {name: "id", title: "Internal ID"},
                    {name: "message", title: "Message", breakpoints: "md"},
                ],
                rows: rows,
                paging: {
                    enabled: true,
                    limit: 5,
                    size: common.page_size.errors
                },
                filtering: {
                    enabled: true,
                    position: "left",
                    connectors: false
                },
                sorting: {
                    enabled: true
                }
            });
        }

        ui.getErrors = function () {
            if (common.read_only) return;

            common.query("errors", {
                success: function (data) {
                    const neighbours_data = data
                        .filter((d) => d.status) // filter out unavailable neighbours
                        .map((d) => d.data);
                    const rows = [].concat.apply([], neighbours_data);
                    $.each(rows, (i, item) => {
                        item.ts = {
                            value: libft.unix_time_format(item.ts),
                            options: {
                                sortValue: item.ts
                            }
                        };
                        for (const prop in item) {
                            if (!{}.hasOwnProperty.call(item, prop)) continue;
                            if (typeof item[prop] === "string") item[prop] = common.escapeHTML(item[prop]);
                        }
                    });
                    if (Object.prototype.hasOwnProperty.call(common.tables, "errors")) {
                        common.tables.errors.rows.load(rows);
                    } else {
                        initErrorsTable(rows);
                    }
                }
            });

            $("#updateErrors").off("click");
            $("#updateErrors").on("click", (e) => {
                e.preventDefault();
                ui.getErrors();
            });
        };

        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", 9);

        $("#updateHistory").off("click");
        $("#updateHistory").on("click", (e) => {
            e.preventDefault();
            ui.getHistory();
        });

        // @reset history log
        $("#resetHistory").off("click");
        $("#resetHistory").on("click", (e) => {
            e.preventDefault();
            if (!confirm("Are you sure you want to reset history log?")) { // eslint-disable-line no-alert
                return;
            }
            libft.destroyTable("history");
            libft.destroyTable("errors");

            common.query("historyreset", {
                success: function () {
                    ui.getHistory();
                    ui.getErrors();
                },
                errorMessage: "Cannot reset history log"
            });
        });

        return ui;
    });