<!--
Copyright (C) 2024 Nicola Murino

This WebUI uses the KeenThemes Mega Bundle, a proprietary theme:

https://keenthemes.com/products/templates-mega-bundle

KeenThemes HTML/CSS/JS components are allowed for use only within the
SFTPGo product and restricted to be used in a resealable HTML template
that can compete with KeenThemes products anyhow.

This WebUI is allowed for use only within the SFTPGo product and
therefore cannot be used in derivative works/products without an
explicit grant from the SFTPGo Team (support@sftpgo.com).
-->
{{template "base" .}}

{{- define "extra_css"}}
<link href="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.css" rel="stylesheet" type="text/css"/>
{{- end}}

{{- define "page_body"}}
{{- template "errmsg" ""}}
<div class="card shadow-sm">
    <div class="card-header bg-light">
        <h3 data-i18n="events.search" class="card-title section-title">Search logs</h3>
    </div>
    <div id="card_body" class="card-body">
        <div class="form-group row">
            <div class="col-md-3 mt-5">
                <select class="form-select" id="idEventType" name="events_type" data-control="i18n-select2" data-hide-search="true">
                    <option value="1" data-i18n="events.fs_events">Fs events</option>
                    <option value="2" data-i18n="events.provider_events">Provider events</option>
                    <option value="3" data-i18n="events.other_events">Other events</option>
                </select>
            </div>
            <div class="col-md-4 mt-5">
                <select class="form-select" id="idActions" name="actions" data-control="i18n-select2" data-hide-search="true"
                    data-close-on-select="false" data-i18n="[data-placeholder]general.actions" multiple>
                </select>
            </div>
            <div class="col-md-3 mt-5">
                <input type="text" class="form-control" id="idUsername" name="username" data-i18n="[placeholder]login.username" spellcheck="false">
            </div>
            <div class="col-md-2 mt-5">
                <input type="text" class="form-control" id="idIp" name="ip" data-i18n="[placeholder]defender.ip">
            </div>
        </div>
        <div class="form-group row">
            <div class="col-md-3 mt-5">
                <select class="form-select fs-events" id="idStatuses" name="statuses" data-control="i18n-select2" data-hide-search="true"
                    data-close-on-select="false" data-i18n="[data-placeholder]general.status" multiple>
                    <option value="1" data-i18n="general.ok">OK</option>
                    <option value="2" data-i18n="general.failed">KO</option>
                    <option value="3" data-i18n="events.quota_exceeded">Quota exceeded</option>
                </select>
            </div>
            <div class="col-md-4 mt-5">
                <select class="form-select fs-events log-events" id="idProtocols" name="protocols" data-control="i18n-select2" data-hide-search="true"
                    data-close-on-select="false" data-i18n="[data-placeholder]ip_list.protocols" multiple>
                    <option value="SFTP">SFTP</option>
                    <option value="SCP">SCP</option>
                    <option value="SSH">SSH</option>
                    <option value="FTP">FTP</option>
                    <option value="DAV">DAV</option>
                    <option value="HTTP">HTTP</option>
                    <option value="OIDC">OIDC</option>
                    <option value="HTTPShare">HTTPShare</option>
                    <option value="DataRetention">DataRetention</option>
                    <option value="EventAction">EventAction</option>
                </select>
            </div>
            <div class="col-md-5 mt-5">
                <input id="dateTimeRange" class="form-control" data-i18n="[placeholder]events.date_range" />
            </div>
        </div>

        <div class="d-flex justify-content-end mt-10 mb-10">
            <button id="export_button" class="btn btn-secondary px-10 me-10">
                <span data-i18n="general.export" class="indicator-label">
                    Export
                </span>
                <span data-i18n="general.wait" class="indicator-progress">
                    Please wait...
                    <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
                </span>
            </button>
            <button id="search_button" class="btn btn-primary px-10">
                <span data-i18n="general.search" class="indicator-label">
                    Search
                </span>
                <span data-i18n="general.wait" class="indicator-progress">
                    Please wait...
                    <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
                </span>
            </button>
        </div>

        <table id="dataTableFs" class="table align-middle table-row-dashed fs-6 gy-5 fs-events">
            <thead>
                <tr class="text-start text-muted fw-bold fs-6 gs-0">
                    <th data-i18n="events.datetime">Date and time</th>
                    <th data-i18n="events.action">Action</th>
                    <th data-i18n="events.path">Path</th>
                    <th data-i18n="login.username">Username</th>
                    <th data-i18n="general.protocol">Protocol</th>
                    <th data-i18n="defender.ip">IP</th>
                    <th data-i18n="general.info">Info</th>
                </tr>
            </thead>
            <tbody id="table_body" class="text-gray-800 fw-semibold"></tbody>
        </table>

        <table id="dataTableProvider" class="table align-middle table-row-dashed fs-6 gy-5 provider-events">
            <thead>
                <tr class="text-start text-muted fw-bold fs-6 gs-0">
                    <th data-i18n="events.datetime">Date and time</th>
                    <th data-i18n="events.action">Action</th>
                    <th data-i18n="events.object">Object</th>
                    <th data-i18n="login.username">Username</th>
                    <th data-i18n="defender.ip">IP</th>
                </tr>
            </thead>
            <tbody id="table_body" class="text-gray-800 fw-semibold"></tbody>
        </table>

        <table id="dataTableLog" class="table align-middle table-row-dashed fs-6 gy-5 log-events">
            <thead>
                <tr class="text-start text-muted fw-bold fs-6 gs-0">
                    <th data-i18n="events.datetime">Date and time</th>
                    <th data-i18n="events.event">Event</th>
                    <th data-i18n="login.username">Username</th>
                    <th data-i18n="general.protocol">Protocol</th>
                    <th data-i18n="defender.ip">IP</th>
                    <th data-i18n="general.info">Info</th>
                </tr>
            </thead>
            <tbody id="table_body" class="text-gray-800 fw-semibold"></tbody>
        </table>

        <div id="paginationContainer" class="d-flex mt-4 mb-4 justify-content-end d-none">
            <div class="btn-group" role="group" aria-label="Pagination">
                <button id="pagePrevious" data-i18n="general.previous" type="button" class="btn btn-outline btn-active-primary disabled">Previous</button>
                <button id="pageNext" data-i18n="general.next" type="button" class="btn btn-outline btn-active-primary disabled">Next</button>
            </div>
        </div>

    </div>
</div>
{{- end}}

{{- define "extra_js"}}
<script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.js"></script>
<script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/assets/plugins/custom/flatpickr/l10n/it.js"></script>
<script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/vendor/humanize-duration/humanize-duration.min.js"></script>
<script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
    const pageSize = 20;
    const paginationData = new Map();

    function resetPagination() {
        $('#pagePrevious').addClass("disabled");
        $('#pageNext').addClass("disabled");
        $('#paginationContainer').addClass("d-none");
        paginationData.delete("firstId");
        paginationData.delete("firstTs");
        paginationData.delete("lastId");
        paginationData.delete("lastTs");
        paginationData.set("prevClicked",false);
        paginationData.set("nextClicked",false);
    }

    function prevClicked(){
        paginationData.set("prevClicked",true);
        paginationData.set("nextClicked",false);
        doSearch();
    }

    function nextClicked(){
        paginationData.set("prevClicked",false);
        paginationData.set("nextClicked",true);
        doSearch();
    }

    function handleResponseData(data) {
        let length = data.length;
        let isNext = paginationData.get("nextClicked");
        let isPrev = paginationData.get("prevClicked");

        if (length > pageSize) {
            data.pop();
            length--;
            if (isPrev || isNext){
                $('#pagePrevious').removeClass("disabled");
            }
            $('#pageNext').removeClass("disabled");
        } else {
            if (isPrev){
                $('#pagePrevious').addClass("disabled");
                $('#pageNext').removeClass("disabled");
            } else if (isNext){
                $('#pagePrevious').removeClass("disabled");
                $('#pageNext').addClass("disabled");
            } else {
                $('#pageNext').addClass("disabled");
            }
        }
        if (isPrev){
            data = data.reverse();
        }
        if (length > 0){
            paginationData.set("lastId",data[0].id);
            paginationData.set("lastTs",data[0].timestamp);
            paginationData.set("firstId",data[length-1].id);
            paginationData.set("firstTs",data[length-1].timestamp);
            $('#paginationContainer').removeClass("d-none");
        } else {
            resetPagination();
        }
        return data;
    }

    function humanizeMilliseconds(val) {
        let units = ["d", "h", "m", "s", "ms"];
        let decimalPoints = 1;
        if (val > 1000){
            units = ["d", "h", "m", "s"]
        }
        if (val > 60000){
            decimalPoints = 0;
        }
        return humanizeDuration(val, {
            language: i18next.resolvedLanguage,
            fallbacks: ["en"],
            maxDecimalPoints: decimalPoints,
            units: units
        })
    }

    function getSearchURL(csvExport) {
        let url = "";
        let eventType = $('#idEventType').val();
        let order = "DESC";
        let limit = pageSize + 1;
        if (csvExport){
            order = "ASC";
        }
        if (eventType == 1){
            url = "{{.FsEventsSearchURL}}?limit="+limit;
            let protocols = [];
            $('#idProtocols').find('option:selected').each(function(){
                protocols.push($(this).val());
            });
            if (protocols.length > 0){
                url+="&protocols="+encodeURIComponent(String(protocols));
            }
            let statuses = [];
            $('#idStatuses').find('option:selected').each(function(){
                statuses.push($(this).val());
            });
            if (statuses.length > 0){
                url+="&statuses="+encodeURIComponent(String(statuses));
            }
        } else if (eventType == 2) {
            url = "{{.ProviderEventsSearchURL}}?omit_object_data=true&limit="+limit;
        } else {
            url = "{{.LogEventsSearchURL}}?limit="+limit;
            let protocols = [];
            $('#idProtocols').find('option:selected').each(function(){
                protocols.push($(this).val());
            });
            if (protocols.length > 0){
                url+="&protocols="+encodeURIComponent(String(protocols));
            }
        }
        let actions = [];
        $('#idActions').find('option:selected').each(function(){
            actions.push($(this).val());
        });
        if (actions.length > 0){
            if (eventType == 3){
                url+="&events="+encodeURIComponent(String(actions));
            } else {
                url+="&actions="+encodeURIComponent(String(actions));
            }
        }
        let username = $('#idUsername').val();
        if (username){
            url+="&username="+encodeURIComponent(username);
        }
        let ip = $('#idIp').val();
        if (ip){
            url+="&ip="+encodeURIComponent(ip);
        }
        const dateRangePicker = document.querySelector("#dateTimeRange")._flatpickr;
        let drp = dateRangePicker.selectedDates;
        let fromID = "";
        let start_ts = 0;
        if (!csvExport && paginationData.get("prevClicked") && paginationData.has("lastId") && paginationData.has("lastTs")){
            order = "ASC";
            start_ts = paginationData.get("lastTs");
            fromID = paginationData.get("lastId");
        } else {
            if (drp.length > 0){
                let d = drp[0];
                if (d) {
                    start_ts = d.getTime()*1000000;
                }
            }
        }
        let end_ts = 0;
        if (!csvExport && paginationData.get("nextClicked") && paginationData.has("firstId") && paginationData.has("firstTs")){
            end_ts = paginationData.get("firstTs");
            fromID = paginationData.get("firstId");
        } else {
            if (drp.length > 1){
                let d = drp[1];
                if (d) {
                    end_ts = d.getTime()*1000000;
                }
            }
        }
        url+="&start_timestamp="+encodeURIComponent(start_ts);
        url+="&end_timestamp="+encodeURIComponent(end_ts);
        if (fromID){
            url+="&from_id="+encodeURIComponent(fromID);
        }
        url+="&order="+order;
        if (csvExport){
            url+="&csv_export=true";
        }
        return url;
    }

    function onExportClicked() {
        paginationData.set("prevClicked",false);
        paginationData.set("nextClicked",false);
        let exportURL = getSearchURL(true);
        let ts = new Date().getTime().toString();
        window.open(`${exportURL}&_=${ts}`);
    }

    function onSearchClicked() {
        resetPagination();
        doSearch();
    }

    function doSearch() {
        let eventType = $('#idEventType').val();
        switch (eventType){
            case "1":
                datatableFsEvents.init();
                return;
            case "2":
                datatableProviderEvents.init();
                return;
            case "3":
                datatableLogEvents.init();
                return;
            default:
                console.log(`unsupported event type "${eventType}"`);
        }
    }

    function selectFsEvents(){
        let idActions = $('#idActions');
        idActions.empty();
        idActions.append(new Option($.t('events.upload'),"upload",false,false));
        idActions.append(new Option($.t('events.download'),"download",false,false));
        idActions.append(new Option($.t('events.mkdir'),"mkdir",false,false));
        idActions.append(new Option($.t('events.rmdir'),"rmdir",false,false));
        idActions.append(new Option($.t('events.rename'),"rename",false,false));
        idActions.append(new Option($.t('events.delete'),"delete",false,false));
        idActions.append(new Option($.t('events.first_upload'),"first-upload",false,false));
        idActions.append(new Option($.t('events.first_download'),"first-download",false,false));
        idActions.append(new Option($.t('events.ssh_cmd'),"ssh_cmd",false,false));
        idActions.trigger('change');
        $('#idUsername').val("");
        $('#idIp').val("");
        $('.provider-events').hide();
        $('.log-events').hide();
        $('.fs-events').show();
        onSearchClicked();
    }

    function selectLogEvents(){
        let idActions = $('#idActions');
        idActions.empty();
        idActions.append(new Option($.t('events.login_ok'),"5",false,false));
        idActions.append(new Option($.t('events.login_failed'),"1",false,false));
        idActions.append(new Option($.t('events.login_missing_user'),"2",false,false));
        idActions.append(new Option($.t('events.no_login_tried'),"3",false,false));
        idActions.append(new Option($.t('events.algo_negotiation_failed'),"4",false,false));
        idActions.trigger('change');
        $('#idUsername').val("");
        $('#idIp').val("");
        $('.provider-events').hide();
        $('.fs-events').hide();
        $('.log-events').show();
        onSearchClicked();
    }

    function selectProviderEvents(){
        let idActions = $('#idActions');
        idActions.empty();
        idActions.append(new Option($.t('events.add'),"add",false,false));
        idActions.append(new Option($.t('events.update'),"update",false,false));
        idActions.append(new Option($.t('events.delete'),"delete",false,false));
        idActions.trigger('change');
        $('#idUsername').val("");
        $('#idIp').val("");
        $('.fs-events').hide();
        $('.log-events').hide();
        $('.provider-events').show();
        onSearchClicked();
    }

    function onEventChanged(val){
        switch (val){
            case '1':
                selectFsEvents();
                break;
            case '2':
                selectProviderEvents();
                break;
            case '3':
                selectLogEvents();
                break;
            default:
                console.log(`unsupported event type: ${val}`);
        }
    }

    var datatableFsEvents = function(){
        var dt;

        var initDatatable = function () {
            if (dt){
                dt.clear().draw();
                dt.ajax.url(getSearchURL(false)).load();
                return;
            }
            $('#errorMsg').addClass("d-none");
            dt = $('#dataTableFs').DataTable({
                ajax: {
                    url: getSearchURL(false),
                    dataSrc: handleResponseData,
                    error: function ($xhr, textStatus, errorThrown) {
                        $(".dt-processing").hide();
                        let txt = "";
                        if ($xhr) {
                            let json = $xhr.responseJSON;
                            if (json) {
                                if (json.message){
                                    txt = json.message;
                                }
                            }
                        }
                        if (!txt){
                            txt = "general.error500";
                        }
                        setI18NData($('#errorTxt'), txt);
                        $('#errorMsg').removeClass("d-none");
                    }
                },
                columns: [
                    {
                        data: "timestamp",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (data){
                                    let ts = data/1000000;
                                    if (ts){
                                        return $.t('general.datetime', {
                                            val: new Date(ts),
                                            formatParams: {
                                                val: { year: '2-digit', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
                                            }
                                        });
                                    }
                                }
                                return "";
                            }
                            return data;
                        }
                    },
                    {
                        data: "action",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                switch (data){
                                    case "upload":
                                        return  $.t('events.upload');
                                    case "download":
                                        return  $.t('events.download');
                                    case "mkdir":
                                        return  $.t('events.mkdir');
                                    case "rmdir":
                                        return  $.t('events.rmdir');
                                    case "rename":
                                        return  $.t('events.rename');
                                    case "delete":
                                        return  $.t('events.delete');
                                    case "first-upload":
                                        return  $.t('events.first_upload');
                                    case "first-download":
                                        return  $.t('events.first_download');
                                    case "ssh_cmd":
                                        return  $.t('events.ssh_cmd');
                                    default:
                                        console.log(`unknown fs action "${data}"`);
                                        return "";
                                }
                            }
                            return data;
                        }
                    },
                    {
                        data: "virtual_path",
                        defaultContent: "",
                        width: '20%',
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (!data){
                                    return "";
                                }
                                if (row.virtual_target_path){
                                    return escapeHTML(`${data} => ${row.virtual_target_path}`);
                                }
                                return escapeHTML(data);
                            }
                            return data;
                        }
                    },
                    {
                        data: "username",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (!data){
                                    return "";
                                }
                                return escapeHTML(data);
                            }
                            return data;
                        }
                    },
                    {
                        data: "protocol",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (!data){
                                    return "";
                                }
                            if (type === 'display') {
                                return escapeHTML(data);
                            }
                            return data;
                        }
                    },
                    {
                        data: "ip",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (!data){
                                    return "";
                                }
                                return escapeHTML(data);
                            }
                            return data;
                        }
                    },
                    {
                        data: "status",
                        defaultContent: "",
                        width: '15%',
                        render: function(data, type, row) {
                            if (type === 'display') {
                                let info = "";
                                switch (data){
                                    case 1:
                                        info = $.t('general.ok');
                                        break;
                                    case 2:
                                        info = $.t('general.failed');
                                        break;
                                    case 3:
                                        info = $.t('events.quota_exceeded');
                                        break;
                                    default:
                                        console.log(`unknow status ${data}`);
                                }
                                if (row.file_size || row.file_size == 0){
                                    let humanSize = fileSizeIEC(row["file_size"]);
                                    info+=`. ${humanSize}`;
                                }
                                if (row.elapsed){
                                    let elapsed = humanizeMilliseconds(row.elapsed);
                                    info+=`. ${elapsed}`;
                                }
                                if (row.ssh_cmd){
                                    info+=". "+$.t('events.ssh_cmd')+` "${row.ssh_cmd}"`;
                                }
                                return info;
                            }
                            return data;
                        }
                    },
                ],
                deferRender: true,
                processing: true,
                lengthChange: false,
                searching: false,
                paging: false,
                info: false,
                ordering: false,
                language: {
                    info: $.t('datatable.info'),
                    infoEmpty: $.t('datatable.info_empty'),
                    infoFiltered: $.t('datatable.info_filtered'),
                    loadingRecords: "",
                    processing: $.t('datatable.processing'),
                    zeroRecords: "",
                    emptyTable: $.t('datatable.no_records')
                },
            });

            dt.on('draw', drawAction);
        }

        function drawAction() {
            $('#table_body').localize();
        }

        return {
            init: function () {
                initDatatable();
            }
        }
    }();

    var datatableProviderEvents = function(){
        var dt;

        var initDatatable = function () {
            if (dt){
                dt.clear().draw();
                dt.ajax.url(getSearchURL(false)).load();
                return;
            }
            $('#errorMsg').addClass("d-none");
            dt = $('#dataTableProvider').DataTable({
                ajax: {
                    url: getSearchURL(false),
                    dataSrc: handleResponseData,
                    error: function ($xhr, textStatus, errorThrown) {
                        $(".dt-processing").hide();
                        let txt = "";
                        if ($xhr) {
                            let json = $xhr.responseJSON;
                            if (json) {
                                if (json.message){
                                    txt = json.message;
                                }
                            }
                        }
                        if (!txt){
                            txt = "general.error500";
                        }
                        setI18NData($('#errorTxt'), txt);
                        $('#errorMsg').removeClass("d-none");
                    }
                },
                columns: [
                    {
                        data: "timestamp",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (data){
                                    let ts = data/1000000;
                                    if (ts){
                                        return $.t('general.datetime', {
                                            val: new Date(ts),
                                            formatParams: {
                                                val: { year: '2-digit', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
                                            }
                                        });
                                    }
                                }
                                return "";
                            }
                            return data;
                        }
                    },
                    {
                        data: "action",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                switch (data){
                                    case "add":
                                        return  $.t('events.add');
                                    case "update":
                                        return  $.t('events.update');
                                    case "delete":
                                        return  $.t('events.delete');
                                    console.log(`unknown provider action "${data}"`);
                                        return "";
                                }
                            }
                            return data;
                        }
                    },
                    {
                        data: "object_type",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (!data){
                                    return "";
                                }
                                let message;
                                switch (data){
                                    case "user":
                                        message = $.t('provider_objects.user');
                                        break;
                                    case "folder":
                                        message = $.t('provider_objects.folder');
                                        break;
                                    case "group":
                                        message = $.t('provider_objects.group');
                                        break;
                                    case "admin":
                                        message = $.t('provider_objects.admin');
                                        break;
                                    case "api_key":
                                        message = $.t('provider_objects.api_key');
                                        break;
                                    case "share":
                                        message = $.t('provider_objects.share');
                                        break;
                                    case "event_action":
                                        message = $.t('provider_objects.event_action');
                                        break;
                                    case "event_rule":
                                        message = $.t('provider_objects.event_rule');
                                        break;
                                    case "role":
                                        message = $.t('provider_objects.role');
                                        break;
                                    case "ip_list_entry":
                                        message = $.t('provider_objects.ip_list_entry');
                                        break;
                                    case "configs":
                                        message = $.t('provider_objects.configs');
                                        break;
                                    default:
                                        console.log("uknown object type: "+data);
                                        return ""
                                }
                                if (row.object_name && data != "configs"){
                                    return message+= escapeHTML(` "${row.object_name}"`);
                                }
                                return message;
                            }
                            return data;
                        }
                    },
                    {
                        data: "username",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (!data){
                                    return "";
                                }
                                return escapeHTML(data);
                            }
                            return data;
                        }
                    },
                    {
                        data: "ip",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (!data){
                                    return "";
                                }
                                return escapeHTML(data);
                            }
                            return data;
                        }
                    },
                ],
                deferRender: true,
                processing: true,
                lengthChange: false,
                searching: false,
                paging: false,
                info: false,
                ordering: false,
                language: {
                    info: $.t('datatable.info'),
                    infoEmpty: $.t('datatable.info_empty'),
                    infoFiltered: $.t('datatable.info_filtered'),
                    loadingRecords: "",
                    processing: $.t('datatable.processing'),
                    zeroRecords: "",
                    emptyTable: $.t('datatable.no_records')
                },
            });

            dt.on('draw', drawAction);
        }

        function drawAction() {
            $('#table_body').localize();
        }

        return {
            init: function () {
                initDatatable();
            }
        }
    }();

    var datatableLogEvents = function(){
        var dt;

        var initDatatable = function () {
            if (dt){
                dt.clear().draw();
                dt.ajax.url(getSearchURL(false)).load();
                return;
            }
            $('#errorMsg').addClass("d-none");
            dt = $('#dataTableLog').DataTable({
                ajax: {
                    url: getSearchURL(false),
                    dataSrc: handleResponseData,
                    error: function ($xhr, textStatus, errorThrown) {
                        $(".dt-processing").hide();
                        let txt = "";
                        if ($xhr) {
                            let json = $xhr.responseJSON;
                            if (json) {
                                if (json.message){
                                    txt = json.message;
                                }
                            }
                        }
                        if (!txt){
                            txt = "general.error500";
                        }
                        setI18NData($('#errorTxt'), txt);
                        $('#errorMsg').removeClass("d-none");
                    }
                },
                columns: [
                    {
                        data: "timestamp",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (data){
                                    let ts = data/1000000;
                                    if (ts){
                                        return $.t('general.datetime', {
                                            val: new Date(ts),
                                            formatParams: {
                                                val: { year: '2-digit', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
                                            }
                                        });
                                    }
                                }
                                return "";
                            }
                            return data;
                        }
                    },
                    {
                        data: "event",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                switch (data){
                                    case 1:
                                        return  $.t('events.login_failed');
                                    case 2:
                                        return  $.t('events.login_missing_user');
                                    case 3:
                                        return  $.t('events.no_login_tried');
                                    case 4:
                                        return  $.t('events.algo_negotiation_failed');
                                    case 5:
                                        return  $.t('events.login_ok');
                                    default:
                                        console.log(`unknown log action "${data}"`);
                                        return "";
                                }
                            }
                            return data;
                        }
                    },
                    {
                        data: "username",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (!data){
                                    return "";
                                }
                                return escapeHTML(data);
                            }
                            return data;
                        }
                    },
                    {
                        data: "protocol",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (!data){
                                    return "";
                                }
                                return escapeHTML(data);
                            }
                            return data;
                        }
                    },
                    {
                        data: "ip",
                        defaultContent: "",
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (!data){
                                    return "";
                                }
                                if (data){
                                    return escapeHTML(data);
                                }
                            }
                            return data;
                        }
                    },
                    {
                        data: "message",
                        defaultContent: "",
                        width: '25%',
                        render: function(data, type, row) {
                            if (type === 'display') {
                                if (data){
                                    return escapeHTML(data);
                                }
                            }
                            return "";
                        }
                    },
                ],
                deferRender: true,
                processing: true,
                lengthChange: false,
                searching: false,
                paging: false,
                info: false,
                ordering: false,
                language: {
                    info: $.t('datatable.info'),
                    infoEmpty: $.t('datatable.info_empty'),
                    infoFiltered: $.t('datatable.info_filtered'),
                    loadingRecords: "",
                    processing: $.t('datatable.processing'),
                    zeroRecords: "",
                    emptyTable: $.t('datatable.no_records')
                },
            });

            dt.on('draw', drawAction);
        }

        function drawAction() {
            $('#table_body').localize();
        }

        return {
            init: function () {
                initDatatable();
            }
        }
    }();

    $(document).on("i18nload", function(){
        $('#dateTimeRange').flatpickr({
            enableTime: true,
            time_24hr: true,
            formatDate: (date, format, locale) => {
                return $.t('general.datetime', {
                            val: new Date(date),
                            formatParams: {
                                val: { year: '2-digit', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
                            }
                        });
            },
            mode: "range",
            defaultDate: [moment().add(-1,'hour').format('YYYY-MM-DD HH:mm')],
            minuteIncrement: 1,
            locale: i18next.resolvedLanguage
        });
        onEventChanged('1');
    });

    $(document).on("i18nshow", function(){
        $('#idEventType').on("change", function(){
            onEventChanged(this.value);
        });

        $('#search_button').on("click", function(e){
            e.preventDefault();
            this.blur();
            onSearchClicked();
        });

        $('#export_button').on("click", function(e){
            e.preventDefault();
            this.blur();
            onExportClicked();
        });

        $('#pagePrevious').on("click", function(e){
            e.preventDefault();
            this.blur();
            prevClicked();
        });

        $('#pageNext').on("click", function(e){
            e.preventDefault();
            this.blur();
            nextClicked();
        });

    });
</script>
{{- end}}