diff --git a/site/dashboard.html b/site/dashboard.html index ffc6e0d..c78c48f 100644 --- a/site/dashboard.html +++ b/site/dashboard.html @@ -288,6 +288,7 @@ body { .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.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 { @@ -787,6 +788,13 @@ function formatTime(epoch) { 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) { if (secs == null) return 'permanent'; if (secs < 60) return `${secs}s left`; @@ -912,8 +920,8 @@ function applyLogFilter() { ? ` ` : ''; return ` - - ${formatTime(e.timestamp_epoch)} + + ${formatTime(e.timestamp_epoch)}
${shortSrc(e.src)} ${e.query_type} ${e.domain}${allowBtn} ${e.path} diff --git a/src/main.rs b/src/main.rs index ebe4aa9..70bc3f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,7 +94,21 @@ async fn main() -> numa::Result<()> { eprintln!("Config path defaults to numa.toml"); 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" { diff --git a/src/setup_phone.rs b/src/setup_phone.rs index 7f9b02a..fd37c84 100644 --- a/src/setup_phone.rs +++ b/src/setup_phone.rs @@ -48,6 +48,31 @@ pub async fn run() -> Result<(), String> { let lan_ip = crate::lan::detect_lan_ip() .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 qr = render_qr(&url)?;