update 2026-05-27 23:25:57

This commit is contained in:
action committed 2026-05-27 23:25:57 +08:00
1 parent 84f707b52b
commit d0159b49a5
759 files changed
+362365

No files matched your search

@@ -0,0 +1,9 @@
'use strict';
'require view.log-viewer.log-system as abc';
return abc.view.extend({
viewName : 'dropbear',
title : _('Dropbear'),
enableAutoRefresh: false,
appPattern : 'dropbear\[[0-9]*\]:',
});
@@ -0,0 +1,10 @@
{
"admin/status/log-viewer/dropbear": {
"title": "Dropbear",
"order": 3,
"action": {
"type": "view",
"path": "log-viewer/dropbear"
}
}
}
@@ -0,0 +1,113 @@
'use strict';
'require baseclass';
'require ui';
'require view.log-viewer.log-base as base';
document.head.append(E('style', {'type': 'text/css'},
`
:root {
--app-log-entry-outline-color: #ccc;
}
:root[data-darkmode="true"] {
--app-log-entry-outline-color: #555;
}
#log-area {
width: 100%;
height: 100%;
margin-bottom: 1em;
}
.log-entry-line {
display: inline-block;
text-indent: 8px;
margin: 0 0 1px 0;
padding: 0 4px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
border: 1px solid var(--app-log-entry-outline-color);
font-weight: normal;
/*font-size: 12px !important;*/
font-family: monospace !important;
white-space: pre-wrap !important;
overflow-wrap: anywhere !important;
}
`));
return baseclass.extend({
view: base.view.extend({
filterHighlightFunc(match) {
return `<span class="log-highlight-item">${match}</span>`;
},
padNumber(number, lengthFirst, lengthLast) {
let length = Math.max(lengthFirst, lengthLast);
try {
number = String(number).padStart(length, ' ');
} catch(e) {
if(e.name != 'TypeError') {
throw e;
};
};
return number;
},
makeLogArea(logdataArray) {
let lines = `<span class="log-entry-line center" style="width:100%">${_('No entries available...')}</span>`;
let logArea = E('div', { 'id': 'log-area' });
for(let level of Object.keys(this.logLevels)) {
this.logLevelsStat[level] = 0;
};
let logdataArrayLen = logdataArray.length;
if(logdataArray.length > 0) {
lines = [];
let firstNumLength = String(logdataArray[0][0]).length;
let lastNumLength = String(logdataArray[logdataArrayLen - 1][0]).length;
logdataArray.forEach((e, i) => {
if(e[4] in this.logLevels) {
this.logLevelsStat[e[4]] = this.logLevelsStat[e[4]] + 1;
};
e[0] = this.padNumber(e[0], firstNumLength, lastNumLength);
if(e[5]) {
e[5] = `&#9;${e[5]}`;
};
lines.push(
`<span class="log-entry-line log-${(e[1]) ? e[4] || 'empty' : (this.entriesHandler) ? 'raw' : 'empty'}">` +
e.filter(i => (i)).join(' ') +
'</span>'
);
});
lines = lines.join('<br />');
};
try {
logArea.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 += `<span class="log-entries-count-level log-${e[0]}" title="${e[0]}">${e[1]}</span>`;
};
});
};
return E([
E('div', { 'class': 'log-entries-count' },
`${_('Entries')}: ${logdataArray.length} / ${this.totalLogLines}${levelsStatString}`
),
logArea,
]);
},
}),
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

@@ -0,0 +1,9 @@
'use strict';
'require view.log-viewer.multilog.log-multilog as abc';
return abc.view.extend({
viewName : 'multilog-boot',
title : _('Log') + ' - ' + _('boot.log'),
autoRefresh: false,
logFile : '/var/log/boot.log',
});
@@ -0,0 +1,9 @@
'use strict';
'require view.log-viewer.multilog.log-multilog as abc';
return abc.view.extend({
viewName : 'multilog-cron',
title : _('Log') + ' - ' + _('cron'),
autoRefresh: false,
logFile : '/var/log/cron',
});
@@ -0,0 +1,25 @@
'use strict';
'require baseclass';
'require fs';
'require view.log-viewer.log-system as abc';
return baseclass.extend({
view: abc.view.extend({
/**
* Log file.
*
* @property {string} logFile
*/
logFile: null,
getLogHash() {
return fs.stat(this.logFile).then((data) => {
return data.mtime || '';
}).catch(e => {});
},
getLogData(tail) {
return L.resolveDefault(fs.read_direct(this.logFile, 'text'), '');
},
}),
});
@@ -0,0 +1,9 @@
'use strict';
'require view.log-viewer.multilog.log-multilog as abc';
return abc.view.extend({
viewName : 'multilog-maillog',
title : _('Log') + ' - ' + _('maillog'),
autoRefresh: false,
logFile : '/var/log/maillog',
});
@@ -0,0 +1,9 @@
'use strict';
'require view.log-viewer.multilog.log-multilog as abc';
return abc.view.extend({
viewName : 'multilog-messages',
title : _('Log') + ' - ' + _('messages'),
autoRefresh: true,
logFile : '/var/log/messages',
});
@@ -0,0 +1,9 @@
'use strict';
'require view.log-viewer.multilog.log-multilog as abc';
return abc.view.extend({
viewName : 'multilog-secure',
title : _('Log') + ' - ' + _('secure'),
autoRefresh: false,
logFile : '/var/log/secure',
});
@@ -0,0 +1,67 @@
{
"admin/status/log-viewer": {
"title": "Log Viewer",
"order": 5,
"action": {
"type": "alias",
"path": "admin/status/log-viewer/messages"
},
"depends": {
"acl": [ "luci-app-log-viewer" ]
}
},
"admin/status/log-viewer/messages": {
"title": "messages",
"order": 1,
"action": {
"type": "view",
"path": "log-viewer/multilog/messages"
}
},
"admin/status/log-viewer/secure": {
"title": "secure",
"order": 2,
"action": {
"type": "view",
"path": "log-viewer/multilog/secure"
}
},
"admin/status/log-viewer/maillog": {
"title": "maillog",
"order": 3,
"action": {
"type": "view",
"path": "log-viewer/multilog/maillog"
}
},
"admin/status/log-viewer/cron": {
"title": "cron",
"order": 4,
"action": {
"type": "view",
"path": "log-viewer/multilog/cron"
}
},
"admin/status/log-viewer/boot": {
"title": "boot",
"order": 5,
"action": {
"type": "view",
"path": "log-viewer/multilog/boot"
}
},
"admin/status/log-viewer/dmesg": {
"title": "kernel",
"order": 6,
"action": {
"type": "view",
"path": "log-viewer/dmesg"
}
}
}
@@ -0,0 +1,22 @@
{
"luci-app-log-viewer": {
"description": "Grant access to log-viewer procedures",
"read": {
"cgi-io": [ "exec" ],
"file": {
"/sbin/logread*": [ "exec" ],
"/usr/sbin/logread*": [ "exec" ],
"/bin/dmesg -r": [ "exec" ],
"/var/log/messages": [ "read" ],
"/var/log/secure": [ "read" ],
"/var/log/maillog": [ "read" ],
"/var/log/cron": [ "read" ],
"/var/log/boot.log": [ "read" ]
},
"ubus": {
"system": [ "info" ],
"luci.log-viewer": [ "getSyslogSize", "getSyslogHash", "getDmesgSize", "getDmesgHash", "getLogfileSize" ]
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

@@ -0,0 +1,85 @@
'use strict';
'require baseclass';
'require ui';
'require view.log-viewer.log-base as base';
return baseclass.extend({
view: base.view.extend({
rowsDefault: 20,
htmlEntities(str) {
return str;
},
filterHighlightFunc(match) {
return `►${match}◄`;
},
padNumber(number, lengthFirst, lengthLast) {
let length = Math.max(lengthFirst, lengthLast);
try {
number = String(number).padStart(length, ' ');
} catch(e) {
if(e.name != 'TypeError') {
throw e;
};
};
return number;
},
makeLogArea(logdataArray) {
let lines = _('No entries available...');
let logTextarea = E('textarea', {
'id' : 'syslog',
'class' : 'cbi-input-textarea',
'style' : 'width:100% !important; margin-bottom:1em; resize:none; font-size:12px; font-family:monospace !important',
'readonly' : 'readonly',
'wrap' : 'off',
'rows' : this.rowsDefault,
'spellcheck': 'false',
});
for(let level of Object.keys(this.logLevels)) {
this.logLevelsStat[level] = 0;
};
let logdataArrayLen = logdataArray.length;
if(logdataArrayLen > 0) {
lines = [];
let firstNumLength = String(logdataArray[0][0]).length;
let lastNumLength = String(logdataArray[logdataArrayLen - 1][0]).length;
logdataArray.forEach((e, i) => {
if(e[4] in this.logLevels) {
this.logLevelsStat[e[4]] = this.logLevelsStat[e[4]] + 1;
};
e[0] = this.padNumber(e[0], firstNumLength, lastNumLength);
if(e[5]) {
e[5] = `\t${e[5]}`;
};
lines.push(e.filter(i => (i)).join(' '));
});
lines = lines.join('\r\n');
};
logTextarea.value = lines;
logTextarea.rows = ((logdataArrayLen + 1) < this.rowsDefault) ? this.rowsDefault : logdataArrayLen + 1;
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 += `<span class="log-entries-count-level log-${e[0]}" title="${e[0]}">${e[1]}</span>`;
};
});
};
return E([
E('div', { 'class': 'log-entries-count' },
`${_('Entries')}: ${logdataArrayLen} / ${this.totalLogLines}${levelsStatString}`
),
logTextarea,
]);
},
}),
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB