feat: startup environment check + self-heal (v1.3)
- env_check(): verifies daemon/pineapd/UCI/monitors/recon DB, auto-fixes fixable issues and re-verifies; forces runtime SSID-pool broadcast off to match the UI (kills the 'pool on but UI shows off' gap) - server.py --env-check CLI; payload.sh runs it verbosely before starting, aborts on core failure - serve() runs the check at every startup (boot + procd respawn) - /api/health exposes env report + pool_runtime; /api/recon/status exposes wlan0_pinned (2.4GHz under-sampling warning); recon page warns when a radio0 AP pins wlan0mon - recon start failures now include the daemon reason in the UI error - shared stabilization refactor (_stabilize_uci/PINEAPD_SAFE_UCI/_raise_monitors) - tests: test_env_check.py (18) + health/recon updates
This commit is contained in:
@@ -516,6 +516,9 @@ const Live = (() => {
|
||||
if (h.pineap_up === false) {
|
||||
el.textContent = 'PINEAPD DOWN';
|
||||
el.className = 'health-chip bad';
|
||||
} else if (h.env && h.env.overall === 'fail') {
|
||||
el.textContent = 'ENV CHECK FAIL';
|
||||
el.className = 'health-chip bad';
|
||||
} else if (h.pool_disabled) {
|
||||
el.textContent = 'POOL OFF';
|
||||
el.className = 'health-chip warn';
|
||||
|
||||
@@ -177,7 +177,8 @@ views.dashboard = (root) => {
|
||||
liveCards.health.textContent = (h2.pineap_up ? 'pineapd up' : 'pineapd DOWN') +
|
||||
' · wlan0mon ' + (h2.wlan0mon_up ? 'up' : 'down') +
|
||||
' · wlan1mon ' + (h2.wlan1mon_up ? 'up' : 'down') +
|
||||
(h2.pool_disabled ? ' · pool off' : '');
|
||||
(h2.pool_disabled ? ' · pool off' : '') +
|
||||
((h2.env || {}).overall ? ' · env ' + h2.env.overall : '');
|
||||
liveCards.health.style.color = h2.pineap_up ? '' : '#b71c1c';
|
||||
}).catch(() => {});
|
||||
PagerAPI.get('/api/recon/status').then((r) => {
|
||||
@@ -1460,7 +1461,8 @@ views.recon = (root) => {
|
||||
apBand: 'all', apEnc: 'all', gps: null, wigle: null,
|
||||
compare: [], history: {}, mapBand: null,
|
||||
archive: null, archives: [], scanRemaining: null,
|
||||
hopperOnline: null, historyReset: false, scanErr: null };
|
||||
hopperOnline: null, historyReset: false, scanErr: null,
|
||||
wlan0Pinned: false };
|
||||
const cols = reconLoadCols();
|
||||
|
||||
// ---- title cards (stat cards with optional mini charts) ----
|
||||
@@ -1658,9 +1660,10 @@ views.recon = (root) => {
|
||||
}
|
||||
if (state.hopperOnline === false) bits.push('Hopper radio offline — fewer networks seen');
|
||||
if (state.historyReset) bits.push('History reset — previous scans archived (see Previous Scans)');
|
||||
if (state.wlan0Pinned) bits.push('2.4GHz under-sampled — OpenAP/Evil WPA holds wlan0mon');
|
||||
if (state.scanErr) bits.push(state.scanErr);
|
||||
scanStatus.textContent = bits.join(' · ');
|
||||
scanStatus.classList.toggle('warn', state.hopperOnline === false || state.historyReset || !!state.scanErr);
|
||||
scanStatus.classList.toggle('warn', state.hopperOnline === false || state.historyReset || state.wlan0Pinned || !!state.scanErr);
|
||||
}
|
||||
scanToggle.addEventListener('change', () => {
|
||||
if (pendingScan) { scanToggle.checked = !scanToggle.checked; return; }
|
||||
@@ -2522,6 +2525,7 @@ views.recon = (root) => {
|
||||
state.scanRemaining = r.data.scan_remaining != null ? r.data.scan_remaining : null;
|
||||
state.hopperOnline = r.data.hopper_online;
|
||||
state.historyReset = !!r.data.history_reset;
|
||||
state.wlan0Pinned = !!r.data.wlan0_pinned;
|
||||
if (!pendingScan) scanToggle.checked = scanning;
|
||||
renderScanBar();
|
||||
if (wasScanning !== scanning) restartPoll();
|
||||
|
||||
Reference in New Issue
Block a user