fix: setup-phone probe, unknown command error, query source in dashboard
- setup-phone now probes the mobile API before printing the QR code and shows an actionable error if [mobile] is not enabled - Unknown CLI subcommands print an error instead of silently attempting to start a full server - Dashboard query log shows source IP under timestamp (localhost for loopback, full IP for LAN devices) with full addr on hover Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -288,6 +288,7 @@ body {
|
|||||||
.path-tag.SERVFAIL { background: rgba(181, 68, 58, 0.12); color: var(--rose); }
|
.path-tag.SERVFAIL { background: rgba(181, 68, 58, 0.12); color: var(--rose); }
|
||||||
.path-tag.BLOCKED { background: rgba(163, 152, 136, 0.15); color: var(--text-dim); }
|
.path-tag.BLOCKED { background: rgba(163, 152, 136, 0.15); color: var(--text-dim); }
|
||||||
.path-tag.COALESCED { background: rgba(138, 104, 158, 0.12); color: var(--violet-dim); }
|
.path-tag.COALESCED { background: rgba(138, 104, 158, 0.12); color: var(--violet-dim); }
|
||||||
|
.src-tag { font-size: 0.6rem; color: var(--text-dim); letter-spacing: 0.02em; }
|
||||||
|
|
||||||
/* Sidebar panels */
|
/* Sidebar panels */
|
||||||
.sidebar {
|
.sidebar {
|
||||||
@@ -787,6 +788,13 @@ function formatTime(epoch) {
|
|||||||
return d.toLocaleTimeString([], { hour12: false });
|
return d.toLocaleTimeString([], { hour12: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shortSrc(addr) {
|
||||||
|
if (!addr) return '';
|
||||||
|
const ip = addr.replace(/:\d+$/, '');
|
||||||
|
if (ip === '127.0.0.1' || ip === '::1') return 'localhost';
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
function formatRemaining(secs) {
|
function formatRemaining(secs) {
|
||||||
if (secs == null) return 'permanent';
|
if (secs == null) return 'permanent';
|
||||||
if (secs < 60) return `${secs}s left`;
|
if (secs < 60) return `${secs}s left`;
|
||||||
@@ -912,8 +920,8 @@ function applyLogFilter() {
|
|||||||
? ` <button class="btn-delete" onclick="allowDomain('${e.domain}')" title="Allow this domain" style="color:var(--emerald);font-size:0.65rem;">allow</button>`
|
? ` <button class="btn-delete" onclick="allowDomain('${e.domain}')" title="Allow this domain" style="color:var(--emerald);font-size:0.65rem;">allow</button>`
|
||||||
: '';
|
: '';
|
||||||
return `
|
return `
|
||||||
<tr>
|
<tr title="Source: ${e.src || 'unknown'}">
|
||||||
<td>${formatTime(e.timestamp_epoch)}</td>
|
<td>${formatTime(e.timestamp_epoch)}<br><span class="src-tag">${shortSrc(e.src)}</span></td>
|
||||||
<td>${e.query_type}</td>
|
<td>${e.query_type}</td>
|
||||||
<td class="domain-cell" title="${e.domain}">${e.domain}${allowBtn}</td>
|
<td class="domain-cell" title="${e.domain}">${e.domain}${allowBtn}</td>
|
||||||
<td><span class="path-tag ${e.path}">${e.path}</span></td>
|
<td><span class="path-tag ${e.path}">${e.path}</span></td>
|
||||||
|
|||||||
16
src/main.rs
16
src/main.rs
@@ -94,7 +94,21 @@ async fn main() -> numa::Result<()> {
|
|||||||
eprintln!("Config path defaults to numa.toml");
|
eprintln!("Config path defaults to numa.toml");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {
|
||||||
|
if !arg1.is_empty()
|
||||||
|
&& arg1 != "run"
|
||||||
|
&& !arg1.contains('/')
|
||||||
|
&& !arg1.contains('\\')
|
||||||
|
&& !arg1.ends_with(".toml")
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"\x1b[1;38;2;192;98;58mNuma\x1b[0m — unknown command: \x1b[1m{}\x1b[0m\n",
|
||||||
|
arg1
|
||||||
|
);
|
||||||
|
eprintln!("Run \x1b[1mnuma help\x1b[0m for a list of commands.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let config_path = if arg1.is_empty() || arg1 == "run" {
|
let config_path = if arg1.is_empty() || arg1 == "run" {
|
||||||
|
|||||||
@@ -48,6 +48,31 @@ pub async fn run() -> Result<(), String> {
|
|||||||
let lan_ip = crate::lan::detect_lan_ip()
|
let lan_ip = crate::lan::detect_lan_ip()
|
||||||
.ok_or("could not detect LAN IP — are you connected to a network?")?;
|
.ok_or("could not detect LAN IP — are you connected to a network?")?;
|
||||||
|
|
||||||
|
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], SETUP_PORT));
|
||||||
|
let api_reachable = tokio::time::timeout(
|
||||||
|
std::time::Duration::from_millis(500),
|
||||||
|
tokio::net::TcpStream::connect(addr),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|r| r.is_ok())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if !api_reachable {
|
||||||
|
eprintln!();
|
||||||
|
eprintln!(
|
||||||
|
" \x1b[1;38;2;192;98;58mNuma\x1b[0m — mobile API is not reachable on port {}.",
|
||||||
|
SETUP_PORT
|
||||||
|
);
|
||||||
|
eprintln!();
|
||||||
|
eprintln!(" The phone won't be able to download the profile until the mobile");
|
||||||
|
eprintln!(" API is running. Add this to your numa.toml and restart Numa:");
|
||||||
|
eprintln!();
|
||||||
|
eprintln!(" [mobile]");
|
||||||
|
eprintln!(" enabled = true");
|
||||||
|
eprintln!();
|
||||||
|
return Err("mobile API not running".into());
|
||||||
|
}
|
||||||
|
|
||||||
let url = format!("http://{}:{}/mobileconfig", lan_ip, SETUP_PORT);
|
let url = format!("http://{}:{}/mobileconfig", lan_ip, SETUP_PORT);
|
||||||
let qr = render_qr(&url)?;
|
let qr = render_qr(&url)?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user