adding the index page and setting up the folder structure
This commit is contained in:
parent
7432d7ab83
commit
962ed80968
206
www/wifi-join/index.html
Normal file
206
www/wifi-join/index.html
Normal file
@ -0,0 +1,206 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>WiFi Join</title>
|
||||
<style>
|
||||
:root { --bg:#0f1115; --card:#1a1d24; --line:#2a2f3a; --fg:#e6e9ef; --mut:#8b93a3; --accent:#4f8cff; --ok:#3ec07a; --warn:#e0a64f; --err:#ff7a7a; }
|
||||
* { box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
|
||||
body { margin:0; background:var(--bg); color:var(--fg); font:16px/1.4 system-ui,sans-serif; padding:16px; max-width:560px; margin:0 auto; }
|
||||
h1 { font-size:20px; margin:8px 0 4px; }
|
||||
h2 { font-size:14px; text-transform:uppercase; letter-spacing:.05em; color:var(--mut); margin:20px 0 8px; }
|
||||
button { font:inherit; border:0; border-radius:10px; padding:12px 16px; background:var(--accent); color:#fff; width:100%; cursor:pointer; }
|
||||
button.sec { background:var(--card); color:var(--fg); border:1px solid var(--line); }
|
||||
button.sm { padding:8px 12px; font-size:14px; width:auto; }
|
||||
button:active { opacity:.8; }
|
||||
button:disabled { opacity:.4; }
|
||||
.row { display:flex; align-items:center; gap:12px; padding:14px; background:var(--card); border:1px solid var(--line); border-radius:10px; margin-bottom:8px; cursor:pointer; }
|
||||
.row:active { border-color:var(--accent); }
|
||||
.ssid { flex:1; font-weight:500; word-break:break-word; }
|
||||
.meta { color:var(--mut); font-size:13px; display:flex; align-items:center; gap:6px; }
|
||||
.bars { display:inline-flex; align-items:flex-end; gap:2px; height:16px; }
|
||||
.bars i { width:3px; background:var(--mut); border-radius:1px; }
|
||||
.bars i.on { background:var(--ok); }
|
||||
.panel { background:var(--card); border:1px solid var(--line); border-radius:10px; padding:16px; margin-bottom:12px; }
|
||||
input { font:inherit; width:100%; padding:12px; border-radius:8px; border:1px solid var(--line); background:var(--bg); color:var(--fg); margin:8px 0; }
|
||||
.status { padding:14px; border-radius:10px; text-align:center; margin-bottom:12px; }
|
||||
.status.ok { background:rgba(62,192,122,.15); color:var(--ok); }
|
||||
.status.work { background:rgba(224,166,79,.15); color:var(--warn); }
|
||||
.status.err { background:rgba(255,90,90,.15); color:var(--err); }
|
||||
.mut { color:var(--mut); font-size:13px; }
|
||||
.hide { display:none; }
|
||||
.spin { display:inline-block; width:14px; height:14px; border:2px solid currentColor; border-right-color:transparent; border-radius:50%; animation:s .7s linear infinite; vertical-align:-2px; margin-right:6px; }
|
||||
@keyframes s { to { transform:rotate(360deg); } }
|
||||
.togglerow { display:flex; align-items:center; justify-content:space-between; padding:10px 0; border-bottom:1px solid var(--line); }
|
||||
.togglerow:last-child { border-bottom:0; }
|
||||
.pill { font-size:12px; padding:3px 10px; border-radius:99px; background:var(--bg); border:1px solid var(--line); }
|
||||
.pill.on { color:var(--ok); border-color:var(--ok); }
|
||||
.pill.off { color:var(--mut); }
|
||||
.chip { display:inline-block; padding:6px 10px; margin:4px 4px 0 0; border-radius:8px; background:var(--bg); border:1px solid var(--line); font-size:13px; cursor:pointer; }
|
||||
.btnrow { display:flex; gap:8px; }
|
||||
.btnrow button { flex:1; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WiFi Join</h1>
|
||||
|
||||
<div id="curstatus" class="status work">Checking connection…</div>
|
||||
|
||||
<!-- ============ JOIN ============ -->
|
||||
<div id="selpanel" class="panel hide">
|
||||
<div id="selname" style="font-weight:600;margin-bottom:4px;"></div>
|
||||
<div id="selpwwrap" class="hide">
|
||||
<div style="position:relative;">
|
||||
<input id="pw" type="password" placeholder="Password" autocomplete="off" style="padding-right:52px;">
|
||||
<button type="button" id="pwtoggle" class="sec" style="position:absolute;right:4px;top:4px;bottom:4px;width:44px;padding:0;border-radius:6px;">👁</button>
|
||||
</div>
|
||||
</div>
|
||||
<button id="joinbtn">Join</button>
|
||||
<button class="sec" id="cancelbtn" style="margin-top:8px;">Cancel</button>
|
||||
</div>
|
||||
|
||||
<button id="scanbtn">Scan for networks</button>
|
||||
<div id="list" style="margin-top:12px;"></div>
|
||||
<div id="msg" class="mut" style="text-align:center;"></div>
|
||||
|
||||
<!-- ============ DNS CONTROL ============ -->
|
||||
<h2>DNS Control</h2>
|
||||
<div class="panel">
|
||||
<div class="btnrow" style="margin-bottom:12px;">
|
||||
<button id="dnsNormal">Normal (filtered)</button>
|
||||
<button class="sec" id="dnsPortal">Portal mode</button>
|
||||
</div>
|
||||
|
||||
<div class="togglerow">
|
||||
<span>AdGuard filter</span>
|
||||
<span id="pillAdguard" class="pill off">—</span>
|
||||
</div>
|
||||
<div class="togglerow">
|
||||
<span>noresolv (lock upstream)</span>
|
||||
<span id="pillNoresolv" class="pill off">—</span>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:12px;">
|
||||
<div class="mut">Upstream servers (dnsmasq):</div>
|
||||
<div id="serverList" class="mut" style="margin:6px 0;">—</div>
|
||||
<div id="venueChip"></div>
|
||||
<input id="dnsAdd" placeholder="Add upstream IP (e.g. 1.1.1.1)" autocomplete="off">
|
||||
<button class="sec sm" id="dnsAddBtn" style="width:100%;">Add upstream</button>
|
||||
</div>
|
||||
|
||||
<div class="togglerow" style="margin-top:12px;">
|
||||
<span>Auto-restore filter after portal</span>
|
||||
<span id="pillAuto" class="pill off">OFF</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const $ = s => document.querySelector(s);
|
||||
const J = (u,b) => fetch(u, b?{method:'POST',body:JSON.stringify(b)}:undefined).then(r=>r.json());
|
||||
const api = {
|
||||
scan: () => J('/cgi-bin/wifi-scan'),
|
||||
join: b => J('/cgi-bin/wifi-join', b),
|
||||
status: () => J('/cgi-bin/wifi-status'),
|
||||
dns: b => J('/cgi-bin/wifi-dns', b),
|
||||
};
|
||||
|
||||
let selected = null;
|
||||
let autoRestore = false; // bones for auto-restore; toggle flips this
|
||||
|
||||
/* ---------- connection status ---------- */
|
||||
function paintStatus(s) {
|
||||
const el = $('#curstatus');
|
||||
if (s.up && s.net_ok) { el.className='status ok'; el.textContent = `Connected: ${s.ssid||'?'} (internet OK)`; }
|
||||
else if (s.up) { el.className='status work'; el.textContent = `Associated: ${s.ssid||'?'} — checking internet…`; }
|
||||
else { el.className='status err'; el.textContent = 'Not connected'; }
|
||||
}
|
||||
function showStatus() { api.status().then(paintStatus).catch(()=>{ $('#curstatus').textContent='Status unavailable'; }); }
|
||||
|
||||
/* ---------- scan + join ---------- */
|
||||
function bars(q, qmax) {
|
||||
const n = Math.max(1, Math.round((qmax?q/qmax:0)*4));
|
||||
let h='<span class="bars">';
|
||||
for (let i=1;i<=4;i++) h+=`<i class="${i<=n?'on':''}" style="height:${i*3+4}px"></i>`;
|
||||
return h+'</span>';
|
||||
}
|
||||
function scan() {
|
||||
$('#msg').textContent='Scanning…'; $('#list').innerHTML='';
|
||||
api.scan().then(r=>{
|
||||
$('#msg').textContent='';
|
||||
if(!r.ok){ $('#msg').textContent='Scan failed'; return; }
|
||||
r.networks.forEach(n=>{
|
||||
const d=document.createElement('div'); d.className='row';
|
||||
d.innerHTML=`<div class="ssid">${n.ssid}</div><div class="meta">${n.secured?'🔒':''}${bars(n.quality,n.quality_max)}</div>`;
|
||||
d.onclick=()=>select(n); $('#list').appendChild(d);
|
||||
});
|
||||
}).catch(()=>{ $('#msg').textContent='Scan error'; });
|
||||
}
|
||||
function select(n){ selected=n; $('#selname').textContent=n.ssid; $('#selpwwrap').className=n.secured?'':'hide'; $('#pw').value=''; $('#selpanel').className='panel'; window.scrollTo(0,0); }
|
||||
function cancel(){ selected=null; $('#selpanel').className='panel hide'; }
|
||||
function join(){
|
||||
if(!selected) return;
|
||||
const body={ssid:selected.ssid,secured:selected.secured};
|
||||
if(selected.secured) body.key=$('#pw').value;
|
||||
$('#joinbtn').disabled=true;
|
||||
$('#curstatus').className='status work'; $('#curstatus').innerHTML='<span class="spin"></span>Joining '+selected.ssid+'…';
|
||||
cancel();
|
||||
api.join(body).then(r=>{
|
||||
if(!r.ok){ $('#curstatus').className='status err'; $('#curstatus').textContent='Join failed: '+(r.error||'?'); $('#joinbtn').disabled=false; return; }
|
||||
poll(0);
|
||||
}).catch(()=>{ $('#curstatus').textContent='Join request error'; $('#joinbtn').disabled=false; });
|
||||
}
|
||||
function poll(t){
|
||||
if(t>60){ $('#curstatus').className='status err'; $('#curstatus').textContent='Timed out waiting to connect'; $('#joinbtn').disabled=false; return; }
|
||||
api.status().then(s=>{
|
||||
if(s.up && s.net_ok){
|
||||
$('#curstatus').className='status ok'; $('#curstatus').textContent=`Connected: ${s.ssid} (internet OK)`; $('#joinbtn').disabled=false;
|
||||
// ---- AUTO-RESTORE BONES (disabled) ----
|
||||
// if (autoRestore) { api.dns({action:'normal'}).then(refreshDns); }
|
||||
} else {
|
||||
const label = s.up ? 'Associated, checking internet' : 'Connecting';
|
||||
$('#curstatus').innerHTML=`<span class="spin"></span>${label}… (${t*3}s)`;
|
||||
setTimeout(()=>poll(t+1),3000);
|
||||
}
|
||||
}).catch(()=>setTimeout(()=>poll(t+1),3000));
|
||||
}
|
||||
|
||||
/* ---------- DNS control ---------- */
|
||||
const ADGUARD='127.0.0.1#5335';
|
||||
function refreshDns(){
|
||||
api.dns({action:'status'}).then(r=>{
|
||||
if(!r.ok) return;
|
||||
const st=r.state, servers=st.servers||[];
|
||||
const hasAg = servers.indexOf(ADGUARD)>=0;
|
||||
$('#pillAdguard').className='pill '+(hasAg?'on':'off'); $('#pillAdguard').textContent=hasAg?'ON':'OFF';
|
||||
$('#pillNoresolv').className='pill '+(st.noresolv?'on':'off'); $('#pillNoresolv').textContent=st.noresolv?'ON':'OFF';
|
||||
$('#serverList').textContent = servers.length?servers.join(', '):'(none)';
|
||||
});
|
||||
}
|
||||
function dnsAction(a){
|
||||
$('#dnsNormal').disabled=true; $('#dnsPortal').disabled=true;
|
||||
api.dns({action:a}).then(()=>{ refreshDns(); $('#dnsNormal').disabled=false; $('#dnsPortal').disabled=false; setTimeout(showStatus,1500); });
|
||||
}
|
||||
function dnsAddUpstream(ip){
|
||||
if(!ip) return;
|
||||
api.dns({action:'status'}).then(r=>{
|
||||
const servers=(r.state&&r.state.servers)?r.state.servers.slice():[];
|
||||
if(servers.indexOf(ip)<0) servers.push(ip);
|
||||
api.dns({action:'set', servers:servers}).then(()=>{ $('#dnsAdd').value=''; refreshDns(); });
|
||||
});
|
||||
}
|
||||
|
||||
/* wire up */
|
||||
$('#scanbtn').onclick=scan;
|
||||
$('#joinbtn').onclick=join;
|
||||
$('#cancelbtn').onclick=cancel;
|
||||
$('#pwtoggle').onclick=()=>{ const p=$('#pw'); p.type=p.type==='password'?'text':'password'; };
|
||||
$('#dnsNormal').onclick=()=>dnsAction('normal');
|
||||
$('#dnsPortal').onclick=()=>dnsAction('portal');
|
||||
$('#dnsAddBtn').onclick=()=>dnsAddUpstream($('#dnsAdd').value.trim());
|
||||
$('#pillAuto').onclick=()=>{ autoRestore=!autoRestore; $('#pillAuto').className='pill '+(autoRestore?'on':'off'); $('#pillAuto').textContent=autoRestore?'ON':'OFF'; };
|
||||
|
||||
showStatus(); scan(); refreshDns();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user