feat(webapp): Dashboard improvements
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3132ef9c14
commit
35eb1f79b2
18
CLAUDE.md
18
CLAUDE.md
@ -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
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user