moga + fmt/clippy
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Kill wakey by PID quietly. Prefer pidof (BusyBox), fallback to ps/awk.
|
||||
|
||||
# 1) Try pidof
|
||||
pids="$(pidof wakey 2>/dev/null)"
|
||||
|
||||
# 2) Fallback: ps | awk (avoid matching awk/grep themselves)
|
||||
if [ -z "$pids" ]; then
|
||||
pids="$(ps w 2>/dev/null | awk '/\/\.bin\/wakey/ && $0 !~ /awk/ {print $1}')"
|
||||
fi
|
||||
|
||||
[ -n "$pids" ] || exit 0
|
||||
|
||||
# Send TERM first
|
||||
kill -TERM $pids 2>/dev/null || true
|
||||
|
||||
# Optional: hard kill if still alive after a short grace
|
||||
sleep 0.2
|
||||
remain=""
|
||||
for p in $pids; do
|
||||
kill -0 "$p" 2>/dev/null && remain="$remain $p"
|
||||
done
|
||||
[ -z "$remain" ] || kill -KILL $remain 2>/dev/null || true
|
||||
|
||||
exit 0
|
||||
+9
-7
@@ -12,19 +12,21 @@ use std::{net::IpAddr, str::FromStr};
|
||||
|
||||
use macaddr::MacAddr;
|
||||
use serde::{Deserialize, Serialize, Serializer, de};
|
||||
use serde_with::skip_serializing_none ;
|
||||
use serde_with::skip_serializing_none;
|
||||
use strum::{Display, EnumString};
|
||||
|
||||
use crate::arpparse::error::IPNeighParseError;
|
||||
mod error;
|
||||
mod r#impl; // custom (de)serialization impls
|
||||
|
||||
/// ip neigh has some cool shit.
|
||||
/// IP
|
||||
/// dev DEV | None
|
||||
/// lladdr MAC | None
|
||||
/// status { permanent | noarp | stale | reachable | none | incomplete | delay | probe | failed } (ip neigh help)
|
||||
/// so you can see its damn good
|
||||
///
|
||||
/// IP
|
||||
/// dev DEV | None
|
||||
/// lladdr MAC | None
|
||||
/// status { permanent | noarp | stale | reachable | none | incomplete | delay | probe | failed } (ip neigh help)
|
||||
///
|
||||
/// so you can see its damn good
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, serde::Serialize)]
|
||||
pub struct IpNeighLine {
|
||||
@@ -43,7 +45,7 @@ pub fn ser_opm<S: Serializer>(bro: &Option<MacAddr>, ser: S) -> Result<S::Ok, S:
|
||||
}
|
||||
|
||||
/// deserialize an [`Option<MacAddr>`]
|
||||
pub fn des_opm<'de, D>(des: D) -> Result<Option<MacAddr>, D::Error>
|
||||
pub fn _des_opm<'de, D>(des: D) -> Result<Option<MacAddr>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
|
||||
@@ -27,4 +27,4 @@ impl From<strum::ParseError> for IPNeighParseError {
|
||||
fn from(value: strum::ParseError) -> Self {
|
||||
Self::StateParseError(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-7
@@ -7,15 +7,15 @@ use axum::{
|
||||
};
|
||||
use tokio::net::TcpListener;
|
||||
mod arpparse;
|
||||
mod route;
|
||||
mod r#static;
|
||||
mod utils;
|
||||
mod route;
|
||||
|
||||
use crate::{
|
||||
route::DeviceQuery,
|
||||
utils::{ping::ping_ip, query::get_macs_2_1, wake::wake},
|
||||
};
|
||||
use r#static as st;
|
||||
use std::io;
|
||||
|
||||
const MACHINE_NAME: &str = "lda.lan";
|
||||
|
||||
@@ -49,7 +49,11 @@ async fn wake_handler(
|
||||
Query(DeviceQuery { name, .. }): Query<DeviceQuery>,
|
||||
) -> axum::response::Result<impl IntoResponse> {
|
||||
match wake(name.as_deref().unwrap_or(MACHINE_NAME)).await {
|
||||
Ok(x) if x > 0 => Ok((StatusCode::ACCEPTED, format!("{x} packets sent!"))),
|
||||
Ok(0) => Err((StatusCode::NOT_FOUND, "No packets sent!").into()),
|
||||
Ok(x) => Ok((
|
||||
StatusCode::ACCEPTED,
|
||||
format!("{x} packet{s} sent!", s = if x > 1 { "s" } else { "" }),
|
||||
)),
|
||||
_ => Err((StatusCode::GATEWAY_TIMEOUT, "Wake failed").into()),
|
||||
}
|
||||
}
|
||||
@@ -95,17 +99,17 @@ pub async fn status_build(machine_name: &str) -> String {
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
#[tokio::main]
|
||||
async fn main() -> color_eyre::Result<()> {
|
||||
use crate::route::{api_status, get_status_2, home_2, home_2_route};
|
||||
async fn entry() -> io::Result<()> {
|
||||
use crate::route::{api_status, devs_router, get_status_2, home_2, home_2_route};
|
||||
|
||||
color_eyre::install()?;
|
||||
let app = Router::new()
|
||||
.route("/home", get(home))
|
||||
.route("/", get(home_2))
|
||||
.merge(home_2_route())
|
||||
.route("/wake", post(wake_handler))
|
||||
.route("/status", get(get_status_2))
|
||||
.merge(api_status());
|
||||
.merge(api_status())
|
||||
.route("/api/devs", get(devs_router));
|
||||
|
||||
let port = TcpListener::bind("0.0.0.0:12012").await?;
|
||||
axum::serve(port, app.into_make_service()).await?;
|
||||
@@ -124,3 +128,9 @@ fn main() -> color_eyre::Result<()> {
|
||||
"OS not supported! run this on your ahh router!"
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
Ok(entry()?)
|
||||
}
|
||||
|
||||
+75
-11
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use axum::{
|
||||
@@ -9,17 +10,20 @@ use axum::{
|
||||
};
|
||||
use axum_extra::extract::Query;
|
||||
use macaddr::MacAddr;
|
||||
use serde::ser::Serializer;
|
||||
use serde_with::skip_serializing_none;
|
||||
|
||||
// use serde_with::skip_serializing_none;
|
||||
// use serde::{Deserialize, de};
|
||||
// use serde_with::{OneOrMany, serde_as};
|
||||
|
||||
use crate::{
|
||||
MACHINE_NAME,
|
||||
arpparse::{self, NUDState, des_opm},
|
||||
arpparse::{self, NUDState},
|
||||
st, status_build,
|
||||
utils::{de_many, query::get_macs},
|
||||
utils::{
|
||||
de_many,
|
||||
query::{
|
||||
dev::{self, has_dev},
|
||||
get_macs,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub async fn home_2() -> Html<&'static str> {
|
||||
@@ -58,8 +62,8 @@ pub struct DeviceQuery {
|
||||
// Accept single or many; ignore blanks
|
||||
#[serde(default, deserialize_with = "de_many::vec_from_strs")]
|
||||
ip: Vec<IpAddr>,
|
||||
#[serde(default, deserialize_with = "des_opm")]
|
||||
mac: Option<MacAddr>,
|
||||
#[serde(default, deserialize_with = "de_many::vec_from_strs")]
|
||||
mac: Vec<MacAddr>,
|
||||
/// optional interface filter (e.g., br-lan)
|
||||
#[serde(default, deserialize_with = "de_many::vec_from_strs")]
|
||||
pub dev: Vec<String>,
|
||||
@@ -87,6 +91,19 @@ pub struct Filters {
|
||||
dev: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
nud: Vec<NUDState>,
|
||||
#[serde(
|
||||
skip_serializing_if = "Vec::is_empty",
|
||||
serialize_with = "serialize_macs"
|
||||
)]
|
||||
mac: Vec<MacAddr>,
|
||||
}
|
||||
|
||||
fn serialize_macs<S>(macs: &[MacAddr], serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let strings: Vec<String> = macs.iter().map(|m| m.to_string()).collect();
|
||||
serde::Serialize::serialize(&strings, serializer)
|
||||
}
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
@@ -98,7 +115,12 @@ pub struct StatusError {
|
||||
pub async fn get_status_json(
|
||||
// p: Option<Path<NamePath>>,
|
||||
Query(DeviceQuery {
|
||||
name, ip, dev, nud, ..
|
||||
name,
|
||||
ip,
|
||||
dev,
|
||||
nud,
|
||||
mac,
|
||||
..
|
||||
}): Query<DeviceQuery>,
|
||||
) -> impl IntoResponse {
|
||||
fn to_opts<T: Clone>(slice: &[T]) -> Vec<Option<T>> {
|
||||
@@ -122,7 +144,12 @@ pub async fn get_status_json(
|
||||
let dev_opts: Vec<Option<String>> = to_opts(&dev);
|
||||
let nud_opts: Vec<Option<NUDState>> = to_opts(&nud);
|
||||
|
||||
let filters = Filters { ip, dev, nud };
|
||||
let filters = Filters {
|
||||
ip,
|
||||
dev,
|
||||
nud,
|
||||
mac: mac.clone(),
|
||||
};
|
||||
|
||||
// Run combinations of dev/nud and merge results
|
||||
let mut tasks = Vec::new();
|
||||
@@ -141,7 +168,12 @@ pub async fn get_status_json(
|
||||
.await
|
||||
.map(|v| v.into_iter().flatten().collect::<Vec<_>>())
|
||||
{
|
||||
Ok(table) => {
|
||||
Ok(mut table) => {
|
||||
// Optional MAC post-filtering if provided
|
||||
if !mac.is_empty() {
|
||||
let wanted: HashSet<MacAddr> = mac.into_iter().collect();
|
||||
table.retain(|row| row.mac.map(|m| wanted.contains(&m)).unwrap_or(false));
|
||||
}
|
||||
// let canonical = format!("/api/status?name={name}");
|
||||
(
|
||||
StatusCode::OK,
|
||||
@@ -175,6 +207,7 @@ pub fn api_status() -> Router {
|
||||
Router::new()
|
||||
.route("/api/status/{name}", get(status_redirect)) // api/status should be like the entire ip neigh br lan like idk like
|
||||
.route("/api/status", get(get_status_json))
|
||||
.route("/api/smart/{q}", get(status_smart_redirect))
|
||||
}
|
||||
|
||||
pub async fn get_status_2(q: Query<DeviceQuery>) -> Html<String> {
|
||||
@@ -186,3 +219,34 @@ pub async fn get_status_2(q: Query<DeviceQuery>) -> Html<String> {
|
||||
};
|
||||
Html(status_build(&name).await)
|
||||
}
|
||||
|
||||
// Smart redirect: accept IP, MAC, dev, or NUD state and redirect to /api/status accordingly
|
||||
pub async fn status_smart_redirect(Path(q): Path<String>) -> Redirect {
|
||||
let s = q.trim();
|
||||
// 1) IP
|
||||
if let Ok(ip) = s.parse::<IpAddr>() {
|
||||
return Redirect::to(&format!("/api/status?ip={ip}"));
|
||||
}
|
||||
// 2) MAC
|
||||
if let Ok(mac) = s.parse::<MacAddr>() {
|
||||
return Redirect::to(&format!("/api/status?mac={mac}"));
|
||||
}
|
||||
// 3) NUD state (reachable, stale, ...)
|
||||
if let Ok(state) = s.parse::<NUDState>() {
|
||||
return Redirect::to(&format!("/api/status?nud={state}"));
|
||||
}
|
||||
// 4) Known device? prefer dev first
|
||||
if has_dev(s) {
|
||||
return Redirect::to(&format!("/api/status?dev={}", urlencoding::encode(s)));
|
||||
}
|
||||
// 5) Try DNS: if it resolves, treat as name
|
||||
if tokio::net::lookup_host((s, 0)).await.is_ok() {
|
||||
return Redirect::to(&format!("/api/status?name={}", urlencoding::encode(s)));
|
||||
}
|
||||
// Default: name last
|
||||
Redirect::to(&format!("/api/status?name={}", urlencoding::encode(s)))
|
||||
}
|
||||
|
||||
pub async fn devs_router() -> Json<Vec<String>> {
|
||||
dev::devs_sorted().into()
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
pub const HOME_2: &str = include_str!("../static/home_2");
|
||||
pub const HOME_2_CSS: &str = include_str!("../static/assets/home_2.css");
|
||||
pub const HOME_2_JS: &str = include_str!("../static/assets/home_2.js");
|
||||
pub const HOME_2_JS: &str = include_str!("../static/assets/home_2.js");
|
||||
|
||||
+12
-10
@@ -1,16 +1,15 @@
|
||||
pub const LDA_MACS: [[u8; 6]; 2] = [
|
||||
// ether
|
||||
[0x04, 0x7c, 0x16, 0x79, 0x6d, 0xee],
|
||||
// wifi
|
||||
[0xbc, 0x09, 0x1b, 0xec, 0x65, 0xd0],
|
||||
];
|
||||
// pub const LDA_MACS: [[u8; 6]; 2] = [
|
||||
// // ether
|
||||
// [0x04, 0x7c, 0x16, 0x79, 0x6d, 0xee],
|
||||
// // wifi
|
||||
// [0xbc, 0x09, 0x1b, 0xec, 0x65, 0xd0],
|
||||
// ];
|
||||
|
||||
/// is it time to lookup host lda.lan for this...
|
||||
pub static LDA_MACS_2: LazyLock<[MacAddr; 2]> = LazyLock::new(|| LDA_MACS.map(MacAddr::from));
|
||||
// pub static LDA_MACS_2: LazyLock<[MacAddr; 2]> = LazyLock::new(|| LDA_MACS.map(MacAddr::from));
|
||||
pub mod wake;
|
||||
use std::{net::IpAddr, sync::LazyLock};
|
||||
use std::net::IpAddr;
|
||||
|
||||
use macaddr::MacAddr;
|
||||
pub mod error;
|
||||
|
||||
/// generic so you can do "123.45.67.89:22" or "lda.lan:22" as an input
|
||||
@@ -21,7 +20,10 @@ pub mod ping;
|
||||
pub async fn get_ips(machine_name: &str) -> error::Result<Vec<IpAddr>> {
|
||||
let it = tokio::net::lookup_host((machine_name, 0))
|
||||
.await
|
||||
.map_err(|e| error::Error::DnsResolve { name: machine_name.to_string(), source: e })?;
|
||||
.map_err(|e| error::Error::DnsResolve {
|
||||
name: machine_name.to_string(),
|
||||
source: e,
|
||||
})?;
|
||||
Ok(it.map(|c| c.ip()).collect())
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -7,4 +7,4 @@ pub(crate) async fn exec_command<S: AsRef<std::ffi::OsStr>>(
|
||||
let mut u = tokio::process::Command::new(cmd);
|
||||
u.args(args);
|
||||
u.output().await
|
||||
}
|
||||
}
|
||||
|
||||
+17
-9
@@ -20,13 +20,19 @@ pub enum Error {
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::DnsResolve { name, source } => write!(f, "DNS resolve failed for {name}: {source}"),
|
||||
Error::CommandFailed { cmd, args, status, stderr } => {
|
||||
let code = status.map(|c| c.to_string()).unwrap_or_else(|| "signal".into());
|
||||
write!(
|
||||
f,
|
||||
"{cmd} {args:?} failed (status: {code}): {stderr}",
|
||||
)
|
||||
Error::DnsResolve { name, source } => {
|
||||
write!(f, "DNS resolve failed for {name}: {source}")
|
||||
}
|
||||
Error::CommandFailed {
|
||||
cmd,
|
||||
args,
|
||||
status,
|
||||
stderr,
|
||||
} => {
|
||||
let code = status
|
||||
.map(|c| c.to_string())
|
||||
.unwrap_or_else(|| "signal".into());
|
||||
write!(f, "{cmd} {args:?} failed (status: {code}): {stderr}",)
|
||||
}
|
||||
Error::Io(e) => write!(f, "IO error: {e}"),
|
||||
}
|
||||
@@ -44,5 +50,7 @@ impl std::error::Error for Error {
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self { Error::Io(e) }
|
||||
}
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -2,7 +2,10 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio::{net::{TcpStream, ToSocketAddrs}, time::timeout};
|
||||
use tokio::{
|
||||
net::{TcpStream, ToSocketAddrs},
|
||||
time::timeout,
|
||||
};
|
||||
|
||||
pub async fn ping_ip<T: ToSocketAddrs>(addr: T) -> bool {
|
||||
timeout(Duration::from_secs(1), TcpStream::connect(addr))
|
||||
@@ -11,4 +14,4 @@ pub async fn ping_ip<T: ToSocketAddrs>(addr: T) -> bool {
|
||||
}
|
||||
pub async fn _ping_ip_2<T: ToSocketAddrs>(_addr: T) -> bool {
|
||||
todo!("use icmp")
|
||||
}
|
||||
}
|
||||
|
||||
+51
-10
@@ -5,7 +5,11 @@ use macaddr::MacAddr;
|
||||
|
||||
use crate::{
|
||||
arpparse::{self, IpNeighLine, NUDState},
|
||||
utils::{cmd::exec_command, error::{self, Error, Result}, get_ips},
|
||||
utils::{
|
||||
cmd::exec_command,
|
||||
error::{self, Error, Result},
|
||||
get_ips,
|
||||
},
|
||||
};
|
||||
|
||||
pub async fn get_macs_2_1(machine_name: &str) -> Result<HashSet<(IpAddr, MacAddr, NUDState)>> {
|
||||
@@ -76,13 +80,8 @@ pub async fn get_macs(
|
||||
state: Option<NUDState>,
|
||||
) -> Result<Vec<IpNeighLine>> {
|
||||
// Collect IPs early (before any await) to avoid holding generics across await points
|
||||
let ip_list: Option<Vec<IpAddr>> = ips.map(|slice| {
|
||||
slice
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|ip| ip.to_canonical())
|
||||
.collect()
|
||||
});
|
||||
let ip_list: Option<Vec<IpAddr>> =
|
||||
ips.map(|slice| slice.iter().copied().map(|ip| ip.to_canonical()).collect());
|
||||
|
||||
// Resolve by machine name if no IPs provided but we have a name
|
||||
let ip_list = match (ip_list, machine_name) {
|
||||
@@ -128,15 +127,57 @@ pub async fn get_macs(
|
||||
} else {
|
||||
parsed.collect()
|
||||
};
|
||||
Ok::<Vec<IpNeighLine>, error::Error>(rows)
|
||||
Ok::<Vec<IpNeighLine>, error::Error>(rows)
|
||||
};
|
||||
|
||||
// If we have specific IPs, query each; otherwise query the whole table once
|
||||
if !ip_list.is_empty() {
|
||||
let futures = ip_list.into_iter().map(|ip| run_one(Some(ip)));
|
||||
let res = futures::future::try_join_all(futures).await?;
|
||||
let res = futures::future::try_join_all(futures).await?;
|
||||
Ok(res.into_iter().flatten().collect())
|
||||
} else {
|
||||
run_one(None).await
|
||||
}
|
||||
}
|
||||
|
||||
pub mod dev {
|
||||
use std::{collections::HashSet, sync::LazyLock};
|
||||
|
||||
pub fn get_dev() -> HashSet<String> {
|
||||
// Prefer /sys/class/net, fallback to /proc/net/dev; filter out loopback
|
||||
let mut devs: HashSet<String> = HashSet::new();
|
||||
if let Ok(rd) = std::fs::read_dir("/sys/class/net") {
|
||||
for e in rd.flatten() {
|
||||
if let Ok(name) = e.file_name().into_string()
|
||||
&& name != "lo"
|
||||
&& !name.is_empty()
|
||||
{
|
||||
devs.insert(name);
|
||||
}
|
||||
}
|
||||
} else if let Ok(txt) = std::fs::read_to_string("/proc/net/dev") {
|
||||
for line in txt.lines().skip(2) {
|
||||
// skip headers
|
||||
if let Some((name, _rest)) = line.split_once(':') {
|
||||
let n = name.trim().to_string();
|
||||
if n != "lo" && !n.is_empty() {
|
||||
devs.insert(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
devs
|
||||
}
|
||||
|
||||
pub static DEVS: LazyLock<HashSet<String>> = LazyLock::new(get_dev);
|
||||
|
||||
pub fn devs_sorted() -> Vec<String> {
|
||||
let mut v: Vec<String> = DEVS.iter().cloned().collect();
|
||||
v.sort();
|
||||
v
|
||||
}
|
||||
|
||||
pub fn has_dev(name: &str) -> bool {
|
||||
DEVS.contains(name)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -2,13 +2,13 @@ use std::{io, net::IpAddr};
|
||||
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::utils::{query::get_macs_2_mac, LDA_MACS_2};
|
||||
use crate::utils::query::get_macs_2_mac;
|
||||
|
||||
pub async fn wake(machine_name: &str) -> io::Result<u32> {
|
||||
let suh = UdpSocket::bind("0.0.0.0:0").await?;
|
||||
suh.set_broadcast(true)?;
|
||||
let mut macs = get_macs_2_mac(machine_name).await.unwrap_or_default();
|
||||
macs.extend(*LDA_MACS_2);
|
||||
let /* mut */ macs = get_macs_2_mac(machine_name).await.unwrap_or_default();
|
||||
// macs.extend(*LDA_MACS_2);
|
||||
let mut sent_ok = 0;
|
||||
for mac in macs {
|
||||
let mb = mac.as_bytes();
|
||||
@@ -29,4 +29,4 @@ pub async fn wake(machine_name: &str) -> io::Result<u32> {
|
||||
}
|
||||
}
|
||||
Ok(sent_ok)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,14 +54,14 @@ function buildStatusUrl(name) {
|
||||
const u = new URL("/api/status", location.origin);
|
||||
if (name) u.searchParams.set("name", name);
|
||||
// forward multi-value filters from current page URL
|
||||
for (const k of ["ip", "dev", "nud"]) {
|
||||
for (const k of ["ip", "dev", "nud", "mac"]) {
|
||||
const vals = qs.getAll(k);
|
||||
for (const v of vals) u.searchParams.append(k, v);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
/** @param {{ name: String, table: Array<{ ip, dev, mac, state }>, filters: {ip, dev, nud}} | { name: String, error }} data from /api/status */
|
||||
/** @param {{ name?: String, table: Array<{ ip, dev, mac, state }>, filters: {ip, dev, nud, mac}} | { name?: String, error }} data from /api/status */
|
||||
function renderStatus(data) {
|
||||
const tbl = document.createElement("table");
|
||||
tbl.innerHTML = `<tr><th>IP</th><th>MAC</th><th>State</th><th>IF</th></tr>`;
|
||||
@@ -82,6 +82,8 @@ function renderStatus(data) {
|
||||
parts.push(`dev=[${data.filters.dev.join(", ")}]`);
|
||||
if (Array.isArray(data.filters.nud) && data.filters.nud.length)
|
||||
parts.push(`nud=[${data.filters.nud.join(", ")}]`);
|
||||
if (Array.isArray(data.filters.mac) && data.filters.mac.length)
|
||||
parts.push(`mac=[${data.filters.mac.join(", ")}]`);
|
||||
if (parts.length) {
|
||||
const info = document.createElement("div");
|
||||
info.className = "filters";
|
||||
@@ -120,7 +122,7 @@ async function fetchStatus(name) {
|
||||
msg = await r.text();
|
||||
}
|
||||
elLog.textContent = `status error: ${msg}`;
|
||||
// elHtml.textContent = msg;
|
||||
// elHtml.textContent = msg;
|
||||
setPill("bad", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user