luci-app: updated log.

This commit is contained in:
gSpot
2023-11-26 16:49:59 +03:00
parent 6cfbf6b8ec
commit 25e681055e
8 changed files with 340 additions and 116 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ LUCI_APP=1
OWRT_VERSION="current"
RUAB_VERSION="1.3-1"
RUAB_MOD_LUA_VERSION="1.3-2"
RUAB_LUCI_APP_VERSION="1.3-3"
RUAB_LUCI_APP_VERSION="1.3-4"
BASE_URL="https://raw.githubusercontent.com/gSpotx2f/packages-openwrt/master"
PKG_DIR="/tmp"
+1 -1
View File
@@ -4,7 +4,7 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.3-3
PKG_VERSION:=1.3-4
LUCI_TITLE:=LuCI support for ruantiblock
LUCI_DEPENDS:=+ruantiblock
LUCI_PKGARCH:=all
@@ -1,4 +1,5 @@
'use strict';
'require poll';
'require baseclass';
'require ui';
'require view';
@@ -161,6 +162,8 @@ return baseclass.extend({
*/
title : null,
pollInterval : L.env.pollinterval,
logFacilities : {
'kern' : E('span', { 'class': 'zonebadge log-facility-dropdown-item' }, E('strong', 'kern')),
'user' : E('span', { 'class': 'zonebadge log-facility-dropdown-item' }, E('strong', 'user')),
@@ -203,8 +206,20 @@ return baseclass.extend({
fastTailValue : null,
timeFilterValue : null,
hostFilterValue : [],
facilityFilterValue : [],
levelFilterValue : [],
msgFilterValue : null,
logSortingValue : 'asc',
autoRefreshValue : true,
isHosts : false,
isFacilities : false,
@@ -223,6 +238,10 @@ return baseclass.extend({
totalLogLines : 0,
lastBytes : null,
actionButtons : [],
htmlEntities(str) {
return String(str).replace(
/&/g, '&').replace(
@@ -232,6 +251,10 @@ return baseclass.extend({
/'/g, ''');
},
checkZeroValue(value) {
return (/^[0-9]+$/.test(value)) ? value : 0
},
makeLogHostsDropdownItem(host) {
return E(
'span',
@@ -263,7 +286,7 @@ return baseclass.extend({
);
},
makeLogFacilitiesDropdownSection(){
makeLogFacilitiesDropdownSection() {
this.logFacilitiesDropdown = new ui.Dropdown(
null,
this.logFacilities,
@@ -287,7 +310,7 @@ return baseclass.extend({
);
},
makeLogLevelsDropdownSection(){
makeLogLevelsDropdownSection() {
this.logLevelsDropdown = new ui.Dropdown(
null,
this.logLevels,
@@ -311,6 +334,47 @@ return baseclass.extend({
);
},
setFilterSettings() {
this.tailValue = this.checkZeroValue(this.tailInput.value);
this.timeFilterValue = this.timeFilter.value;
if(this.isHosts) {
this.hostFilterValue = this.logHostsDropdown.getValue();
};
if(this.isFacilities) {
this.facilityFilterValue = this.logFacilitiesDropdown.getValue();
};
if(this.isLevels) {
this.levelFilterValue = this.logLevelsDropdown.getValue();
};
this.msgFilterValue = this.msgFilter.value;
this.logSortingValue = this.logSorting.value;
this.autoRefreshValue = this.autoRefresh.checked;
if(this.autoRefreshValue) {
poll.add(this.pollFuncWrapper, this.pollInterval);
this.refreshBtn.style.visibility = 'hidden';
} else {
poll.remove(this.pollFuncWrapper);
this.refreshBtn.style.visibility = 'visible';
};
},
resetFormValues() {
this.tailInput.value = this.tailValue;
this.timeFilter.value = this.timeFilterValue;
if(this.isHosts) {
this.logHostsDropdown.setValue(this.hostFilterValue);
};
if(this.isFacilities) {
this.logFacilitiesDropdown.setValue(this.facilityFilterValue);
};
if(this.isLevels) {
this.logLevelsDropdown.setValue(this.levelFilterValue);
};
this.msgFilter.value = this.msgFilterValue;
this.logSorting.value = this.logSortingValue;
this.autoRefresh.checked = this.autoRefreshValue;
},
/**
* Receives raw log data.
* Abstract method, must be overridden by a subclass!
@@ -374,7 +438,7 @@ return baseclass.extend({
},
setDateFilter(entriesArray) {
let fPattern = this.timeFilter.value;
let fPattern = this.timeFilterValue;
if(!fPattern) {
return entriesArray;
};
@@ -384,12 +448,11 @@ return baseclass.extend({
setHostFilter(entriesArray) {
let logHostsKeys = Object.keys(this.logHosts);
if(logHostsKeys.length > 0 && this.logHostsDropdown) {
let selectedHosts = this.logHostsDropdown.getValue();
this.logHostsDropdown.addChoices(logHostsKeys, this.logHosts);
if(selectedHosts.length === 0 || logHostsKeys.length === selectedHosts.length) {
if(this.hostFilterValue.length === 0 || logHostsKeys.length === this.hostFilterValue.length) {
return entriesArray;
};
return entriesArray.filter(e => selectedHosts.includes(e[2]));
return entriesArray.filter(e => this.hostFilterValue.includes(e[2]));
};
return entriesArray;
},
@@ -397,11 +460,10 @@ return baseclass.extend({
setFacilityFilter(entriesArray) {
let logFacilitiesKeys = Object.keys(this.logFacilities);
if(logFacilitiesKeys.length > 0 && this.logFacilitiesDropdown) {
let selectedFacilities = this.logFacilitiesDropdown.getValue();
if(selectedFacilities.length === 0 || logFacilitiesKeys.length === selectedFacilities.length) {
if(this.facilityFilterValue.length === 0 || logFacilitiesKeys.length === this.facilityFilterValue.length) {
return entriesArray;
};
return entriesArray.filter(e => selectedFacilities.includes(e[3]));
return entriesArray.filter(e => this.facilityFilterValue.includes(e[3]));
};
return entriesArray;
},
@@ -409,17 +471,16 @@ return baseclass.extend({
setLevelFilter(entriesArray) {
let logLevelsKeys = Object.keys(this.logLevels);
if(logLevelsKeys.length > 0 && this.logLevelsDropdown) {
let selectedLevels = this.logLevelsDropdown.getValue();
if(selectedLevels.length === 0 || logLevelsKeys.length === selectedLevels.length) {
if(this.levelFilterValue.length === 0 || logLevelsKeys.length === this.levelFilterValue.length) {
return entriesArray;
};
return entriesArray.filter(e => selectedLevels.includes(e[4]));
return entriesArray.filter(e => this.levelFilterValue.includes(e[4]));
};
return entriesArray;
},
setMsgFilter(entriesArray) {
let fPattern = this.msgFilter.value;
let fPattern = this.msgFilterValue;
if(!fPattern) {
return entriesArray;
};
@@ -438,10 +499,22 @@ return baseclass.extend({
throw new Error('makeLogArea must be overridden by a subclass');
},
downloadLog(ev) {
let formElems = Array.from(this.logForm.elements);
formElems.forEach(e => e.disabled = true);
disableFormElems() {
Array.from(this.logFilterForm.elements).forEach(
e => e.disabled = true
);
this.actionButtons.forEach(e => e.disabled = true);
},
enableFormElems() {
Array.from(this.logFilterForm.elements).forEach(
e => e.disabled = false
);
this.actionButtons.forEach(e => e.disabled = false);
},
downloadLog(ev) {
this.disableFormElems();
return this.getLogData(0).then(logdata => {
logdata = logdata || '';
let link = E('a', {
@@ -455,41 +528,47 @@ return baseclass.extend({
ui.addNotification(null,
E('p', {}, _('Download error') + ': ' + err.message));
}).finally(() => {
formElems.forEach(e => e.disabled = false);
this.enableFormElems();
});
},
restoreSettings() {
restoreSettingsFromLocalStorage() {
let tailValueLocal = localStorage.getItem(`luci-app-${this.viewName}-tailValue`);
if(tailValueLocal) {
this.tailValue = Number(tailValueLocal);
};
let logSortingLocal = localStorage.getItem(`luci-app-${this.viewName}-logSorting`);
let logSortingLocal = localStorage.getItem(`luci-app-${this.viewName}-logSortingValue`);
if(logSortingLocal) {
this.logSortingValue = logSortingLocal;
};
let autoRefreshLocal = localStorage.getItem(`luci-app-${this.viewName}-autoRefreshValue`);
if(autoRefreshLocal) {
this.autoRefreshValue = Boolean(Number(autoRefreshLocal));
};
},
saveSettings(tailValue, logSortingValue) {
saveSettingsToLocalStorage(tailValue, logSortingValue, autoRefreshValue) {
tailValue = this.checkZeroValue(tailValue);
if(this.tailValue != tailValue) {
this.tailValue = (/^[0-9]+$/.test(tailValue)) ? tailValue : 0;
localStorage.setItem(
`luci-app-${this.viewName}-tailValue`, String(this.tailValue));
`luci-app-${this.viewName}-tailValue`, String(tailValue));
};
if(this.logSortingValue != logSortingValue) {
this.logSortingValue = logSortingValue;
localStorage.setItem(
`luci-app-${this.viewName}-logSorting`, this.logSortingValue);
`luci-app-${this.viewName}-logSortingValue`, logSortingValue);
};
if(this.autoRefreshValue != autoRefreshValue) {
localStorage.setItem(
`luci-app-${this.viewName}-autoRefreshValue`, String(Number(autoRefreshValue)));
};
},
onSubmitForm(tail) {
let formElems = Array.from(this.logForm.elements);
formElems.forEach(e => e.disabled = true);
this.logDownloadBtn.disabled = true;
reloadLog(tail, modal=false, autorefresh=false) {
tail = (tail && tail > 0) ? tail : 0;
this.logSortingValue = this.logSorting.value;
if(!autorefresh) {
this.disableFormElems();
poll.stop();
};
return this.getLogData(tail).then(logdata => {
logdata = logdata || '';
this.logWrapper.innerHTML = '';
@@ -508,7 +587,6 @@ return baseclass.extend({
)
)
);
if(logdata && logdata !== '') {
if(this.isFacilities && !this.logFacilitiesDropdown) {
this.logFacilitiesDropdownElem = this.makeLogFacilitiesDropdownSection();
@@ -521,10 +599,13 @@ return baseclass.extend({
};
};
}).finally(() => {
formElems.forEach(e => e.disabled = false);
this.logDownloadBtn.disabled = false;
this.fastTailValue = this.totalLogLines;
ui.hideModal();
if(modal) {
ui.hideModal();
};
if(!autorefresh) {
this.enableFormElems();
poll.start();
};
});
},
@@ -549,11 +630,9 @@ return baseclass.extend({
}, _('Timestamp filter')),
E('div', { 'class': 'cbi-value-field' }, this.timeFilter),
]),
this.logHostsDropdownElem,
this.logFacilitiesDropdownElem,
this.logLevelsDropdownElem,
E('div', { 'class': 'cbi-value' }, [
E('label', {
'class': 'cbi-value-title',
@@ -569,50 +648,106 @@ return baseclass.extend({
}, _('Sorting entries')),
E('div', { 'class': 'cbi-value-field' }, this.logSorting),
]),
E('div', { 'class': 'cbi-value' }, [
E('label', {
'class': 'cbi-value-title',
'for' : 'autoRefresh',
}, _('Auto refresh')),
E('div', { 'class': 'cbi-value-field' },
E('div', { 'class': 'cbi-checkbox' }, [
this.autoRefresh,
E('label', {})
])
),
]),
]),
]),
]),
E('div', { 'class': 'right' }, [
this.logForm,
E('input', {
'type' : 'submit',
'form' : 'logForm',
'class': 'btn cbi-button-positive important',
'value': _('Apply'),
}),
' ',
this.logFilterForm,
E('button', {
'class': 'btn',
'click': ui.hideModal,
}, _('Close')),
'click': ev => {
ev.target.blur();
ui.hideModal();
this.resetFormValues();
},
}, _('Dismiss')),
' ',
E('button', {
'type' : 'submit',
'form' : 'logFilterForm',
'class': 'btn cbi-button-positive important',
'click': ui.createHandlerFn(this, function(ev) {
ev.target.blur();
ev.preventDefault();
return this.onSubmitFilter();
}),
}, _('Apply')),
]),
], 'cbi-modal');
},
updateLog(autorefresh=false) {
let tail = (Number(this.tailValue) == 0 || Number(this.fastTailValue) == 0)
? 0 : Math.max(Number(this.tailValue), this.fastTailValue)
return this.reloadLog(tail, false, autorefresh);
},
/**
* Creates a promise for the RPC request.
* Abstract method, must be overridden by a subclass!
*
* @returns {Promise}
* Returns a promise that returns the size of the log in bytes.
*/
getLogSize() {
throw new Error('getLogSize must be overridden by a subclass');
},
async pollFunc() {
await this.getLogSize().then(async bytes => {
if(this.lastBytes != bytes) {
this.lastBytes = bytes;
return await this.updateLog(true);
};
});
},
onSubmitFilter() {
this.saveSettingsToLocalStorage(
this.tailInput.value, this.logSorting.value, this.autoRefresh.checked);
this.setFilterSettings();
this.fastTailValue = Number(this.tailValue);
return this.reloadLog(Number(this.tailValue), true);
},
load() {
// Restoring settings from localStorage
this.restoreSettings();
this.restoreSettingsFromLocalStorage();
return this.getLogData(this.tailValue);
},
render(logdata) {
this.pollFuncWrapper = L.bind(this.pollFunc, this);
this.logWrapper = E('div', {
'id' : 'logWrapper',
'style': 'width:100%; min-height:20em'
}, this.makeLogArea(this.parseLogData(logdata, this.tailValue)));
this.fastTailValue = this.totalLogLines;
this.fastTailValue = this.tailValue
this.tailInput = E('input', {
'id' : 'tailInput',
'name' : 'tailInput',
'type' : 'text',
'form' : 'logForm',
'form' : 'logFilterForm',
'class' : 'cbi-input-text',
'style' : 'width:4em !important; min-width:4em !important',
'maxlength': 5,
});
this.tailInput.value = (this.tailValue === 0) ? null : this.tailValue;
this.tailInput.value = this.tailValue;
ui.addValidator(this.tailInput, 'uinteger', true);
this.logHostsDropdownElem = '';
@@ -632,7 +767,7 @@ return baseclass.extend({
'id' : 'timeFilter',
'name' : 'timeFilter',
'type' : 'text',
'form' : 'logForm',
'form' : 'logFilterForm',
'class' : 'cbi-input-text',
'placeholder': _('Type an expression...'),
});
@@ -641,7 +776,7 @@ return baseclass.extend({
'id' : 'msgFilter',
'name' : 'msgFilter',
'type' : 'text',
'form' : 'logForm',
'form' : 'logFilterForm',
'class' : 'cbi-input-text',
'placeholder': _('Type an expression...'),
});
@@ -649,7 +784,7 @@ return baseclass.extend({
this.logSorting = E('select', {
'id' : 'logSorting',
'name' : 'logSorting',
'form' : 'logForm',
'form' : 'logFilterForm',
'class': "cbi-input-select",
}, [
E('option', { 'value': 'asc' }, _('ascending')),
@@ -657,6 +792,28 @@ return baseclass.extend({
]);
this.logSorting.value = this.logSortingValue;
this.autoRefresh = E('input', {
'id' : 'autoRefresh',
'name' : 'autoRefresh',
'type' : 'checkbox',
'form' : 'logFilterForm',
});
this.autoRefresh.checked = this.autoRefreshValue;
this.filterEditsBtn = E('button', {
'class': 'cbi-button btn cbi-button-action',
'click': L.bind(this.filterSettingsModal, this),
}, _('Edit'));
this.logFilterForm = E('form', {
'id' : 'logFilterForm',
'name' : 'logFilterForm',
'submit': ev => {
ev.preventDefault();
return this.onSubmitFilter();
},
});
this.logDownloadBtn = E('button', {
'id' : 'logDownloadBtn',
'name' : 'logDownloadBtn',
@@ -664,17 +821,56 @@ return baseclass.extend({
'click': ui.createHandlerFn(this, this.downloadLog),
}, _('Download log'));
this.logForm = E('form', {
'id' : 'logForm',
'name' : 'logForm',
'style' : 'display:inline-block; margin-top:0.5em',
'submit': ui.createHandlerFn(this, function(ev) {
ev.preventDefault();
// Saving settings to localStorage
this.saveSettings(this.tailInput.value, this.logSorting.value);
return this.onSubmitForm(Number(this.tailInput.value));
this.refreshBtn = E('button', {
'title' : _('Refresh log'),
'class' : 'btn log-side-btn',
'style' : `visibility:${(this.autoRefreshValue) ? 'hidden' : 'visible'}`,
'click' : ui.createHandlerFn(this, function(ev) {
ev.target.blur();
return this.updateLog();
}),
}, E('span', {}, ' '));
}, '⟳');
this.moreEntriesBtn = E('button', {
'title': _('Get more entries'),
'class': 'btn log-side-btn',
'style': 'margin-top:1px !important',
'click': ui.createHandlerFn(this, function(ev) {
ev.target.blur();
if(this.fastTailValue === null) {
this.fastTailValue = Number(this.tailValue);
}
if(this.fastTailValue > 0) {
this.fastTailValue += this.fastTailIncrement;
};
return this.reloadLog(this.fastTailValue);
}),
}, `+${this.fastTailIncrement}`);
this.allEntriesBtn = E('button', {
'title': _('Get all entries'),
'class': 'btn log-side-btn',
'style': 'margin-top:1px !important',
'click': ui.createHandlerFn(this, function(ev) {
ev.target.blur();
this.fastTailValue = 0;
return this.reloadLog(0);
}),
}, _('All'));
this.filterModalBtn = E('button', {
'title': _('Filter settings'),
'class': 'btn log-side-btn',
'style': 'margin-top:10px !important',
'click': ev => {
ev.target.blur();
this.filterSettingsModal();
},
}, '▢');
this.actionButtons.push(this.filterEditsBtn, this.logDownloadBtn,
this.refreshBtn,this.moreEntriesBtn,
this.allEntriesBtn, this.filterModalBtn);
document.body.append(
E('div', {
@@ -682,47 +878,10 @@ return baseclass.extend({
'class': 'log-side-block',
'style': `right:1px; top:${window.innerHeight / 2 - 60}px`,
}, [
E('button', {
'title': _('Refresh log'),
'class': 'btn log-side-btn',
'click': ui.createHandlerFn(this, function(ev) {
ev.target.blur();
return this.onSubmitForm(
Math.max(Number(this.tailValue), this.fastTailValue));
}),
}, '🗘'),
E('button', {
'title': _('Get more entries'),
'class': 'btn log-side-btn',
'style': 'margin-top:1px !important',
'click': ui.createHandlerFn(this, function(ev) {
ev.target.blur();
if(this.fastTailValue === null) {
this.fastTailValue = Number(this.tailValue);
};
this.fastTailValue += this.fastTailIncrement;
return this.onSubmitForm(this.fastTailValue);
}),
}, `+${this.fastTailIncrement}`),
E('button', {
'title': _('Get all entries'),
'class': 'btn log-side-btn',
'style': 'margin-top:1px !important',
'click': ui.createHandlerFn(this, function(ev) {
ev.target.blur();
return this.onSubmitForm(0);
}),
}, '𝍤'),
E('button', {
'title': _('Filter settings'),
'class': 'btn log-side-btn',
'style': 'margin-top:10px !important',
'click': ev => {
ev.target.blur();
this.filterSettingsModal();
},
}, '🗔'),
this.refreshBtn,
this.moreEntriesBtn,
this.allEntriesBtn,
this.filterModalBtn,
E('button', {
'class': 'btn log-side-btn',
'style': 'margin-top:10px !important',
@@ -742,6 +901,10 @@ return baseclass.extend({
])
);
if(this.autoRefreshValue) {
poll.add(this.pollFuncWrapper, this.pollInterval);
};
return E([
E('h2', { 'id': 'logTitle', 'class': 'fade-in' }, this.title),
E('div', { 'class': 'cbi-section-descr fade-in' }),
@@ -753,12 +916,7 @@ return baseclass.extend({
'for' : 'filterSettings',
}, _('Filter settings')),
E('div', { 'class': 'cbi-value-field' }, [
E('div', {},
E('button', {
'class': 'cbi-button btn cbi-button-action',
'click': L.bind(this.filterSettingsModal, this),
}, _('Edit'))
),
E('div', {}, this.filterEditsBtn),
E('input', {
'id' : 'filterSettings',
'type': 'hidden',
@@ -1,4 +1,5 @@
'require fs';
'require rpc';
'require ui';
'require view.ruantiblock.log-widget as abc';
'require view.ruantiblock.tools as tools';
@@ -14,6 +15,24 @@ return abc.view.extend({
entriesHandler : null,
lastBytes : 0,
callLogSize: rpc.declare({
object: 'luci.ruantiblock',
method: 'getLogSize',
expect: { '': {} }
}),
getLogSize() {
return this.callLogSize().then((data) => {
if(data.bytes) {
return Number(data.bytes);
} else {
throw new Error(_('An error occurred while trying to get the log size!'));
};
});
},
// logd
logdHandler(strArray, lineNum) {
let logLevel = strArray[5].split('.');
+7 -1
View File
@@ -37,12 +37,18 @@ msgstr "Весь трафик указанных хостов проходит
msgid "Allows you to limit the hosts that are allowed to bypass blocking"
msgstr "Позволяет ограничить хосты, которым разрешено обходить блокировки"
msgid "An error occurred while trying to get the log size!"
msgstr "Произошла ошибка при попытке получить размер лога!"
msgid "Apply"
msgstr "Применить"
msgid "Apply proxy rules to router application traffic"
msgstr "Применять правила прокси к трафику приложений роутера"
msgid "Auto refresh"
msgstr "Автообновление"
msgid "Blacklist module"
msgstr "Модуль блэклиста"
@@ -114,7 +120,7 @@ msgid "Download error"
msgstr "Ошибка загрузки"
msgid "Download log"
msgstr "Скачать лог"
msgstr "Скачать журнал"
msgid "Downloading a blacklist via proxy"
msgstr "Скачивать блэклист через прокси"
@@ -22,12 +22,18 @@ msgstr ""
msgid "All traffic of the specified hosts passes through the proxy, without a blacklist"
msgstr ""
msgid "An error occurred while trying to get the log size!"
msgstr ""
msgid "Apply"
msgstr ""
msgid "Apply proxy rules to router application traffic"
msgstr ""
msgid "Auto refresh"
msgstr ""
msgid "Blacklist module"
msgstr ""
@@ -0,0 +1,34 @@
#!/bin/sh
. /lib/functions.sh
. /usr/share/libubox/jshn.sh
readonly LOGREAD_CMD="logread -e ruantiblock:"
make_size_value() {
json_init
json_add_string 'bytes' "$1"
json_dump
json_cleanup
}
get_log_size() {
make_size_value "`$LOGREAD_CMD | wc -c`"
}
case "$1" in
list)
json_init
json_add_object "getLogSize"
json_close_object
json_dump
json_cleanup
;;
call)
case "$2" in
getLogSize)
get_log_size
;;
esac
;;
esac
@@ -18,7 +18,8 @@
},
"uci": [ "network", "ruantiblock" ],
"ubus": {
"luci": [ "getInitList", "setInitAction" ]
"luci": [ "getInitList", "setInitAction" ],
"luci.ruantiblock": [ "getLogSize" ]
}
},
"write": {