Feature Flags and shit codex did idfk
This commit is contained in:
+14
-2
@@ -5,6 +5,10 @@ version = "0.0.2"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = ["gitea"]
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
experimental-nl = ["dep:rtnetlink"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
macaddr = { version = "1", features = ["serde", "serde_std"] }
|
macaddr = { version = "1", features = ["serde", "serde_std"] }
|
||||||
strum = { version = "0", features = ["derive", "strum_macros"] }
|
strum = { version = "0", features = ["derive", "strum_macros"] }
|
||||||
@@ -13,7 +17,15 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
serde_with = { version = "3", features = ["json"] }
|
serde_with = { version = "3", features = ["json"] }
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
tokio = { version = "1", features = ["fs", "process", "rt-multi-thread", "io-util", "macros"] }
|
tokio = { version = "1", features = [
|
||||||
rtnetlink = "0"
|
"fs",
|
||||||
|
"process",
|
||||||
|
"rt-multi-thread",
|
||||||
|
"io-util",
|
||||||
|
"macros",
|
||||||
|
] }
|
||||||
futures = "0"
|
futures = "0"
|
||||||
|
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
rtnetlink = { version = "0", optional = true }
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
//! i also need to see devices and idk MAYBE maybe not MAYBE UHHHHHH maybe broadcast
|
//! i also need to see devices and idk MAYBE maybe not MAYBE UHHHHHH maybe broadcast
|
||||||
//!
|
//!
|
||||||
//! LOWK if this were to be calls to kernel or some bullshit then PLEASE because doing ts parsing its hell cuh
|
//! LOWK if this were to be calls to kernel or some bullshit then PLEASE because doing ts parsing its hell cuh
|
||||||
|
|
||||||
pub mod subcommands;
|
pub mod subcommands;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
//! lowk why its free but its indirection and its ass
|
//! lowk why its free but its indirection and its ass
|
||||||
|
|
||||||
pub mod json;
|
pub mod json;
|
||||||
|
#[cfg(all(unix, feature = "experimental-nl"))]
|
||||||
pub mod nl;
|
pub mod nl;
|
||||||
|
|
||||||
|
pub use crate::subcommands::Backend;
|
||||||
use crate::utils::serialize::mac::option_mac;
|
use crate::utils::serialize::mac::option_mac;
|
||||||
use macaddr::MacAddr;
|
use macaddr::MacAddr;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -40,3 +42,18 @@ pub struct AddrInfo {
|
|||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
// many more exist; we only take what we need
|
// many more exist; we only take what we need
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<AddrOutput>> {
|
||||||
|
get_with_backend(Backend::Json, dev).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_with_backend(
|
||||||
|
backend: Backend,
|
||||||
|
dev: Option<&str>,
|
||||||
|
) -> anyhow::Result<Vec<AddrOutput>> {
|
||||||
|
match backend {
|
||||||
|
Backend::Json => json::get(dev).await,
|
||||||
|
#[cfg(all(unix, feature = "experimental-nl"))]
|
||||||
|
Backend::Netlink => nl::get(dev).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//! i said i aint doing ts no more why am i still here
|
//! i said i aint doing ts no more why am i still here
|
||||||
|
#![cfg(unix)]
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
|
|
||||||
use crate::subcommands::address::AddrOutput;
|
use crate::subcommands::address::AddrOutput;
|
||||||
|
|||||||
@@ -1,2 +1,9 @@
|
|||||||
pub mod address;
|
pub mod address;
|
||||||
pub mod neighbor;
|
pub mod neighbor;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Backend {
|
||||||
|
Json,
|
||||||
|
#[cfg(all(unix, feature = "experimental-nl"))]
|
||||||
|
Netlink,
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
//! yes. this is a real call.
|
//! yes. this is a real call.
|
||||||
|
|
||||||
pub mod json;
|
pub mod json;
|
||||||
|
#[cfg(all(unix, feature = "experimental-nl"))]
|
||||||
pub mod nl;
|
pub mod nl;
|
||||||
|
|
||||||
|
pub use crate::subcommands::Backend;
|
||||||
use crate::utils::serialize::mac::option_mac;
|
use crate::utils::serialize::mac::option_mac;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
@@ -80,3 +82,28 @@ pub struct NeighborItem {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub state: Vec<NUDState>,
|
pub state: Vec<NUDState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get(
|
||||||
|
ip: Option<IpAddr>,
|
||||||
|
dev: Option<&str>,
|
||||||
|
nud: &[NUDState],
|
||||||
|
) -> anyhow::Result<Vec<NeighborItem>> {
|
||||||
|
get_with_backend(Backend::Json, ip, dev, nud).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_with_backend(
|
||||||
|
backend: Backend,
|
||||||
|
ip: Option<IpAddr>,
|
||||||
|
dev: Option<&str>,
|
||||||
|
nud: &[NUDState],
|
||||||
|
) -> anyhow::Result<Vec<NeighborItem>> {
|
||||||
|
match backend {
|
||||||
|
Backend::Json => json::get(ip, dev, nud).await,
|
||||||
|
#[cfg(all(unix, feature = "experimental-nl"))]
|
||||||
|
Backend::Netlink => {
|
||||||
|
let ips: Vec<IpAddr> = ip.into_iter().collect();
|
||||||
|
let devs: Vec<&str> = dev.into_iter().collect();
|
||||||
|
nl::get(&ips, &devs, nud, &[]).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//! rtnetlink-based neighbor table query. One syscall, filter in userspace.
|
//! rtnetlink-based neighbor table query. One syscall, filter in userspace.
|
||||||
|
#![cfg(unix)]
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
net::IpAddr,
|
net::IpAddr,
|
||||||
|
|||||||
+10
-5
@@ -2,16 +2,17 @@ use std::collections::HashSet;
|
|||||||
|
|
||||||
use lda_ipjs::subcommands::{address, neighbor};
|
use lda_ipjs::subcommands::{address, neighbor};
|
||||||
|
|
||||||
|
#[cfg(all(unix, feature = "experimental-nl"))]
|
||||||
#[tokio::test] // ← Use tokio::test instead of manual #[tokio::main]
|
#[tokio::test] // ← Use tokio::test instead of manual #[tokio::main]
|
||||||
async fn ball1() -> anyhow::Result<()> {
|
async fn ball1() -> anyhow::Result<()> {
|
||||||
let result = neighbor::nl::get(&[], &[] as &[&str], &[], &[]).await?;
|
let result = neighbor::get_with_backend(neighbor::Backend::Netlink, None, None, &[]).await?;
|
||||||
println!("netlink results: {:?}", result);
|
println!("netlink results: {:?}", result);
|
||||||
Ok(()) // ← Don't force error, let it succeed
|
Ok(()) // ← Don't force error, let it succeed
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn ball2() -> anyhow::Result<()> {
|
async fn ball2() -> anyhow::Result<()> {
|
||||||
let result = neighbor::json::get(None, None, &[]).await?;
|
let result = neighbor::get_with_backend(neighbor::Backend::Json, None, None, &[]).await?;
|
||||||
println!("json results: {:?}", result);
|
println!("json results: {:?}", result);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -91,11 +92,14 @@ impl TypeName for serde_json::Value {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn ball_compare_backends() -> anyhow::Result<()> {
|
async fn ball_compare_backends() -> anyhow::Result<()> {
|
||||||
println!("=== JSON Backend ===");
|
println!("=== JSON Backend ===");
|
||||||
let json_result = neighbor::json::get(None, None, &[]).await?;
|
let json_result = neighbor::get_with_backend(neighbor::Backend::Json, None, None, &[]).await?;
|
||||||
println!("Got {} entries from JSON", json_result.len());
|
println!("Got {} entries from JSON", json_result.len());
|
||||||
|
|
||||||
|
#[cfg(all(unix, feature = "experimental-nl"))]
|
||||||
|
{
|
||||||
println!("\n=== Netlink Backend ===");
|
println!("\n=== Netlink Backend ===");
|
||||||
let nl_result = neighbor::nl::get(&[], &[] as &[&str], &[], &[]).await?;
|
let nl_result =
|
||||||
|
neighbor::get_with_backend(neighbor::Backend::Netlink, None, None, &[]).await?;
|
||||||
println!("Got {} entries from netlink", nl_result.len());
|
println!("Got {} entries from netlink", nl_result.len());
|
||||||
|
|
||||||
// Compare counts
|
// Compare counts
|
||||||
@@ -115,6 +119,7 @@ async fn ball_compare_backends() -> anyhow::Result<()> {
|
|||||||
len1 = a.len(),
|
len1 = a.len(),
|
||||||
len2 = b.len()
|
len2 = b.len()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -126,7 +131,7 @@ async fn ball_compare_backends() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn ipjas() -> anyhow::Result<()> {
|
async fn ipjas() -> anyhow::Result<()> {
|
||||||
let cuh = address::json::get(None).await?;
|
let cuh = address::get_with_backend(address::Backend::Json, None).await?;
|
||||||
println!("{cuh:#?}");
|
println!("{cuh:#?}");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
use serde::Serialize;
|
||||||
|
use wakey_core::parse::mac;
|
||||||
|
use wakey_core::{
|
||||||
|
DeviceFilters, DhcpLeaseWithState, NeighborEntry, Status, WakeResult, WakeTargetResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct LegacyStatusRow {
|
||||||
|
pub ip: std::net::IpAddr,
|
||||||
|
pub dev: Option<String>,
|
||||||
|
#[serde(with = "mac::option_mac")]
|
||||||
|
pub mac: Option<macaddr::MacAddr>,
|
||||||
|
pub state: wakey_core::NeighborState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct LegacyStatusResponse {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub table: Vec<LegacyStatusRow>,
|
||||||
|
pub filters: DeviceFilters,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct LegacyLeaseRow {
|
||||||
|
pub expires_epoch: u64,
|
||||||
|
pub ip: std::net::IpAddr,
|
||||||
|
#[serde(with = "mac")]
|
||||||
|
pub mac: macaddr::MacAddr,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub nud_state: Option<wakey_core::NeighborState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct LegacyWakeResult {
|
||||||
|
pub result: Vec<LegacyWakeResultRow>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize)]
|
||||||
|
pub struct LegacyWakeResultRow {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub target: wakey_core::WakeTarget,
|
||||||
|
pub status: wakey_core::WakeStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn legacy_status_from_domain(status: Status<NeighborEntry>) -> LegacyStatusResponse {
|
||||||
|
LegacyStatusResponse {
|
||||||
|
name: status.name,
|
||||||
|
table: status.table.into_iter().map(legacy_status_row).collect(),
|
||||||
|
filters: status.filters,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn legacy_status_row(row: NeighborEntry) -> LegacyStatusRow {
|
||||||
|
LegacyStatusRow {
|
||||||
|
ip: row.ip,
|
||||||
|
dev: row.dev,
|
||||||
|
mac: row.mac,
|
||||||
|
state: row.state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn legacy_leases_from_domain(leases: Vec<DhcpLeaseWithState>) -> Vec<LegacyLeaseRow> {
|
||||||
|
leases.into_iter().map(legacy_lease_row).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn legacy_lease_row(lease: DhcpLeaseWithState) -> LegacyLeaseRow {
|
||||||
|
LegacyLeaseRow {
|
||||||
|
expires_epoch: lease.lease_line.expires_epoch,
|
||||||
|
ip: lease.lease_line.ip,
|
||||||
|
mac: lease.lease_line.mac,
|
||||||
|
name: lease.lease_line.name,
|
||||||
|
nud_state: lease.nud_state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn legacy_wake_from_domain(result: WakeResult) -> LegacyWakeResult {
|
||||||
|
LegacyWakeResult {
|
||||||
|
result: result.result.into_iter().map(legacy_wake_row).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn legacy_wake_row(row: WakeTargetResult) -> LegacyWakeResultRow {
|
||||||
|
LegacyWakeResultRow {
|
||||||
|
target: row.target,
|
||||||
|
status: row.status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
use wakey_core::{DhcpLease, NeighborState, WakeStatus, WakeTarget};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maps_status_to_legacy_shape() {
|
||||||
|
let status = Status {
|
||||||
|
name: Some("pc".into()),
|
||||||
|
table: vec![NeighborEntry {
|
||||||
|
ip: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)),
|
||||||
|
dev: Some("br-lan".into()),
|
||||||
|
mac: Some("aa:bb:cc:dd:ee:ff".parse().expect("mac")),
|
||||||
|
state: NeighborState::Reachable,
|
||||||
|
}],
|
||||||
|
filters: DeviceFilters::default(),
|
||||||
|
};
|
||||||
|
let legacy = legacy_status_from_domain(status);
|
||||||
|
assert_eq!(legacy.name.as_deref(), Some("pc"));
|
||||||
|
assert_eq!(legacy.table.len(), 1);
|
||||||
|
assert_eq!(legacy.table[0].state, NeighborState::Reachable);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maps_leases_to_legacy_shape() {
|
||||||
|
let leases = vec![DhcpLeaseWithState {
|
||||||
|
lease_line: DhcpLease {
|
||||||
|
expires_epoch: 42,
|
||||||
|
ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
|
||||||
|
mac: "aa:bb:cc:dd:ee:ff".parse().expect("mac"),
|
||||||
|
name: Some("pc".into()),
|
||||||
|
},
|
||||||
|
nud_state: Some(NeighborState::Reachable),
|
||||||
|
}];
|
||||||
|
let legacy = legacy_leases_from_domain(leases);
|
||||||
|
assert_eq!(legacy.len(), 1);
|
||||||
|
assert_eq!(legacy[0].name.as_deref(), Some("pc"));
|
||||||
|
assert_eq!(legacy[0].nud_state, Some(NeighborState::Reachable));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maps_wake_to_legacy_shape() {
|
||||||
|
let result = WakeResult {
|
||||||
|
result: vec![WakeTargetResult {
|
||||||
|
target: WakeTarget {
|
||||||
|
ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
|
||||||
|
mac: Some("aa:bb:cc:dd:ee:ff".parse().expect("mac")),
|
||||||
|
},
|
||||||
|
status: WakeStatus::Succeed,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
let legacy = legacy_wake_from_domain(result);
|
||||||
|
assert_eq!(legacy.result.len(), 1);
|
||||||
|
assert_eq!(legacy.result[0].status, WakeStatus::Succeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-7
@@ -1,4 +1,5 @@
|
|||||||
pub mod arpparse;
|
pub mod arpparse;
|
||||||
|
pub mod compat;
|
||||||
pub mod dhcpparse;
|
pub mod dhcpparse;
|
||||||
pub mod route;
|
pub mod route;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
@@ -17,7 +18,8 @@ use wakey_core::{
|
|||||||
pub type StatusResponse = Status<NeighborEntry>;
|
pub type StatusResponse = Status<NeighborEntry>;
|
||||||
|
|
||||||
pub async fn resolve_query(input: impl Into<String>) -> Result<DeviceQuery> {
|
pub async fn resolve_query(input: impl Into<String>) -> Result<DeviceQuery> {
|
||||||
Ok(match wakey_linux::devices::classify_query(input.into()).await {
|
Ok(
|
||||||
|
match wakey_linux::devices::classify_query(input.into()).await {
|
||||||
QueryInput::Ip(ip_addr) => DeviceQuery {
|
QueryInput::Ip(ip_addr) => DeviceQuery {
|
||||||
filter: DeviceFilters {
|
filter: DeviceFilters {
|
||||||
ips: vec![ip_addr],
|
ips: vec![ip_addr],
|
||||||
@@ -50,7 +52,8 @@ pub async fn resolve_query(input: impl Into<String>) -> Result<DeviceQuery> {
|
|||||||
name: Some(name),
|
name: Some(name),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_status(query: DeviceQuery) -> Result<StatusResponse> {
|
pub async fn get_status(query: DeviceQuery) -> Result<StatusResponse> {
|
||||||
@@ -117,16 +120,14 @@ pub async fn get_ips(name: impl AsRef<str>) -> Result<Vec<std::net::IpAddr>> {
|
|||||||
pub fn http_app(static_root: std::path::PathBuf) -> Router {
|
pub fn http_app(static_root: std::path::PathBuf) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.nest("/api", route::api_router())
|
.nest("/api", route::api_router())
|
||||||
.fallback_service(
|
.fallback_service(axum::routing::get_service(
|
||||||
axum::routing::get_service(
|
|
||||||
ServeDir::new(static_root)
|
ServeDir::new(static_root)
|
||||||
.append_index_html_on_directories(true)
|
.append_index_html_on_directories(true)
|
||||||
.precompressed_br()
|
.precompressed_br()
|
||||||
.precompressed_deflate()
|
.precompressed_deflate()
|
||||||
.precompressed_gzip()
|
.precompressed_gzip()
|
||||||
.precompressed_zstd(),
|
.precompressed_zstd(),
|
||||||
),
|
))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn serve_http(addr: SocketAddr, static_root: std::path::PathBuf) -> io::Result<()> {
|
pub async fn serve_http(addr: SocketAddr, static_root: std::path::PathBuf) -> io::Result<()> {
|
||||||
@@ -161,7 +162,10 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn resolve_query_parses_ip() {
|
async fn resolve_query_parses_ip() {
|
||||||
let query = resolve_query("192.168.1.10").await.expect("resolve query");
|
let query = resolve_query("192.168.1.10").await.expect("resolve query");
|
||||||
assert_eq!(query.filter.ips, vec![IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10))]);
|
assert_eq!(
|
||||||
|
query.filter.ips,
|
||||||
|
vec![IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10))]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
+5
-1
@@ -13,7 +13,11 @@ pub async fn get_dhcp_leases(
|
|||||||
let include_state = include_state.as_deref().map(boolish_str).unwrap_or(false);
|
let include_state = include_state.as_deref().map(boolish_str).unwrap_or(false);
|
||||||
|
|
||||||
match crate::get_leases(include_state).await {
|
match crate::get_leases(include_state).await {
|
||||||
Ok(leases) => (StatusCode::OK, Json(leases)).into_response(),
|
Ok(leases) => (
|
||||||
|
StatusCode::OK,
|
||||||
|
Json(crate::compat::legacy_leases_from_domain(leases)),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
Err(e) => ApiError {
|
Err(e) => ApiError {
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
code: StatusCode::BAD_GATEWAY,
|
code: StatusCode::BAD_GATEWAY,
|
||||||
|
|||||||
+6
-2
@@ -1,11 +1,15 @@
|
|||||||
use crate::route::error::ApiError;
|
use crate::route::error::ApiError;
|
||||||
use axum::{Json, http::StatusCode, response::IntoResponse};
|
use axum::{Json, http::StatusCode, response::IntoResponse};
|
||||||
use axum_extra::extract::Query;
|
use axum_extra::extract::Query;
|
||||||
pub use wakey_core::{DeviceQuery, NamePath, Status};
|
pub use wakey_core::{DeviceQuery, NamePath};
|
||||||
|
|
||||||
pub async fn get_status_json(Query(query): Query<DeviceQuery>) -> impl IntoResponse {
|
pub async fn get_status_json(Query(query): Query<DeviceQuery>) -> impl IntoResponse {
|
||||||
match crate::get_status(query).await {
|
match crate::get_status(query).await {
|
||||||
Ok(status) => (StatusCode::OK, Json(status)).into_response(),
|
Ok(status) => (
|
||||||
|
StatusCode::OK,
|
||||||
|
Json(crate::compat::legacy_status_from_domain(status)),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
Err(error) => ApiError {
|
Err(error) => ApiError {
|
||||||
code: StatusCode::BAD_GATEWAY,
|
code: StatusCode::BAD_GATEWAY,
|
||||||
error: error
|
error: error
|
||||||
|
|||||||
+6
-2
@@ -1,10 +1,14 @@
|
|||||||
use crate::route::error::ApiError;
|
use crate::route::error::ApiError;
|
||||||
use axum::{extract::Json, http::StatusCode, response::IntoResponse};
|
use axum::{extract::Json, http::StatusCode, response::IntoResponse};
|
||||||
pub use wakey_core::{WakeResult, WakeTarget};
|
pub use wakey_core::WakeTarget;
|
||||||
|
|
||||||
pub async fn wake_multi(Json(req): Json<Vec<WakeTarget>>) -> impl IntoResponse {
|
pub async fn wake_multi(Json(req): Json<Vec<WakeTarget>>) -> impl IntoResponse {
|
||||||
match crate::wake_targets(req).await {
|
match crate::wake_targets(req).await {
|
||||||
Ok(result) => (StatusCode::OK, Json(result)).into_response(),
|
Ok(result) => (
|
||||||
|
StatusCode::OK,
|
||||||
|
Json(crate::compat::legacy_wake_from_domain(result)),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error = format!("Error: {}", error);
|
let error = format!("Error: {}", error);
|
||||||
ApiError::ise(error).into_response()
|
ApiError::ise(error).into_response()
|
||||||
|
|||||||
@@ -15,3 +15,4 @@ wakey-core = { path = "../wakey-core" }
|
|||||||
path = "../ipjs"
|
path = "../ipjs"
|
||||||
registry = "gitea"
|
registry = "gitea"
|
||||||
version = "*"
|
version = "*"
|
||||||
|
features = ["experimental-nl"]
|
||||||
|
|||||||
@@ -46,16 +46,61 @@ pub async fn get_neighbors(
|
|||||||
let nud_filter: Vec<neighbor::NUDState> = state.iter().copied().map(to_ipjs_state).collect();
|
let nud_filter: Vec<neighbor::NUDState> = state.iter().copied().map(to_ipjs_state).collect();
|
||||||
let dev_strs: Vec<&str> = devs.iter().map(AsRef::as_ref).collect();
|
let dev_strs: Vec<&str> = devs.iter().map(AsRef::as_ref).collect();
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
let results = neighbor::nl::get(&ip_filter, &dev_strs, &nud_filter, macs)
|
let results = neighbor::nl::get(&ip_filter, &dev_strs, &nud_filter, macs)
|
||||||
.await
|
.await
|
||||||
.context("rtnetlink failed")?
|
.context("rtnetlink failed")?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(map_neighbor_item)
|
.map(map_neighbor_item)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
{
|
||||||
|
let mut results = Vec::new();
|
||||||
|
if ip_filter.is_empty() {
|
||||||
|
let dev = dev_strs.first().copied();
|
||||||
|
results = neighbor::get_with_backend(neighbor::Backend::Json, None, dev, &nud_filter)
|
||||||
|
.await
|
||||||
|
.context("ip -j neigh failed")?
|
||||||
|
.into_iter()
|
||||||
|
.map(map_neighbor_item)
|
||||||
|
.collect();
|
||||||
|
} else {
|
||||||
|
for ip in &ip_filter {
|
||||||
|
let dev = dev_strs.first().copied();
|
||||||
|
let mut rows = neighbor::get_with_backend(
|
||||||
|
neighbor::Backend::Json,
|
||||||
|
Some(*ip),
|
||||||
|
dev,
|
||||||
|
&nud_filter,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("ip -j neigh failed for {ip}"))?
|
||||||
|
.into_iter()
|
||||||
|
.map(map_neighbor_item)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
results.append(&mut rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !dev_strs.is_empty() {
|
||||||
|
results.retain(|row| row.dev.as_deref().is_some_and(|d| dev_strs.contains(&d)));
|
||||||
|
}
|
||||||
|
if !macs.is_empty() {
|
||||||
|
let mac_set: HashSet<macaddr::MacAddr> = macs.iter().copied().collect();
|
||||||
|
results.retain(|row| row.mac.is_some_and(|m| mac_set.contains(&m)));
|
||||||
|
}
|
||||||
|
if !ip_filter.is_empty() {
|
||||||
|
let ip_set: HashSet<IpAddr> = ip_filter.iter().copied().collect();
|
||||||
|
results.retain(|row| ip_set.contains(&row.ip));
|
||||||
|
}
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn query_status(query: &DeviceQuery) -> Result<Vec<NeighborEntry>> {
|
pub async fn query_status(query: &DeviceQuery) -> Result<Vec<NeighborEntry>> {
|
||||||
get_neighbors(
|
get_neighbors(
|
||||||
query.name.as_slice(),
|
query.name.as_slice(),
|
||||||
|
|||||||
Reference in New Issue
Block a user