feat(webapp): Dashboard improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-23 06:56:49 +01:00
parent 3132ef9c14
commit 35eb1f79b2
3 changed files with 83 additions and 44 deletions

View File

@ -40,3 +40,21 @@ When checking if a port is listening, use this order of fallbacks:
- OpenWrt uses `logread` instead of traditional log files - OpenWrt uses `logread` instead of traditional log files
- Use `logread -l N` to get last N lines - Use `logread -l N` to get last N lines
- CrowdSec writes to `/var/log/crowdsec.log` - CrowdSec writes to `/var/log/crowdsec.log`
## Build & Sync Workflow
### Local Feeds Hygiene
- Clean and resync local feeds before build iterations when dependency drift is suspected
- Prefer the repo helpers; avoid ad-hoc `rm` unless explicitly needed
### Local Build Flow
- Use `./secubox-tools/local-build.sh build <module>` for cached SDK builds
- If CI parity is required, use `make package/<module>/compile V=s`
### Sync Build Artifacts
- After building, synchronize results into the build output folder used by local-build.sh
- Use the repo sync helper scripts where available to avoid missing `root/` vs `htdocs/` payloads
### Toolchain Usage
- Use the OpenWrt toolchain when a module requires it (native SDK packages, toolchain-bound dependencies)
- If unsure, start with `local-build.sh`; fall back to full toolchain builds when SDK cache cannot resolve dependencies

View File

@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-webapp PKG_NAME:=secubox-app-webapp
PKG_VERSION:=1.5.0 PKG_VERSION:=1.5.0
PKG_RELEASE:=3 PKG_RELEASE:=6
PKG_LICENSE:=MIT PKG_LICENSE:=MIT
PKG_MAINTAINER:=CyberMind.FR <contact@cybermind.fr> PKG_MAINTAINER:=CyberMind.FR <contact@cybermind.fr>

View File

@ -3369,15 +3369,20 @@
// UBUS returns [code, result] - code 0 means success // UBUS returns [code, result] - code 0 means success
if (data.result && Array.isArray(data.result)) { if (data.result && Array.isArray(data.result)) {
if (data.result[0] === 0) { const code = data.result[0];
if (code === 0) {
return data.result[1] || {}; return data.result[1] || {};
} else if (data.result[0] === 6) { } else if (code === 6) {
// Session expired // Session expired
throw new Error('SESSION_EXPIRED'); throw new Error('SESSION_EXPIRED');
} else {
// Other error codes: 1=invalid cmd, 2=invalid arg, 4=not found, 5=no data, 7=permission denied
console.warn(`UBUS error code ${code} for ${object}.${method}`);
return null;
} }
} }
return data.result || data; return data.result?.[1] || null;
} catch (error) { } catch (error) {
console.error(`UBUS Error [${object}.${method}]:`, error); console.error(`UBUS Error [${object}.${method}]:`, error);
throw error; throw error;
@ -4138,47 +4143,44 @@
const countBadge = document.getElementById('adminSessionsCount'); const countBadge = document.getElementById('adminSessionsCount');
try { try {
// Get LuCI sessions by listing session files
const result = await UBUS.execCommand('/bin/sh', ['-c',
'for f in /tmp/luci-sessions/*; do [ -f "$f" ] && echo "$(basename $f)|$(cat $f 2>/dev/null | jsonfilter -e @.data.username 2>/dev/null)|$(cat $f 2>/dev/null | jsonfilter -e @.data.localtime 2>/dev/null)|$(stat -c %Y $f 2>/dev/null)"; done 2>/dev/null || ls -la /tmp/luci-sessions/ 2>/dev/null'
]);
let sessions = []; let sessions = [];
if (result && result.stdout) { // Get current session info via session.get
const lines = result.stdout.trim().split('\n').filter(l => l && l.includes('|'));
sessions = lines.map(line => {
const parts = line.split('|');
return {
id: parts[0] || 'unknown',
username: parts[1] || 'root',
ip: parts[2] || 'local',
mtime: parseInt(parts[3]) || 0
};
}).filter(s => s.id !== 'unknown');
}
// Also try to get session via ubus session.list (requires admin)
try { try {
const ubusResult = await UBUS.call('session', 'list', {}); const sessionData = await UBUS.call('session', 'get', {});
if (ubusResult && typeof ubusResult === 'object') { if (sessionData) {
// Add current session info // Extract username from session data
let username = 'root';
if (sessionData.data && sessionData.data.username) {
username = sessionData.data.username;
} else if (sessionData.values && sessionData.values.username) {
username = sessionData.values.username;
}
const currentSession = { const currentSession = {
id: STATE.sessionId ? STATE.sessionId.substring(0, 8) : 'current', id: STATE.sessionId ? STATE.sessionId.substring(0, 8) : 'current',
username: ubusResult.username || 'root', username: username,
ip: window.location.hostname, ip: window.location.hostname,
mtime: Math.floor(Date.now() / 1000), mtime: Math.floor(Date.now() / 1000),
expires: ubusResult.expires || 300, expires: sessionData.expires || 300,
current: true current: true
}; };
sessions.push(currentSession);
// Add if not already in list
if (!sessions.find(s => s.current)) {
sessions.unshift(currentSession);
}
} }
} catch (e) { } catch (e) {
// Session.list might not be available console.warn('session.get error:', e);
}
// If no session from API, add current session from STATE (user IS logged in)
if (sessions.length === 0 && STATE.sessionId) {
sessions.push({
id: STATE.sessionId.substring(0, 8),
username: 'root',
ip: window.location.hostname,
mtime: Math.floor(Date.now() / 1000),
expires: 300,
current: true
});
} }
// Update badge // Update badge
@ -4326,8 +4328,8 @@
if (result && result.logs && Array.isArray(result.logs)) { if (result && result.logs && Array.isArray(result.logs)) {
lines = result.logs; lines = result.logs;
} else { } else {
// Fallback: try file.exec with logread // Fallback: use shell to get last 20 lines (logread -l N not available on BusyBox)
const logResult = await UBUS.execCommand('/sbin/logread', ['-l', '20']).catch(() => null); const logResult = await UBUS.execCommand('/bin/sh', ['-c', 'logread | tail -n 20']).catch(() => null);
if (logResult && logResult.stdout) { if (logResult && logResult.stdout) {
lines = logResult.stdout.trim().split('\n').filter(l => l); lines = logResult.stdout.trim().split('\n').filter(l => l);
} }
@ -5003,17 +5005,35 @@
async function loadStorageInfo() { async function loadStorageInfo() {
try { try {
const result = await UBUS.execCommand('/bin/df', ['-h']); // Try luci.system-hub RPC first
const rpcResult = await UBUS.call('luci.system-hub', 'get_storage_info').catch(() => null);
if (rpcResult && rpcResult.filesystems) {
rpcResult.filesystems.forEach(fs => {
if (fs.mount === '/') {
document.getElementById('storageRoot').textContent = `${fs.used_human || fs.used} / ${fs.size_human || fs.size} (${fs.percent || '--'}%)`;
} else if (fs.mount === '/tmp') {
document.getElementById('storageTmp').textContent = `${fs.used_human || fs.used} / ${fs.size_human || fs.size} (${fs.percent || '--'}%)`;
} else if (fs.mount === '/overlay') {
document.getElementById('storageOverlay').textContent = `${fs.used_human || fs.used} / ${fs.size_human || fs.size} (${fs.percent || '--'}%)`;
}
});
return;
}
// Fallback: use shell command
const result = await UBUS.execCommand('/bin/sh', ['-c', 'df -h']).catch(() => null);
if (result && result.stdout) { if (result && result.stdout) {
const lines = result.stdout.trim().split('\n'); const lines = result.stdout.trim().split('\n');
lines.forEach(line => { lines.forEach(line => {
const parts = line.split(/\s+/); const parts = line.split(/\s+/);
if (parts[5] === '/') { if (parts.length >= 6) {
document.getElementById('storageRoot').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`; if (parts[5] === '/') {
} else if (parts[5] === '/tmp') { document.getElementById('storageRoot').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`;
document.getElementById('storageTmp').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`; } else if (parts[5] === '/tmp') {
} else if (parts[5] === '/overlay') { document.getElementById('storageTmp').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`;
document.getElementById('storageOverlay').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`; } else if (parts[5] === '/overlay') {
document.getElementById('storageOverlay').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`;
}
} }
}); });
} }
@ -5577,7 +5597,8 @@
const container = document.getElementById('fullLogsContainer'); const container = document.getElementById('fullLogsContainer');
try { try {
const result = await UBUS.execCommand('/sbin/logread', ['-l', '100']); // Use shell to get last 100 lines (logread -l N not available on BusyBox)
const result = await UBUS.execCommand('/bin/sh', ['-c', 'logread | tail -n 100']);
if (result && result.stdout) { if (result && result.stdout) {
allLogs = result.stdout.trim().split('\n').filter(l => l).reverse(); allLogs = result.stdout.trim().split('\n').filter(l => l).reverse();