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
- Use `logread -l N` to get last N lines
- 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_VERSION:=1.5.0
PKG_RELEASE:=3
PKG_RELEASE:=6
PKG_LICENSE:=MIT
PKG_MAINTAINER:=CyberMind.FR <contact@cybermind.fr>

View File

@ -3369,15 +3369,20 @@
// UBUS returns [code, result] - code 0 means success
if (data.result && Array.isArray(data.result)) {
if (data.result[0] === 0) {
const code = data.result[0];
if (code === 0) {
return data.result[1] || {};
} else if (data.result[0] === 6) {
} else if (code === 6) {
// 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) {
console.error(`UBUS Error [${object}.${method}]:`, error);
throw error;
@ -4138,47 +4143,44 @@
const countBadge = document.getElementById('adminSessionsCount');
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 = [];
if (result && result.stdout) {
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)
// Get current session info via session.get
try {
const ubusResult = await UBUS.call('session', 'list', {});
if (ubusResult && typeof ubusResult === 'object') {
// Add current session info
const sessionData = await UBUS.call('session', 'get', {});
if (sessionData) {
// 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 = {
id: STATE.sessionId ? STATE.sessionId.substring(0, 8) : 'current',
username: ubusResult.username || 'root',
username: username,
ip: window.location.hostname,
mtime: Math.floor(Date.now() / 1000),
expires: ubusResult.expires || 300,
expires: sessionData.expires || 300,
current: true
};
// Add if not already in list
if (!sessions.find(s => s.current)) {
sessions.unshift(currentSession);
}
sessions.push(currentSession);
}
} 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
@ -4326,8 +4328,8 @@
if (result && result.logs && Array.isArray(result.logs)) {
lines = result.logs;
} else {
// Fallback: try file.exec with logread
const logResult = await UBUS.execCommand('/sbin/logread', ['-l', '20']).catch(() => null);
// Fallback: use shell to get last 20 lines (logread -l N not available on BusyBox)
const logResult = await UBUS.execCommand('/bin/sh', ['-c', 'logread | tail -n 20']).catch(() => null);
if (logResult && logResult.stdout) {
lines = logResult.stdout.trim().split('\n').filter(l => l);
}
@ -5003,17 +5005,35 @@
async function loadStorageInfo() {
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) {
const lines = result.stdout.trim().split('\n');
lines.forEach(line => {
const parts = line.split(/\s+/);
if (parts[5] === '/') {
document.getElementById('storageRoot').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`;
} else if (parts[5] === '/tmp') {
document.getElementById('storageTmp').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`;
} else if (parts[5] === '/overlay') {
document.getElementById('storageOverlay').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`;
if (parts.length >= 6) {
if (parts[5] === '/') {
document.getElementById('storageRoot').textContent = `${parts[2]} / ${parts[1]} (${parts[4]})`;
} else if (parts[5] === '/tmp') {
document.getElementById('storageTmp').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');
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) {
allLogs = result.stdout.trim().split('\n').filter(l => l).reverse();