diff --git a/autoinstall/2.x/autoinstall.sh b/autoinstall/2.x/autoinstall.sh index 0534d03..0496a54 100755 --- a/autoinstall/2.x/autoinstall.sh +++ b/autoinstall/2.x/autoinstall.sh @@ -10,9 +10,9 @@ LUCI_APP=1 HTTPS_DNS_PROXY=1 OWRT_VERSION="current" -RUAB_VERSION="2.1.4-r3" -RUAB_MOD_LUA_VERSION="2.1.4-r1" -RUAB_LUCI_APP_VERSION="2.1.4-r1" +RUAB_VERSION="2.1.5-r1" +RUAB_MOD_LUA_VERSION="2.1.5-r1" +RUAB_LUCI_APP_VERSION="2.1.5-r1" BASE_URL="https://raw.githubusercontent.com/gSpotx2f/packages-openwrt/master" PKG_DIR="/tmp" @@ -29,7 +29,6 @@ URL_LUCI_APP_PKG="${BASE_URL}/${OWRT_VERSION}/luci-app-ruantiblock_${RUAB_LUCI_A URL_LUCI_APP_RU_PKG="${BASE_URL}/${OWRT_VERSION}/luci-i18n-ruantiblock-ru_${RUAB_LUCI_APP_VERSION}_all.ipk" ### tor URL_TORRC="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/tor/etc/tor/torrc" -### ruantiblock-mod-lua URL_LUA_IDN="https://raw.githubusercontent.com/haste/lua-idn/master/idn.lua" ### Local files @@ -37,7 +36,7 @@ URL_LUA_IDN="https://raw.githubusercontent.com/haste/lua-idn/master/idn.lua" CONFIG_DIR="${PREFIX}/etc/ruantiblock" USER_LISTS_DIR="${CONFIG_DIR}/user_lists" EXEC_DIR="${PREFIX}/usr/bin" -BACKUP_DIR="${CONFIG_DIR}/autoinstall.bak.`date +%s`" +BACKUP_DIR="${CONFIG_DIR}/autoinstall.bak.$(date +%s)" ### packages FILE_RUAB_PKG="${PKG_DIR}/ruantiblock_${RUAB_VERSION}_all.ipk" FILE_MOD_LUA_PKG="${PKG_DIR}/ruantiblock-mod-lua_${RUAB_MOD_LUA_VERSION}_all.ipk" @@ -56,23 +55,21 @@ FILE_INIT_SCRIPT="${PREFIX}/etc/init.d/ruantiblock" FILE_MAIN_SCRIPT="${EXEC_DIR}/ruantiblock" ### tor FILE_TORRC="${PREFIX}/etc/tor/torrc" -### ruantiblock-mod-lua -#FILE_LUA_IPTOOL="${PREFIX}/usr/lib/lua/iptool.lua" FILE_LUA_IDN="${PREFIX}/usr/lib/lua/idn.lua" AWK_CMD="awk" -WGET_CMD=`which wget` +WGET_CMD="$(which wget)" if [ $? -ne 0 ]; then echo " Error! wget doesn't exists" >&2 exit 1 fi WGET_PARAMS="--no-check-certificate -q -O " -OPKG_CMD=`which opkg` +OPKG_CMD="$(which opkg)" if [ $? -ne 0 ]; then echo " Error! opkg doesn't exists" >&2 exit 1 fi -UCI_CMD=`which uci` +UCI_CMD="$(which uci)" if [ $? -ne 0 ]; then echo " Error! uci doesn't exists" >&2 exit 1 @@ -104,7 +101,7 @@ RemoveFile() { DlFile() { local _dir _file if [ -n "$2" ]; then - _dir=`dirname "$2"` + _dir=$(dirname "$2") MakeDir "$_dir" _file="$2" else @@ -119,19 +116,19 @@ DlFile() { } BackupFile() { - [ -e "$1" ] && cp -f "$1" "${1}.bak.`date +%s`" + [ -e "$1" ] && cp -f "$1" "${1}.bak.$(date +%s)" } BackupCurrentConfig() { local _file MakeDir "$BACKUP_DIR" - for _file in `ls -1 "$CONFIG_DIR" | grep -v "$(basename $BACKUP_DIR)"` + for _file in $(ls -1 "$CONFIG_DIR" | grep -v "$(basename $BACKUP_DIR)") do cp -af "${CONFIG_DIR}/${_file}" "${BACKUP_DIR}/${_file}" done for _file in "$FILE_UCI_CONFIG" "$FILE_TORRC" do - [ -e "$_file" ] && cp -af "$_file" "${BACKUP_DIR}/`basename ${_file}`" + [ -e "$_file" ] && cp -af "$_file" "${BACKUP_DIR}/$(basename ${_file})" done } @@ -165,7 +162,7 @@ InstallPackages() { local _pkg for _pkg in $@ do - if [ -z "`$OPKG_CMD list-installed $_pkg`" ]; then + if [ -z "$($OPKG_CMD list-installed $_pkg)" ]; then $OPKG_CMD --force-overwrite install $_pkg if [ $? -ne 0 ]; then echo "Error during installation of the package (${_pkg})" >&2 @@ -181,7 +178,6 @@ InstallBaseConfig() { RemoveFile "$FILE_RUAB_PKG" > /dev/null DlFile "$URL_RUAB_PKG" "$FILE_RUAB_PKG" && $OPKG_CMD install "$FILE_RUAB_PKG" > /dev/null _return_code=$? - # костыль для остановки сервиса, который запускается автоматически после установки пакета! AppStop return $_return_code } @@ -205,7 +201,7 @@ InstallTPConfig() { } TorrcSettings() { - local _lan_ip=`$UCI_CMD get network.lan.ipaddr | $AWK_CMD -F "/" '{print $1}'` + local _lan_ip=$($UCI_CMD get network.lan.ipaddr | $AWK_CMD -F "/" '{print $1}') if [ -z "$_lan_ip" ]; then _lan_ip="0.0.0.0" fi @@ -223,7 +219,6 @@ InstallTorConfig() { TorrcSettings $UCI_CMD set ruantiblock.config.proxy_mode="1" $UCI_CMD commit ruantiblock - # dnsmasq rebind protection $UCI_CMD add_list dhcp.@dnsmasq[0].rebind_domain='onion' $UCI_CMD commit dhcp } @@ -392,7 +387,6 @@ ConfirmProcessing() { ConfirmProxyMode ConfirmBlacklist -#ConfirmLuaModule ConfirmLuciApp ConfirmHttpsDnsProxy ConfirmProcessing @@ -414,7 +408,7 @@ if [ $? -eq 0 ]; then else PrintBold "Installing Tor configuration..." InstallTorConfig - if `/etc/init.d/tor enabled`; then + if $(/etc/init.d/tor enabled); then /etc/init.d/tor restart fi fi diff --git a/autoinstall/2.x/uninstall.sh b/autoinstall/2.x/uninstall.sh index 82568c7..67516f8 100755 --- a/autoinstall/2.x/uninstall.sh +++ b/autoinstall/2.x/uninstall.sh @@ -7,7 +7,7 @@ PREFIX="" CONFIG_DIR="${PREFIX}/etc/ruantiblock" USER_LISTS_DIR="${CONFIG_DIR}/user_lists" EXEC_DIR="${PREFIX}/usr/bin" -BACKUP_DIR="${CONFIG_DIR}/autoinstall.bak.`date +%s`" +BACKUP_DIR="${CONFIG_DIR}/autoinstall.bak.$(date +%s)" HTDOCS_VIEW="${PREFIX}/www/luci-static/resources/view" HTDOCS_RUAB="${HTDOCS_VIEW}/ruantiblock" CRONTAB_FILE="/etc/crontabs/root" @@ -33,7 +33,7 @@ FILE_MAIN_SCRIPT="${EXEC_DIR}/ruantiblock" FILE_TORRC="${PREFIX}/etc/tor/torrc" AWK_CMD="awk" -OPKG_CMD=`which opkg` +OPKG_CMD="$(which opkg)" if [ $? -ne 0 ]; then echo " Error! opkg doesn't exists" >&2 exit 1 @@ -61,13 +61,13 @@ RemoveFile() { BackupCurrentConfig() { local _file MakeDir "$BACKUP_DIR" - for _file in `ls -1 "$CONFIG_DIR" | grep -v "$(basename $BACKUP_DIR)"` + for _file in $(ls -1 "$CONFIG_DIR" | grep -v "$(basename $BACKUP_DIR)") do cp -af "${CONFIG_DIR}/${_file}" "${BACKUP_DIR}/${_file}" done for _file in "$FILE_UCI_CONFIG" "$FILE_TORRC" do - [ -e "$_file" ] && cp -af "$_file" "${BACKUP_DIR}/`basename ${_file}`" + [ -e "$_file" ] && cp -af "$_file" "${BACKUP_DIR}/$(basename ${_file})" done } @@ -91,7 +91,7 @@ RemoveCronTask() { RestoreTorConfig() { [ -e "${FILE_TORRC}.bak" ] && mv -f "${FILE_TORRC}.bak" "$FILE_TORRC" if [ -x "/etc/init.d/tor" ]; then - if `/etc/init.d/tor enabled`; then + if $(/etc/init.d/tor enabled); then /etc/init.d/tor restart fi fi diff --git a/luci-app-ruantiblock/Makefile b/luci-app-ruantiblock/Makefile index 6aaa118..2729256 100644 --- a/luci-app-ruantiblock/Makefile +++ b/luci-app-ruantiblock/Makefile @@ -1,11 +1,11 @@ # -# (с) 2024 gSpot (https://github.com/gSpotx2f/ruantiblock_openwrt) +# (с) 2025 gSpot (https://github.com/gSpotx2f/ruantiblock_openwrt) # include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-ruantiblock -PKG_VERSION:=2.1.4 +PKG_VERSION:=2.1.5 PKG_RELEASE:=1 LUCI_TITLE:=LuCI support for ruantiblock LUCI_DEPENDS:=+ruantiblock diff --git a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-base.js b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-base.js index 3bebb13..294b681 100644 --- a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-base.js +++ b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-base.js @@ -10,6 +10,7 @@ document.head.append(E('style', {'type': 'text/css'}, --app-log-dark-font-color: #2e2e2e; --app-log-light-font-color: #fff; --app-log-debug-font-color: #737373; + --app-log-raw-font-color: #737373; --app-log-emerg-color: #a93734; --app-log-alert: #ff7968; --app-log-crit: #fcc3bf; @@ -18,12 +19,14 @@ document.head.append(E('style', {'type': 'text/css'}, --app-log-notice: #e3ffec; --app-log-info: rgba(0,0,0,0); --app-log-debug: #ebf6ff; + --app-log-raw: #eee; --app-log-entries-count-border: #ccc; } :root[data-darkmode="true"] { --app-log-dark-font-color: #fff; --app-log-light-font-color: #fff; --app-log-debug-font-color: #e7e7e7; + --app-log-raw-font-color: #aaa; --app-log-emerg-color: #960909; --app-log-alert: #eb5050; --app-log-crit: #dc7f79; @@ -32,6 +35,7 @@ document.head.append(E('style', {'type': 'text/css'}, --app-log-notice: #007627; --app-log-info: rgba(0,0,0,0); --app-log-debug: #5986b1; + --app-log-raw: #353535; --app-log-entries-count-border: #555; } #logWrapper { @@ -114,6 +118,16 @@ log-emerg td { .log-debug td { color: var(--app-log-debug-font-color) !important; } +.log-raw { + background-color: var(--app-log-raw) !important; + color: var(--app-log-raw-font-color) !important; +} +.log-raw .td { + color: var(--app-log-raw-font-color) !important; +} +.log-raw td { + color: var(--app-log-raw-font-color) !important; +} .log-highlight-item { background-color: #ffef00; color: #2e2e2e; @@ -168,25 +182,32 @@ return baseclass.extend({ * * @property {string} viewName */ - viewName : null, + viewName : null, /** * Page title. * * @property {string} title */ - title : null, + title : null, /** * Enable auto refresh log. * - * @property {bool} autoRefresh + * @property {bool} enableAutoRefresh */ - autoRefresh : false, + enableAutoRefresh : false, - pollInterval : L.env.pollinterval, + /** + * Enable timestamp conversion. + * + * @property {bool} enableConvertTimestamp + */ + enableConvertTimestamp: false, - logFacilities : { + 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')), 'mail' : E('span', { 'class': 'zonebadge log-facility-dropdown-item' }, E('strong', 'mail')), @@ -211,7 +232,7 @@ return baseclass.extend({ 'local7' : E('span', { 'class': 'zonebadge log-facility-dropdown-item' }, E('strong', 'local7')), }, - logLevels : { + logLevels : { 'emerg' : E('span', { 'class': 'zonebadge log-emerg' }, E('strong', 'Emergency')), 'alert' : E('span', { 'class': 'zonebadge log-alert' }, E('strong', 'Alert')), 'crit' : E('span', { 'class': 'zonebadge log-crit' }, E('strong', 'Critical')), @@ -246,13 +267,15 @@ return baseclass.extend({ autoRefreshValue : true, - isAutorefresh : true, + autorefreshOn : true, - isHosts : false, + logTimestampFlag : false, - isFacilities : false, + logHostsFlag : false, - isLevels : false, + logFacilitiesFlag : false, + + logLevelsFlag : false, logHosts : {}, @@ -266,7 +289,13 @@ return baseclass.extend({ totalLogLines : 0, - lastHash : null, + logCols : [ '#', null, null, null, null, _('Message') ], + + convertTimestampValue: false, + + convertTimestampOn : false, + + lastHash : null, actionButtons : [], @@ -283,6 +312,68 @@ return baseclass.extend({ return (/^[0-9]+$/.test(value)) ? value : 0 }, + makeLogConvertTimestampSection() { + if(!this.enableConvertTimestamp) { + return ''; + }; + return E('div', { 'class': 'cbi-value' }, [ + E('label', { + 'class': 'cbi-value-title', + 'for' : 'convertTimestamp', + }, _('Date')), + E('div', { 'class': 'cbi-value-field' }, [ + E('div', { 'class': 'cbi-checkbox' }, [ + this.convertTimestamp, + E('label', {}), + ]), + E('div', { 'class': 'cbi-value-description' }, + _('Convert timestamps to a human readable date') + ), + ]), + ]); + }, + + makeLogTimeFilterSection() { + return E('div', { 'class': 'cbi-value' }, [ + E('label', { + 'class': 'cbi-value-title', + 'for' : 'timeFilter', + }, _('Timestamp filter')), + E('div', { 'class': 'cbi-value-field' }, + E('span', { 'class': 'control-group' }, [ + this.timeFilter, + E('button', { + 'class': 'cbi-button btn', + 'click': L.bind(ev => { + ev.target.blur(); + ev.preventDefault(); + this.timeFilter.value = null; + this.timeFilter.focus(); + }, this), + }, '⌫'), + ]) + ), + ]); + }, + + makeLogtimeFilterReSection() { + return E('div', { 'class': 'cbi-value' }, [ + E('label', { + 'class': 'cbi-value-title', + 'for' : 'timeFilterRe', + }, _('Filter is regexp')), + E('div', { 'class': 'cbi-value-field' }, [ + E('div', { 'class': 'cbi-checkbox' }, [ + this.timeFilterRe, + E('label', {}), + ]), + E('div', { 'class': 'cbi-value-description' }, + _('Apply timestamp filter as regular expression') + ), + ]), + ]); + }, + makeLogHostsDropdownItem(host) { return E( 'span', @@ -385,23 +476,28 @@ return baseclass.extend({ }, setFilterSettings() { - this.tailValue = this.checkZeroValue(this.tailInput.value); - this.timeFilterValue = this.timeFilter.value; - this.timeFilterReValue = this.timeFilterRe.checked; - if(this.isHosts) { + this.tailValue = this.checkZeroValue(this.tailInput.value); + if(this.logTimestampFlag) { + if(this.enableConvertTimestamp) { + this.convertTimestampValue = this.convertTimestamp.checked; + }; + this.timeFilterValue = this.timeFilter.value; + this.timeFilterReValue = this.timeFilterRe.checked; + }; + if(this.logHostsFlag) { this.hostFilterValue = this.logHostsDropdown.getValue(); }; - if(this.isFacilities) { + if(this.logFacilitiesFlag) { this.facilityFilterValue = this.logFacilitiesDropdown.getValue(); }; - if(this.isLevels) { + if(this.logLevelsFlag) { this.levelFilterValue = this.logLevelsDropdown.getValue(); }; - this.msgFilterValue = this.msgFilter.value; - this.msgFilterReValue = this.msgFilterRe.checked; - this.logSortingValue = this.logSorting.value; - this.autoRefreshValue = this.autoRefresh.checked; - if(this.isAutorefresh) { + this.msgFilterValue = this.msgFilter.value; + this.msgFilterReValue = this.msgFilterRe.checked; + this.logSortingValue = this.logSorting.value; + this.autoRefreshValue = this.autoRefresh.checked; + if(this.autorefreshOn) { if(this.autoRefreshValue) { poll.add(this.pollFuncWrapper, this.pollInterval); this.refreshBtn.style.visibility = 'hidden'; @@ -413,16 +509,21 @@ return baseclass.extend({ }, resetFormValues() { - this.tailInput.value = this.tailValue; - this.timeFilter.value = this.timeFilterValue; - this.timeFilterRe.checked = this.timeFilterReValue; - if(this.isHosts) { + this.tailInput.value = this.tailValue; + if(this.logTimestampFlag) { + if(this.enableConvertTimestamp) { + this.convertTimestamp.checked = this.convertTimestampValue; + }; + this.timeFilter.value = this.timeFilterValue; + this.timeFilterRe.checked = this.timeFilterReValue; + }; + if(this.logHostsFlag) { this.logHostsDropdown.setValue(this.hostFilterValue); }; - if(this.isFacilities) { + if(this.logFacilitiesFlag) { this.logFacilitiesDropdown.setValue(this.facilityFilterValue); }; - if(this.isLevels) { + if(this.logLevelsFlag) { this.logLevelsDropdown.setValue(this.levelFilterValue); }; this.msgFilter.value = this.msgFilterValue; @@ -508,7 +609,7 @@ return baseclass.extend({ regExp.lastIndex = 0; }); } catch(err) { - if(err.name === 'SyntaxError') { + if(err.name == 'SyntaxError') { ui.addNotification(null, E('p', {}, _('Invalid regular expression') + ': ' + err.message)); return entriesArray; @@ -533,7 +634,7 @@ return baseclass.extend({ let logHostsKeys = Object.keys(this.logHosts); if(logHostsKeys.length > 0 && this.logHostsDropdown) { this.logHostsDropdown.addChoices(logHostsKeys, this.logHosts); - if(this.hostFilterValue.length === 0 || logHostsKeys.length === this.hostFilterValue.length) { + if(this.hostFilterValue.length == 0) { return entriesArray; }; return entriesArray.filter(e => this.hostFilterValue.includes(e[2])); @@ -544,7 +645,7 @@ return baseclass.extend({ setFacilityFilter(entriesArray) { let logFacilitiesKeys = Object.keys(this.logFacilities); if(logFacilitiesKeys.length > 0 && this.logFacilitiesDropdown) { - if(this.facilityFilterValue.length === 0 || logFacilitiesKeys.length === this.facilityFilterValue.length) { + if(this.facilityFilterValue.length == 0) { return entriesArray; }; return entriesArray.filter(e => this.facilityFilterValue.includes(e[3])); @@ -555,7 +656,7 @@ return baseclass.extend({ setLevelFilter(entriesArray) { let logLevelsKeys = Object.keys(this.logLevels); if(logLevelsKeys.length > 0 && this.logLevelsDropdown) { - if(this.levelFilterValue.length === 0 || logLevelsKeys.length === this.levelFilterValue.length) { + if(this.levelFilterValue.length == 0) { return entriesArray; }; return entriesArray.filter(e => this.levelFilterValue.includes(e[4])); @@ -630,7 +731,13 @@ return baseclass.extend({ if(logSortingLocal) { this.logSortingValue = logSortingLocal; }; - if(this.isAutorefresh) { + if(this.enableConvertTimestamp) { + let convertTimestampLocal = localStorage.getItem(`luci-app-${this.viewName}-convertTimestampValue`); + if(convertTimestampLocal) { + this.convertTimestampValue = Boolean(Number(convertTimestampLocal)); + }; + }; + if(this.enableAutoRefresh) { let autoRefreshLocal = localStorage.getItem(`luci-app-${this.viewName}-autoRefreshValue`); if(autoRefreshLocal) { this.autoRefreshValue = Boolean(Number(autoRefreshLocal)); @@ -638,7 +745,7 @@ return baseclass.extend({ }; }, - saveSettingsToLocalStorage(tailValue, logSortingValue, autoRefreshValue) { + saveSettingsToLocalStorage(tailValue, logSortingValue, autoRefreshValue, convertTimestampValue) { tailValue = this.checkZeroValue(tailValue); if(this.tailValue != tailValue) { localStorage.setItem( @@ -648,7 +755,13 @@ return baseclass.extend({ localStorage.setItem( `luci-app-${this.viewName}-logSortingValue`, logSortingValue); }; - if(this.isAutorefresh) { + if(this.convertTimestampOn) { + if(this.convertTimestampValue != convertTimestampValue) { + localStorage.setItem( + `luci-app-${this.viewName}-convertTimestampValue`, String(Number(convertTimestampValue))); + }; + }; + if(this.autorefreshOn) { if(this.autoRefreshValue != autoRefreshValue) { localStorage.setItem( `luci-app-${this.viewName}-autoRefreshValue`, String(Number(autoRefreshValue))); @@ -681,13 +794,23 @@ return baseclass.extend({ ) ); if(logdata && logdata !== '') { - if(this.isFacilities && !this.logFacilitiesDropdown) { + if(this.enableConvertTimestamp && !this.logConvertTimestampElem) { + this.logConvertTimestampElem = this.makeLogConvertTimestampSection(); + }; + if(this.logTimestampFlag && !this.logTimeFilterElem) { + this.logTimeFilterElem = this.makeLogTimeFilterSection(); + }; + if(this.logTimestampFlag && !this.logtimeFilterReElem) { + this.logtimeFilterReElem = this.makeLogtimeFilterReSection(); + }; + + if(this.logFacilitiesFlag && !this.logFacilitiesDropdown) { this.logFacilitiesDropdownElem = this.makeLogFacilitiesDropdownSection(); }; - if(this.isLevels && !this.logLevelsDropdown) { + if(this.logLevelsFlag && !this.logLevelsDropdown) { this.logLevelsDropdownElem = this.makeLogLevelsDropdownSection(); }; - if(this.isHosts && !this.logHostsDropdown) { + if(this.logHostsFlag && !this.logHostsDropdown) { this.logHostsDropdownElem = this.makeLogHostsDropdownSection(); }; }; @@ -730,41 +853,9 @@ return baseclass.extend({ ]) ), ]), - E('div', { 'class': 'cbi-value' }, [ - E('label', { - 'class': 'cbi-value-title', - 'for' : 'timeFilter', - }, _('Timestamp filter')), - E('div', { 'class': 'cbi-value-field' }, - E('span', { 'class': 'control-group' }, [ - this.timeFilter, - E('button', { - 'class': 'cbi-button btn', - 'click': L.bind(ev => { - ev.target.blur(); - ev.preventDefault(); - this.timeFilter.value = null; - this.timeFilter.focus(); - }, this), - }, '⌫'), - ]) - ), - ]), - E('div', { 'class': 'cbi-value' }, [ - E('label', { - 'class': 'cbi-value-title', - 'for' : 'timeFilterRe', - }, _('Filter is regexp')), - E('div', { 'class': 'cbi-value-field' }, [ - E('div', { 'class': 'cbi-checkbox' }, [ - this.timeFilterRe, - E('label', {}), - ]), - E('div', { 'class': 'cbi-value-description' }, - _('Apply timestamp filter as regular expression') - ), - ]), - ]), + this.logConvertTimestampElem, + this.logTimeFilterElem, + this.logtimeFilterReElem, this.logHostsDropdownElem, this.logFacilitiesDropdownElem, this.logLevelsDropdownElem, @@ -810,7 +901,7 @@ return baseclass.extend({ }, _('Sorting entries')), E('div', { 'class': 'cbi-value-field' }, this.logSorting), ]), - ((this.isAutorefresh) ? + ((this.autorefreshOn) ? E('div', { 'class': 'cbi-value' }, [ E('label', { 'class': 'cbi-value-title', @@ -877,6 +968,25 @@ return baseclass.extend({ throw new Error('getLogHash must be overridden by a subclass'); }, + /** + * Converts the timestamp format. + * Abstract method, must be overridden by a subclass! + * + * To completely disable the convert timrstamp option, views extending + * this base class should overwrite the `convertTimestampFunc` function + * with `null`. + * + * @instance + * @abstract + * + * @param {string} t + * @returns {String} + * Returns the converted timestamp string. + */ + convertTimestampFunc(t) { + throw new Error('convertTimestampFunc must be overridden by a subclass'); + }, + async pollFunc() { await this.getLogHash().then(async hash => { if(this.lastHash !== hash) { @@ -895,7 +1005,8 @@ return baseclass.extend({ }; }; this.saveSettingsToLocalStorage( - this.tailInput.value, this.logSorting.value, this.autoRefresh.checked); + this.tailInput.value, this.logSorting.value, + this.autoRefresh.checked, this.convertTimestamp.checked); this.setFilterSettings(); this.fastTailValue = Number(this.tailValue); return this.reloadLog(Number(this.tailValue), true); @@ -911,10 +1022,13 @@ return baseclass.extend({ load() { this.restoreSettingsFromLocalStorage(); - if(!this.autoRefresh || typeof(this.getLogHash) != 'function') { - this.isAutorefresh = false; + if(!this.enableAutoRefresh || typeof(this.getLogHash) != 'function') { + this.autorefreshOn = false; this.autoRefreshValue = false; }; + if(this.enableConvertTimestamp && typeof(this.convertTimestampFunc) == 'function') { + this.convertTimestampOn = true; + }; return this.getLogData(this.tailValue); }, @@ -939,18 +1053,13 @@ return baseclass.extend({ this.tailInput.value = this.tailValue; ui.addValidator(this.tailInput, 'uinteger', true); - this.logHostsDropdownElem = ''; - this.logFacilitiesDropdownElem = ''; - this.logLevelsDropdownElem = ''; - if(this.isLevels) { - this.logLevelsDropdownElem = this.makeLogLevelsDropdownSection(); - }; - if(this.isFacilities) { - this.logFacilitiesDropdownElem = this.makeLogFacilitiesDropdownSection(); - }; - if(this.isHosts) { - this.logHostsDropdownElem = this.makeLogHostsDropdownSection(); - }; + this.convertTimestamp = E('input', { + 'id' : 'convertTimestamp', + 'name' : 'convertTimestamp', + 'type' : 'checkbox', + 'form' : 'logFilterForm', + }); + this.convertTimestamp.checked = this.convertTimestampValue; this.timeFilter = E('input', { 'id' : 'timeFilter', @@ -971,6 +1080,28 @@ return baseclass.extend({ this.setRegexpValidator(this.timeFilter, this.timeFilterRe); + this.logConvertTimestampElem = ''; + this.logTimeFilterElem = ''; + this.logtimeFilterReElem = ''; + this.logHostsDropdownElem = ''; + this.logFacilitiesDropdownElem = ''; + this.logLevelsDropdownElem = ''; + + if(this.logTimestampFlag) { + this.logConvertTimestampElem = this.makeLogConvertTimestampSection(); + this.logTimeFilterElem = this.makeLogTimeFilterSection(); + this.logtimeFilterReElem = this.makeLogtimeFilterReSection(); + }; + if(this.logLevelsFlag) { + this.logLevelsDropdownElem = this.makeLogLevelsDropdownSection(); + }; + if(this.logFacilitiesFlag) { + this.logFacilitiesDropdownElem = this.makeLogFacilitiesDropdownSection(); + }; + if(this.logHostsFlag) { + this.logHostsDropdownElem = this.makeLogHostsDropdownSection(); + }; + this.msgFilter = E('input', { 'id' : 'msgFilter', 'name' : 'msgFilter', @@ -1123,7 +1254,7 @@ return baseclass.extend({ ]) ); - if(this.isAutorefresh && this.autoRefreshValue) { + if(this.autorefreshOn && this.autoRefreshValue) { poll.add(this.pollFuncWrapper, this.pollInterval); }; diff --git a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-abstract.js b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-system.js similarity index 57% rename from luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-abstract.js rename to luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-system.js index 3c40261..34fb157 100644 --- a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-abstract.js +++ b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-system.js @@ -1,17 +1,18 @@ 'use strict'; 'require baseclass'; 'require fs'; -'require ui'; 'require view.ruantiblock.log-widget as widget'; return baseclass.extend({ view: widget.view.extend({ + enableAutoRefresh: true, + /** * Pattern for picking application-specific entries from the log. * * @property {string} appPattern */ - appPattern : '^', + appPattern : '^', /** * Enable "tail" option for the logread (logread -l). @@ -19,19 +20,19 @@ return baseclass.extend({ * * @property {bool} loggerTail */ - loggerTail : false, + loggerTail : false, - logdRegexp : new RegExp(/^([^\s]{3}\s+[^\s]{3}\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2}\s+\d{4})\s+([a-z0-9]+)\.([a-z]+)\s+(.*)$/), + logdRegexp : new RegExp(/^([^\s]{3}\s+[^\s]{3}\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2}\s+\d{4})\s+([a-z0-9]+)\.([a-z]+)\s+(.*)$/), - syslog_ngRegexp: new RegExp(/^([^\s]{3}\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2})\s+([^\s]+)\s+(.*)$/), + syslog_ngRegexp : new RegExp(/^([^\s]{3}\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2})\s+([^\s]+)\s+(.*)$/), - entryRegexp : null, + entryRegexp : null, - isLoggerChecked: false, + loggerChecked : false, - entriesHandler : null, + entriesHandler : null, - logger : null, + logger : null, getLogHash() { return this.getLogData(1, true).then(data => { @@ -66,6 +67,18 @@ return baseclass.extend({ ]; }, + // unsupported log handler + unsupportedLogHandler(line, lineNum) { + return [ + lineNum, // # (Number) + null, // Timestamp (String) + null, // Host (String) + null, // Facility (String) + null, // Level (String) + this.htmlEntities(line) || ' ', // Message (String) + ]; + }, + checkLogread() { return Promise.all([ L.resolveDefault(fs.stat('/sbin/logread'), null), @@ -102,8 +115,7 @@ return baseclass.extend({ return []; }; - let unsupportedLog = false; - let strings = logdata.trim().split(/\n/); + let strings = logdata.trim().split(/\n/); if(!this.loggerTail && tail && tail > 0 && strings) { strings = strings.slice(-tail); @@ -111,45 +123,61 @@ return baseclass.extend({ this.totalLogLines = strings.length; - let entriesArray = strings.map((e, i) => { - if(!this.isLoggerChecked) { + let entriesArray = strings.map((e, i) => { + if(!this.loggerChecked) { if(this.logdRegexp.test(e)) { - this.entryRegexp = this.logdRegexp; - this.isFacilities = true; - this.isLevels = true; - this.logHosts = {}; - this.entriesHandler = this.logdHandler; + this.entryRegexp = this.logdRegexp; + this.logTimestampFlag = true; + this.logFacilitiesFlag = true; + this.logLevelsFlag = true; + this.logHostsFlag = false; + this.logHosts = {}; + this.entriesHandler = this.logdHandler; + this.logCols = [ + '#', + _('Timestamp'), + null, + _('Facility'), + _('Level'), + _('Message'), + ]; + this.loggerChecked = true; } else if(this.syslog_ngRegexp.test(e)) { - this.entryRegexp = this.syslog_ngRegexp; - this.isHosts = true; - this.logFacilities = {}; - this.logLevels = {}; - this.entriesHandler = this.syslog_ngHandler; + this.entryRegexp = this.syslog_ngRegexp; + this.logTimestampFlag = true; + this.logFacilitiesFlag = false; + this.logLevelsFlag = false; + this.logHostsFlag = true; + this.logFacilities = {}; + this.logLevels = {}; + this.entriesHandler = this.syslog_ngHandler; + this.logCols = [ + '#', + _('Timestamp'), + _('Host'), + null, + null, + _('Message'), + ]; + this.loggerChecked = true; } else { - unsupportedLog = true; - return; + return this.unsupportedLogHandler(e, i + 1); }; - this.isLoggerChecked = true; }; let strArray = e.match(this.entryRegexp); if(strArray) { return this.entriesHandler(strArray, i + 1); } else { - unsupportedLog = true; - return; + return this.unsupportedLogHandler(e, i + 1); }; }); - if(unsupportedLog) { - throw new Error(_('Unable to load log data:') + ' ' + _('Unsupported log format')); - } else { - if(this.logSortingValue === 'desc') { - entriesArray.reverse(); - }; - return entriesArray; + if(this.logSortingValue == 'desc') { + entriesArray.reverse(); }; + return entriesArray; }, }), }); diff --git a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-widget.js b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-widget.js index 5a8e6c5..3aab422 100644 --- a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-widget.js +++ b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-widget.js @@ -46,38 +46,50 @@ return baseclass.extend({ if(e[4] in this.logLevels) { this.logLevelsStat[e[4]] = this.logLevelsStat[e[4]] + 1; }; - lines.push( - `