From ea2e82e784028bc83e5f12611ed3182da6d6bfe3 Mon Sep 17 00:00:00 2001 From: gSpot Date: Fri, 10 Nov 2023 02:39:58 +0300 Subject: [PATCH] luci-app: update log. --- autoinstall/current/autoinstall.sh | 2 +- luci-app-ruantiblock/Makefile | 2 +- .../{abstract-log.js => log-base.js} | 236 +++++++----------- .../resources/view/ruantiblock/log-widget.js | 111 ++++++++ .../resources/view/ruantiblock/log.js | 2 +- 5 files changed, 205 insertions(+), 148 deletions(-) rename luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/{abstract-log.js => log-base.js} (82%) create mode 100644 luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-widget.js diff --git a/autoinstall/current/autoinstall.sh b/autoinstall/current/autoinstall.sh index d012682..316cc4f 100755 --- a/autoinstall/current/autoinstall.sh +++ b/autoinstall/current/autoinstall.sh @@ -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-1" +RUAB_LUCI_APP_VERSION="1.3-2" BASE_URL="https://raw.githubusercontent.com/gSpotx2f/packages-openwrt/master" PKG_DIR="/tmp" diff --git a/luci-app-ruantiblock/Makefile b/luci-app-ruantiblock/Makefile index ab37433..a74232e 100644 --- a/luci-app-ruantiblock/Makefile +++ b/luci-app-ruantiblock/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk -PKG_VERSION:=1.3-1 +PKG_VERSION:=1.3-2 LUCI_TITLE:=LuCI support for ruantiblock LUCI_DEPENDS:=+ruantiblock LUCI_PKGARCH:=all diff --git a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/abstract-log.js b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-base.js similarity index 82% rename from luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/abstract-log.js rename to luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-base.js index ffbf64e..93a6aa2 100644 --- a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/abstract-log.js +++ b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-base.js @@ -17,6 +17,7 @@ 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-entries-count-border: #ccc; } :root[data-darkmode="true"] { --app-log-dark-font-color: #fff; @@ -30,34 +31,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; -} -.log-entry-empty { -} -.log-entry-number { - min-width: 4em !important; -} -.log-entry-time { - min-width: 15em !important; -} -.log-entry-host { - min-width: 10em !important; -} -.log-entry-host-cell { - word-break: break-all !important; - word-wrap: break-word !important; -} -.log-entry-log-level { - max-width: 5em !important; -} -.log-entry-facility{ - max-width: 7em !important; -} -.log-entry-message { - min-width: 25em !important; -} -.log-entry-message-cell { - overflow-x: hidden !important; - text-overflow: ellipsis !important; + --app-log-entries-count-border: #555; } .log-empty { } @@ -123,6 +97,7 @@ log-emerg td { } .log-info { background-color: var(--app-log-info) !important; + /*color: var(--app-log-dark-font-color) !important;*/ } .log-debug { background-color: var(--app-log-debug) !important; @@ -150,13 +125,25 @@ log-emerg td { -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; - border: 1px solid #ccc; + border: 1px solid var(--app-log-entries-count-border); font-weight: normal; } .log-host-dropdown-item { } .log-facility-dropdown-item { } +.log-scroll-block { + position: fixed; + z-index: 1 !important; + opacity: 0.7; +} +.log-scroll-btn { + position: relative; + display: block; + margin: 0 !important; + left: 1px; + top: 1px; +} `)); return baseclass.extend({ @@ -325,7 +312,7 @@ return baseclass.extend({ * * @param {number} tail * @returns {string} - * Returns the raw content of the log + * Returns the raw content of the log. */ getLogData(tail) { throw new Error('getLogData must be overridden by a subclass'); @@ -338,12 +325,49 @@ return baseclass.extend({ * @param {string} logdata * @param {number} tail * @returns {Array} - * Returns an array of values: [ #, Timestamp, Host, Level, Facility, Message ] + * Returns an array of values: [ #, Timestamp, Host, Level, Facility, Message ]. */ parseLogData(logdata, tail) { throw new Error('parseLogData must be overridden by a subclass'); }, + /** + * Highlight the result of a regular expression. + * Abstract method, must be overridden by a subclass! + * + * @param {string} logdata + * @returns {string} + * Returns a string with the highlighted part. + */ + regexpFilterHighlightFunc(match) { + throw new Error('parseLogData must be overridden by a subclass'); + }, + + setRegexpFilter(entriesArray, fieldNum, pattern) { + let fArr = []; + try { + let regExp = new RegExp(pattern, 'giu'); + entriesArray.forEach((e, i) => { + if(e[fieldNum] !== null && regExp.test(e[fieldNum])) { + if(this.regexpFilterHighlightFunc) { + e[fieldNum] = e[fieldNum].replace(regExp, this.regexpFilterHighlightFunc); + }; + fArr.push(e); + }; + regExp.lastIndex = 0; + }); + } catch(err) { + if(err.name === 'SyntaxError') { + ui.addNotification(null, + E('p', {}, _('Invalid regular expression') + ': ' + err.message)); + return entriesArray; + } else { + throw err; + }; + }; + return fArr; + }, + setDateFilter(entriesArray) { let fPattern = document.getElementById('timeFilter').value; if(!fPattern) { @@ -389,35 +413,6 @@ return baseclass.extend({ return entriesArray; }, - regexpFilterHighlightFunc(match) { - return `${match}`; - }, - - setRegexpFilter(entriesArray, fieldNum, pattern) { - let fArr = []; - try { - let regExp = new RegExp(pattern, 'giu'); - entriesArray.forEach((e, i) => { - if(e[fieldNum] !== null && regExp.test(e[fieldNum])) { - if(this.regexpFilterHighlightFunc) { - e[fieldNum] = e[fieldNum].replace(regExp, this.regexpFilterHighlightFunc); - }; - fArr.push(e); - }; - regExp.lastIndex = 0; - }); - } catch(err) { - if(err.name === 'SyntaxError') { - ui.addNotification(null, - E('p', {}, _('Invalid regular expression') + ': ' + err.message)); - return entriesArray; - } else { - throw err; - }; - }; - return fArr; - }, - setMsgFilter(entriesArray) { let fPattern = document.getElementById('msgFilter').value; if(!fPattern) { @@ -426,70 +421,16 @@ return baseclass.extend({ return this.setRegexpFilter(entriesArray, 5, fPattern); }, + /** + * Creates the contents of the log area. + * Abstract method, must be overridden by a subclass! + * + * @param {Array} logdataArray + * @returns {Node} + * Returns a DOM node containing the log area. + */ makeLogArea(logdataArray) { - let lines = `${_('No entries available...')}`; - let logTable = E('table', { 'id': 'logTable', 'class': 'table' }); - - for(let level of Object.keys(this.logLevels)) { - this.logLevelsStat[level] = 0; - }; - - if(logdataArray.length > 0) { - lines = []; - logdataArray.forEach((e, i) => { - if(e[4] in this.logLevels) { - this.logLevelsStat[e[4]] = this.logLevelsStat[e[4]] + 1; - }; - - lines.push( - `${e[0]}` + - ((e[1]) ? `${e[1]}` : '') + - ((e[2]) ? `${e[2]}` : '') + - ((e[3]) ? `${e[3]}` : '') + - ((e[4]) ? `${e[4]}` : '') + - ((e[5]) ? `${e[5]}` : '') + - `` - ); - }); - lines = lines.join(''); - - logTable.append( - E('tr', { 'class': 'tr table-titles' }, [ - E('th', { 'class': 'th left log-entry-number' }, '#'), - (logdataArray[0][1]) ? E('th', { 'class': 'th left log-entry-time' }, _('Timestamp')) : '', - (logdataArray[0][2]) ? E('th', { 'class': 'th left log-entry-host' }, _('Host')) : '', - (logdataArray[0][4]) ? E('th', { 'class': 'th left log-entry-facility' }, _('Facility')) : '', - (logdataArray[0][3]) ? E('th', { 'class': 'th left log-entry-log-level' }, _('Level')) : '', - (logdataArray[0][5]) ? E('th', { 'class': 'th left log-entry-message' }, _('Message')) : '', - ]) - ); - }; - - try { - logTable.insertAdjacentHTML('beforeend', lines); - } catch(err) { - if(err.name === 'SyntaxError') { - ui.addNotification(null, - E('p', {}, _('HTML/XML error') + ': ' + err.message), 'error'); - }; - throw err; - }; - - let levelsStatString = ''; - if((Object.values(this.logLevelsStat).reduce((s,c) => s + c, 0)) > 0) { - Object.entries(this.logLevelsStat).forEach(e => { - if(e[0] in this.logLevels && e[1] > 0) { - levelsStatString += `${e[1]}`; - }; - }); - }; - - return E([ - E('div', { 'class': 'log-entries-count' }, - `${_('Entries')}: ${logdataArray.length} / ${this.totalLogLines}${levelsStatString}` - ), - logTable, - ]); + throw new Error('parseLogData must be overridden by a subclass'); }, downloadLog(ev) { @@ -529,7 +470,7 @@ return baseclass.extend({ render(logdata) { let logWrapper = E('div', { 'id' : 'logWrapper', - 'style': 'width:100%; min-height:20em; padding: 0 0 0 45px; font-size:0.9em !important' + 'style': 'width:100%; min-height:20em' }, this.makeLogArea(this.parseLogData(logdata, this.tailValue))); let tailInput = E('input', { @@ -670,6 +611,29 @@ return baseclass.extend({ }); }; + document.body.append( + E('div', { + 'class': 'log-scroll-block', + 'style': 'right:1px; top:' + (window.innerHeight / 4 * 3) + 'px', + }, [ + E('button', { + 'class': 'btn log-scroll-btn', + 'click': ev => { + document.getElementById('logTitle').scrollIntoView(true); + ev.target.blur(); + }, + }, '↑'), + E('button', { + 'class': 'btn log-scroll-btn', + 'style': 'margin-top:1px !important', + 'click': ev => { + logWrapper.scrollIntoView(false); + ev.target.blur(); + }, + }, '↓'), + ]) + ); + return E([ E('h2', { 'id': 'logTitle', 'class': 'fade-in' }, this.title), E('div', { 'class': 'cbi-section-descr fade-in' }), @@ -740,27 +704,9 @@ return baseclass.extend({ ]) ), E('div', { 'class': 'cbi-section fade-in' }, - E('div', { 'class': 'cbi-section-node' }, [ - E('div', { 'style': 'position:fixed; z-index:1 !important' }, [ - E('button', { - 'class': 'btn', - 'style': 'position:relative; display:block; margin:0 !important; left:1px; top:1px', - 'click': ev => { - document.getElementById('logTitle').scrollIntoView(true); - ev.target.blur(); - }, - }, '↑'), - E('button', { - 'class': 'btn', - 'style': 'position:relative; display:block; margin:0 !important; margin-top:1px !important; left:1px; top:1px', - 'click': ev => { - logWrapper.scrollIntoView(false); - ev.target.blur(); - }, - }, '↓'), - ]), - logWrapper, - ]) + E('div', { 'class': 'cbi-section-node' }, + logWrapper + ) ), E('div', { 'class': 'cbi-section fade-in' }, E('div', { 'class': 'cbi-section-node' }, 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 new file mode 100644 index 0000000..c226eb4 --- /dev/null +++ b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log-widget.js @@ -0,0 +1,111 @@ +'use strict'; +'require baseclass'; +'require ui'; +'require view.log.log-base as abc'; + +document.head.append(E('style', {'type': 'text/css'}, +` +.log-entry-empty { +} +.log-entry-number { + min-width: 4em !important; +} +.log-entry-time { + min-width: 15em !important; +} +.log-entry-host { + min-width: 10em !important; +} +.log-entry-host-cell { + word-break: break-all !important; + word-wrap: break-word !important; +} +.log-entry-log-level { + max-width: 5em !important; +} +.log-entry-facility{ + max-width: 7em !important; +} +.log-entry-message { + min-width: 25em !important; +} +.log-entry-message-cell { + overflow-x: hidden !important; + text-overflow: ellipsis !important; +} +`)); + +return baseclass.extend({ + view: abc.view.extend({ + + regexpFilterHighlightFunc(match) { + return `${match}`; + }, + + makeLogArea(logdataArray) { + let lines = `${_('No entries available...')}`; + let logTable = E('table', { 'id': 'logTable', 'class': 'table' }); + + for(let level of Object.keys(this.logLevels)) { + this.logLevelsStat[level] = 0; + }; + + if(logdataArray.length > 0) { + lines = []; + logdataArray.forEach((e, i) => { + if(e[4] in this.logLevels) { + this.logLevelsStat[e[4]] = this.logLevelsStat[e[4]] + 1; + }; + + lines.push( + `${e[0]}` + + ((e[1]) ? `${e[1]}` : '') + + ((e[2]) ? `${e[2]}` : '') + + ((e[3]) ? `${e[3]}` : '') + + ((e[4]) ? `${e[4]}` : '') + + ((e[5]) ? `${e[5]}` : '') + + '' + ); + }); + lines = lines.join(''); + + logTable.append( + E('tr', { 'class': 'tr table-titles' }, [ + E('th', { 'class': 'th left log-entry-number' }, '#'), + (logdataArray[0][1]) ? E('th', { 'class': 'th left log-entry-time' }, _('Timestamp')) : '', + (logdataArray[0][2]) ? E('th', { 'class': 'th left log-entry-host' }, _('Host')) : '', + (logdataArray[0][3]) ? E('th', { 'class': 'th left log-entry-facility' }, _('Facility')) : '', + (logdataArray[0][4]) ? E('th', { 'class': 'th left log-entry-log-level' }, _('Level')) : '', + (logdataArray[0][5]) ? E('th', { 'class': 'th left log-entry-message' }, _('Message')) : '', + ]) + ); + }; + + try { + logTable.insertAdjacentHTML('beforeend', lines); + } catch(err) { + if(err.name === 'SyntaxError') { + ui.addNotification(null, + E('p', {}, _('HTML/XML error') + ': ' + err.message), 'error'); + }; + throw err; + }; + + let levelsStatString = ''; + if((Object.values(this.logLevelsStat).reduce((s,c) => s + c, 0)) > 0) { + Object.entries(this.logLevelsStat).forEach(e => { + if(e[0] in this.logLevels && e[1] > 0) { + levelsStatString += `${e[1]}`; + }; + }); + }; + + return E([ + E('div', { 'class': 'log-entries-count' }, + `${_('Entries')}: ${logdataArray.length} / ${this.totalLogLines}${levelsStatString}` + ), + logTable, + ]); + }, + }), +}); diff --git a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log.js b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log.js index 54808b2..05225ce 100644 --- a/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log.js +++ b/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log.js @@ -1,6 +1,6 @@ 'require fs'; 'require ui'; -'require view.ruantiblock.abstract-log as abc'; +'require view.ruantiblock.log-widget as abc'; 'require view.ruantiblock.tools as tools'; return abc.view.extend({