This commit is contained in:
lda committed 2026-04-06 17:42:04 +07:00
1 parent 9305702b36
commit c393111cb0
9 files changed
+374 -316

No files matched your search

+8
View File
@@ -1,3 +1,9 @@
//! Compatibility types and mappers for the legacy HTTP/static client.
//!
//! These types intentionally preserve old JSON shapes expected by `/static`
//! while the core and service layers evolve underneath them. They do not define
//! the long-term domain model of the project.
use serde::Serialize;
use wakey_core::parse::mac;
use wakey_core::{
@@ -49,6 +55,8 @@ pub struct LegacyWakeResultRow {
}
/// Map legacy-style status rows into the old response shape.
///
/// This helper exists for compatibility with the original frontend contract.
pub fn legacy_status_from_domain(status: Status<NeighborEntry>) -> LegacyStatusResponse {
LegacyStatusResponse {
name: status.name,
+18 -2
View File
@@ -1,3 +1,9 @@
//! Temporary HTTP adapter for the legacy web/static surface.
//!
//! This module exists to keep the old `/api` routes and `/static` frontend
//! working while the project is migrated toward a service-first architecture.
//! New product logic should live in [`crate::service`], not here.
pub mod compat;
pub mod route;
@@ -5,12 +11,17 @@ use std::{io, net::SocketAddr};
use axum::Router;
use tokio::net::TcpListener;
use tower_http::services::ServeDir;
use tower_http::{services::ServeDir, trace::TraceLayer};
use tracing::info;
/// Build the temporary HTTP app that serves the legacy API and static frontend.
///
/// This is a compatibility surface. It should stay thin and delegate actual
/// product behavior to the service layer.
pub fn http_app(static_root: std::path::PathBuf) -> Router {
Router::new()
.nest("/api", route::api_router())
.layer(TraceLayer::new_for_http())
.fallback_service(axum::routing::get_service(
ServeDir::new(static_root)
.append_index_html_on_directories(true)
@@ -22,12 +33,17 @@ pub fn http_app(static_root: std::path::PathBuf) -> Router {
}
/// Serve the temporary HTTP app on the provided socket address.
///
/// This is intended for transition and compatibility, not as the long-term
/// architecture boundary of the project.
pub async fn serve_http(addr: SocketAddr, static_root: std::path::PathBuf) -> io::Result<()> {
info!(%addr, static_root = %static_root.display(), "starting legacy http adapter");
let listener = TcpListener::bind(addr).await?;
axum::serve(listener, http_app(static_root).into_make_service()).await
}
/// Serve the HTTP app using the `static/` directory next to the current executable.
/// Serve the HTTP app using the `static/` directory next to the current
/// executable.
pub async fn serve_http_from_current_exe(addr: SocketAddr) -> io::Result<()> {
let exe = std::env::current_exe()?;
let root = exe
+6
View File
@@ -1,2 +1,8 @@
//! Transitional compatibility wrappers preserved during the migration.
//!
//! Items in this module exist so the codebase can keep working while older
//! parsing paths and adapter surfaces are being retired or replaced. New logic
//! should prefer the service layer and the dedicated crate boundaries instead.
pub mod arpparse;
pub mod dhcpparse;
+66 -2
View File
@@ -2,7 +2,8 @@ mod cli_table;
use std::net::{IpAddr, SocketAddr};
use clap::{Args, Parser, Subcommand};
use clap::{ArgAction, Args, Parser, Subcommand};
use tracing::{debug, info};
use wakey_core::{DeviceFilters, DeviceQuery, InterfaceSummary, WakeResult};
#[derive(Parser)]
@@ -12,6 +13,10 @@ use wakey_core::{DeviceFilters, DeviceQuery, InterfaceSummary, WakeResult};
long_about = "Wakey can run as a local/operator CLI or serve the legacy HTTP/static interface during the migration to a service-first architecture."
)]
struct Cli {
/// Increase log verbosity. Use `-v` for debug and `-vv` for trace.
#[arg(short = 'v', long = "verbose", action = ArgAction::Count, global = true)]
verbose: u8,
#[command(subcommand)]
command: Command,
}
@@ -200,14 +205,17 @@ fn main() -> anyhow::Result<()> {
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
init_tracing(cli.verbose);
match cli.command {
Command::Http(args) => {
let addr = SocketAddr::new(args.host, args.port);
info!(%addr, "dispatching http command");
wakey::serve_http_from_current_exe(addr).await?;
}
Command::Status(args) => {
let as_json = args.json;
let query = status_args_to_query(args);
debug!(?query, json = as_json, "dispatching status command");
let status = if query.name.is_some()
&& query.filter.ips.is_empty()
&& query.filter.devs.is_empty()
@@ -228,6 +236,11 @@ async fn main() -> anyhow::Result<()> {
}
}
Command::Leases(args) => {
debug!(
include_state = args.include_state,
json = args.json,
"dispatching leases command"
);
let leases = wakey::get_leases(wakey_core::LeaseQuery {
include_state: args.include_state,
})
@@ -240,6 +253,13 @@ async fn main() -> anyhow::Result<()> {
}
Command::Wake(args) => {
let as_json = args.json;
debug!(
has_query = args.query.is_some(),
has_mac = args.mac.is_some(),
has_ip = args.ip.is_some(),
json = as_json,
"dispatching wake command"
);
let result = run_wake(args).await?;
if as_json {
println!("{}", serde_json::to_string_pretty(&result)?);
@@ -248,6 +268,7 @@ async fn main() -> anyhow::Result<()> {
}
}
Command::Devs(args) => {
debug!(dev = ?args.dev, up = args.up, json = args.json, "dispatching devs command");
let devs = if let Some(name) = &args.dev {
wakey::get_interface_summary(name)
.await?
@@ -267,9 +288,32 @@ async fn main() -> anyhow::Result<()> {
Ok(())
}
#[cfg(target_os = "linux")]
fn init_tracing(verbose: u8) {
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
let filter = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new(default_filter_for_verbosity(verbose)))
.expect("static tracing filter should parse");
tracing_subscriber::registry()
.with(filter)
.with(fmt::layer())
.init();
}
#[cfg(target_os = "linux")]
fn default_filter_for_verbosity(verbose: u8) -> &'static str {
match verbose {
0 => "wakey=info,tower_http=info",
1 => "wakey=debug,tower_http=debug",
_ => "wakey=trace,tower_http=trace",
}
}
#[cfg(test)]
mod tests {
use super::WakeArgs;
use super::{WakeArgs, default_filter_for_verbosity};
#[test]
fn wake_rejects_ip_without_mac() {
@@ -318,4 +362,24 @@ mod tests {
})
.expect("manual mac mode should be accepted");
}
#[test]
fn verbosity_maps_to_expected_default_filters() {
assert_eq!(
default_filter_for_verbosity(0),
"wakey=info,tower_http=info"
);
assert_eq!(
default_filter_for_verbosity(1),
"wakey=debug,tower_http=debug"
);
assert_eq!(
default_filter_for_verbosity(2),
"wakey=trace,tower_http=trace"
);
assert_eq!(
default_filter_for_verbosity(9),
"wakey=trace,tower_http=trace"
);
}
}
+4 -1
View File
@@ -122,7 +122,10 @@ mod tests {
let err = broadcast_wake_targets_from_interfaces(&interfaces, mac)
.expect_err("should error without broadcast-capable interfaces");
assert!(err.to_string().contains("no broadcast-capable interfaces found"));
assert!(
err.to_string()
.contains("no broadcast-capable interfaces found")
);
}
#[test]