fix: pre-scan WiFi before entering AP mode

wlan0 cannot scan while in AP mode (hardware limitation).
Now scan nearby networks before starting hotspot and cache
the results for the captive portal page.

Made-with: Cursor
This commit is contained in:
stswangzhiping
2026-03-16 13:04:00 +08:00
parent 64f4050014
commit c6f55c8c15
3 changed files with 22 additions and 20 deletions

View File

@@ -1,11 +1,5 @@
fix: EROFS dns config, double WS connection, and watchdog timeout fix: pre-scan WiFi before entering AP mode
1. EROFS: install.sh pre-writes DNS hijack config to dnsmasq-shared.d wlan0 cannot scan while in AP mode (hardware limitation).
since /etc may be read-only at runtime. dns-hijack.js gracefully Now scan nearby networks before starting hotspot and cache
falls back to checking if config already exists. the results for the captive portal page.
2. Double WS: add _connectionStarted guard to prevent _proceedWithConnection
from being called twice (via event + hasInternet check).
3. Watchdog: move _startSdNotify() to start() beginning so READY=1
is sent immediately, not delayed until network is ready.

View File

@@ -2,7 +2,7 @@
const http = require('http'); const http = require('http');
const log = require('./logger'); const log = require('./logger');
const { scanWifi } = require('./network'); // scanWifi 不再在此调用AP 模式下无法扫描),改用缓存
const { CAPTIVE_DOMAIN } = require('./dns-hijack'); const { CAPTIVE_DOMAIN } = require('./dns-hijack');
const PORT = 80; const PORT = 80;
@@ -32,7 +32,8 @@ class CaptiveServer {
constructor(opts = {}) { constructor(opts = {}) {
this._server = null; this._server = null;
this._clawId = opts.clawId || '???'; this._clawId = opts.clawId || '???';
this._onConnect = opts.onConnect || null; // (ssid, password) => Promise<{success, error?}> this._onConnect = opts.onConnect || null;
this._cachedWifiList = opts.cachedWifiList || [];
} }
startListening() { startListening() {
@@ -96,8 +97,8 @@ class CaptiveServer {
// ── API ────────────────────────────────────────────────────────────────── // ── API ──────────────────────────────────────────────────────────────────
_apiScan(req, res) { _apiScan(req, res) {
const list = scanWifi(); // AP 模式下 wlan0 无法扫描,返回开 AP 前的缓存结果
this._json(res, { wifi: list }); this._json(res, { wifi: this._cachedWifiList, cached: true });
} }
async _apiConnect(req, res) { async _apiConnect(req, res) {
@@ -227,7 +228,9 @@ async function doScan(){
o.textContent=w.ssid+' ('+w.signal+'% '+w.security+')'; o.textContent=w.ssid+' ('+w.signal+'% '+w.security+')';
sel.appendChild(o); sel.appendChild(o);
}); });
setStatus('扫描到 '+d.wifi.length+' 个网络','ok'); var msg='发现 '+d.wifi.length+' 个网络';
if(d.wifi.length===0) msg='未发现网络,请手动输入 SSID';
setStatus(msg,'ok');
}catch(e){setStatus('扫描失败: '+e.message,'err')} }catch(e){setStatus('扫描失败: '+e.message,'err')}
$('connectBtn').disabled=false; $('connectBtn').disabled=false;
} }

View File

@@ -2,7 +2,7 @@
const EventEmitter = require('events'); const EventEmitter = require('events');
const log = require('./logger'); const log = require('./logger');
const { hasInternet, hasSavedWifiConnection, isWifiStaConnected, startAP, stopAP, connectWifi, AP_IP } = require('./network'); const { hasInternet, hasSavedWifiConnection, isWifiStaConnected, scanWifi, startAP, stopAP, connectWifi, AP_IP } = require('./network');
const { DnsHijack } = require('./dns-hijack'); const { DnsHijack } = require('./dns-hijack');
const { CaptiveServer } = require('./captive-server'); const { CaptiveServer } = require('./captive-server');
@@ -98,8 +98,12 @@ class ProvisionManager extends EventEmitter {
if (this._state === 'ap') return; if (this._state === 'ap') return;
try { try {
// 先写 DNS 劫持配置,再启动 AP // AP 模式下无法扫描 WiFi必须在开 AP 之前扫描并缓存
// NM 启动热点时会加载 dnsmasq-shared.d/ 下的配置 log.info('provision', '扫描周边 WiFi...');
this._cachedWifiList = scanWifi();
log.info('provision', `扫描到 ${this._cachedWifiList.length} 个网络`);
// 写 DNS 劫持配置NM 启动热点时加载)
this._dns = new DnsHijack(); this._dns = new DnsHijack();
this._dns.start('wlan0', AP_IP); this._dns.start('wlan0', AP_IP);
@@ -107,6 +111,7 @@ class ProvisionManager extends EventEmitter {
this._server = new CaptiveServer({ this._server = new CaptiveServer({
clawId: this._clawId, clawId: this._clawId,
cachedWifiList: this._cachedWifiList,
onConnect: (ssid, password) => this._handleWifiConnect(ssid, password), onConnect: (ssid, password) => this._handleWifiConnect(ssid, password),
}); });
this._server.startListening(); this._server.startListening();