From 3bc7cf8ed36016ca3da9438a98f4fe8b8e6f9e61 Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Thu, 15 Feb 2024 17:17:30 +0100 Subject: Closes #10 & #12. Added `thiserror` crate and changed to `IntoSocketAddr` for easier usage and error handling --- src/routes/device.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/routes/device.rs') diff --git a/src/routes/device.rs b/src/routes/device.rs index c85df1b..aa52cf7 100644 --- a/src/routes/device.rs +++ b/src/routes/device.rs @@ -12,7 +12,7 @@ use crate::error::Error; pub async fn get(State(state): State>, headers: HeaderMap, Json(payload): Json) -> Result, Error> { info!("add device {}", payload.id); let secret = headers.get("authorization"); - if auth(&state.config, secret).map_err(Error::Auth)? { + if auth(&state.config, secret)? { let device = sqlx::query_as!( Device, r#" @@ -21,7 +21,7 @@ pub async fn get(State(state): State>, headers: HeaderMap, WHERE id = $1; "#, payload.id - ).fetch_one(&state.db).await.map_err(Error::DB)?; + ).fetch_one(&state.db).await?; debug!("got device {:?}", device); @@ -39,7 +39,7 @@ pub struct GetDevicePayload { pub async fn put(State(state): State>, headers: HeaderMap, Json(payload): Json) -> Result, Error> { info!("add device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); let secret = headers.get("authorization"); - if auth(&state.config, secret).map_err(Error::Auth)? { + if auth(&state.config, secret)? { sqlx::query!( r#" INSERT INTO devices (id, mac, broadcast_addr, ip) @@ -49,7 +49,7 @@ pub async fn put(State(state): State>, headers: HeaderMap, payload.mac, payload.broadcast_addr, payload.ip - ).execute(&state.db).await.map_err(Error::DB)?; + ).execute(&state.db).await?; Ok(Json(json!(PutDeviceResponse { success: true }))) } else { @@ -73,7 +73,7 @@ pub struct PutDeviceResponse { pub async fn post(State(state): State>, headers: HeaderMap, Json(payload): Json) -> Result, Error> { info!("edit device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); let secret = headers.get("authorization"); - if auth(&state.config, secret).map_err(Error::Auth)? { + if auth(&state.config, secret)? { let device = sqlx::query_as!( Device, r#" @@ -85,7 +85,7 @@ pub async fn post(State(state): State>, headers: HeaderMap, payload.broadcast_addr, payload.ip, payload.id - ).fetch_one(&state.db).await.map_err(Error::DB)?; + ).fetch_one(&state.db).await?; Ok(Json(json!(device))) } else { -- cgit v1.2.3 From 016fa3a31f8847d3f52800941b7f8fe5ef872240 Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Thu, 15 Feb 2024 19:29:48 +0100 Subject: Closes #15. No usable Error message right now --- src/auth.rs | 49 +++++++++++++--------------------------- src/error.rs | 19 +++++++++------- src/routes/device.rs | 63 +++++++++++++++++++++++++++++++++++++--------------- src/routes/start.rs | 2 +- 4 files changed, 72 insertions(+), 61 deletions(-) (limited to 'src/routes/device.rs') diff --git a/src/auth.rs b/src/auth.rs index eb4d1bf..22f87e7 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,49 +1,30 @@ -use axum::http::{StatusCode, HeaderValue}; -use axum::http::header::ToStrError; -use tracing::{debug, error, trace}; -use crate::auth::Error::{MissingSecret, WrongSecret}; +use axum::http::HeaderValue; +use tracing::{debug, trace}; use crate::config::Config; +use crate::error::Error; -pub fn auth(config: &Config, secret: Option<&HeaderValue>) -> Result { +pub fn auth(config: &Config, secret: Option<&HeaderValue>) -> Result { debug!("auth request with secret {:?}", secret); - if let Some(value) = secret { - trace!("value exists"); + let res = if let Some(value) = secret { + trace!("auth value exists"); let key = &config.apikey; if value.to_str()? == key.as_str() { debug!("successful auth"); - Ok(true) + Response::Success } else { debug!("unsuccessful auth (wrong secret)"); - Err(WrongSecret) + Response::WrongSecret } } else { debug!("unsuccessful auth (no secret)"); - Err(MissingSecret) - } + Response::MissingSecret + }; + Ok(res) } -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("wrong secret")] +#[derive(Debug)] +pub enum Response { + Success, WrongSecret, - #[error("missing secret")] - MissingSecret, - #[error("parse error: {source}")] - HeaderToStr { - #[from] - source: ToStrError - } -} - -impl Error { - pub fn get(self) -> (StatusCode, &'static str) { - match self { - Self::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"), - Self::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"), - Self::HeaderToStr { source } => { - error!("auth: {}", source); - (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") - }, - } - } + MissingSecret } diff --git a/src/error.rs b/src/error.rs index 4f1bedd..63b214e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use crate::auth::Error as AuthError; +use axum::http::header::ToStrError; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use axum::Json; @@ -11,12 +11,6 @@ pub enum Error { #[error("generic error")] Generic, - #[error("auth: {source}")] - Auth { - #[from] - source: AuthError, - }, - #[error("db: {source}")] Db { #[from] @@ -29,6 +23,12 @@ pub enum Error { source: std::num::ParseIntError, }, + #[error("header parse: {source}")] + ParseHeader { + #[from] + source: ToStrError, + }, + #[error("io: {source}")] Io { #[from] @@ -40,7 +40,6 @@ impl IntoResponse for Error { fn into_response(self) -> Response { error!("{}", self.to_string()); let (status, error_message) = match self { - Self::Auth { source } => source.get(), Self::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""), Self::Db { source } => { error!("{source}"); @@ -50,6 +49,10 @@ impl IntoResponse for Error { error!("{source}"); (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") } + Self::ParseHeader { source } => { + error!("{source}"); + (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") + } Self::ParseInt { source } => { error!("{source}"); (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") diff --git a/src/routes/device.rs b/src/routes/device.rs index aa52cf7..5ca574a 100644 --- a/src/routes/device.rs +++ b/src/routes/device.rs @@ -1,18 +1,23 @@ -use std::sync::Arc; +use crate::auth::auth; +use crate::db::Device; +use crate::error::Error; use axum::extract::State; -use axum::Json; use axum::http::HeaderMap; +use axum::Json; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; +use std::sync::Arc; use tracing::{debug, info}; -use crate::auth::auth; -use crate::db::Device; -use crate::error::Error; -pub async fn get(State(state): State>, headers: HeaderMap, Json(payload): Json) -> Result, Error> { +pub async fn get( + State(state): State>, + headers: HeaderMap, + Json(payload): Json, +) -> Result, Error> { info!("add device {}", payload.id); let secret = headers.get("authorization"); - if auth(&state.config, secret)? { + let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); + if authorized { let device = sqlx::query_as!( Device, r#" @@ -21,7 +26,9 @@ pub async fn get(State(state): State>, headers: HeaderMap, WHERE id = $1; "#, payload.id - ).fetch_one(&state.db).await?; + ) + .fetch_one(&state.db) + .await?; debug!("got device {:?}", device); @@ -36,10 +43,18 @@ pub struct GetDevicePayload { id: String, } -pub async fn put(State(state): State>, headers: HeaderMap, Json(payload): Json) -> Result, Error> { - info!("add device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); +pub async fn put( + State(state): State>, + headers: HeaderMap, + Json(payload): Json, +) -> Result, Error> { + info!( + "add device {} ({}, {}, {})", + payload.id, payload.mac, payload.broadcast_addr, payload.ip + ); let secret = headers.get("authorization"); - if auth(&state.config, secret)? { + let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); + if authorized { sqlx::query!( r#" INSERT INTO devices (id, mac, broadcast_addr, ip) @@ -49,7 +64,9 @@ pub async fn put(State(state): State>, headers: HeaderMap, payload.mac, payload.broadcast_addr, payload.ip - ).execute(&state.db).await?; + ) + .execute(&state.db) + .await?; Ok(Json(json!(PutDeviceResponse { success: true }))) } else { @@ -62,18 +79,26 @@ pub struct PutDevicePayload { id: String, mac: String, broadcast_addr: String, - ip: String + ip: String, } #[derive(Serialize)] pub struct PutDeviceResponse { - success: bool + success: bool, } -pub async fn post(State(state): State>, headers: HeaderMap, Json(payload): Json) -> Result, Error> { - info!("edit device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); +pub async fn post( + State(state): State>, + headers: HeaderMap, + Json(payload): Json, +) -> Result, Error> { + info!( + "edit device {} ({}, {}, {})", + payload.id, payload.mac, payload.broadcast_addr, payload.ip + ); let secret = headers.get("authorization"); - if auth(&state.config, secret)? { + let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); + if authorized { let device = sqlx::query_as!( Device, r#" @@ -85,7 +110,9 @@ pub async fn post(State(state): State>, headers: HeaderMap, payload.broadcast_addr, payload.ip, payload.id - ).fetch_one(&state.db).await?; + ) + .fetch_one(&state.db) + .await?; Ok(Json(json!(device))) } else { diff --git a/src/routes/start.rs b/src/routes/start.rs index 66b7cb4..ec4f98f 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs @@ -20,7 +20,7 @@ pub async fn start( ) -> Result, Error> { info!("POST request"); let secret = headers.get("authorization"); - let authorized = auth(&state.config, secret)?; + let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); if authorized { let device = sqlx::query_as!( Device, -- cgit v1.2.3 From 2f9f18b80a9e2134f674f345e48a5f21de5efadd Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Sun, 18 Feb 2024 21:16:46 +0100 Subject: Refactor stuff. Use Postgres Types --- ...f32262fd643b452aacca373ee527c978e816115de6.json | 8 +- ...f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json | 4 +- ...b8cf06394a302d288e91f5eedde65db6630021f737.json | 4 +- Cargo.lock | 50 ++++++- Cargo.toml | 4 +- migrations/20231009123228_devices.sql | 4 +- src/db.rs | 6 +- src/error.rs | 22 +++ src/main.rs | 54 ++++---- src/routes.rs | 3 + src/routes/device.rs | 19 ++- src/routes/mod.rs | 3 - src/routes/start.rs | 78 ++++++----- src/routes/status.rs | 79 ++++++++++- src/services.rs | 1 + src/services/mod.rs | 1 - src/services/ping.rs | 154 +++++++++------------ 17 files changed, 317 insertions(+), 177 deletions(-) create mode 100644 src/routes.rs delete mode 100644 src/routes/mod.rs create mode 100644 src/services.rs delete mode 100644 src/services/mod.rs (limited to 'src/routes/device.rs') diff --git a/.sqlx/query-1dc5f44967ffdee882f4cef32262fd643b452aacca373ee527c978e816115de6.json b/.sqlx/query-1dc5f44967ffdee882f4cef32262fd643b452aacca373ee527c978e816115de6.json index 33d524d..dd85eaa 100644 --- a/.sqlx/query-1dc5f44967ffdee882f4cef32262fd643b452aacca373ee527c978e816115de6.json +++ b/.sqlx/query-1dc5f44967ffdee882f4cef32262fd643b452aacca373ee527c978e816115de6.json @@ -11,7 +11,7 @@ { "ordinal": 1, "name": "mac", - "type_info": "Varchar" + "type_info": "Macaddr" }, { "ordinal": 2, @@ -21,7 +21,7 @@ { "ordinal": 3, "name": "ip", - "type_info": "Varchar" + "type_info": "Inet" }, { "ordinal": 4, @@ -31,9 +31,9 @@ ], "parameters": { "Left": [ + "Macaddr", "Varchar", - "Varchar", - "Varchar", + "Inet", "Text" ] }, diff --git a/.sqlx/query-62c84231c7e9c85dc91d71f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json b/.sqlx/query-62c84231c7e9c85dc91d71f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json index 5ec47e3..905bb51 100644 --- a/.sqlx/query-62c84231c7e9c85dc91d71f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json +++ b/.sqlx/query-62c84231c7e9c85dc91d71f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json @@ -11,7 +11,7 @@ { "ordinal": 1, "name": "mac", - "type_info": "Varchar" + "type_info": "Macaddr" }, { "ordinal": 2, @@ -21,7 +21,7 @@ { "ordinal": 3, "name": "ip", - "type_info": "Varchar" + "type_info": "Inet" }, { "ordinal": 4, diff --git a/.sqlx/query-adead45e1a6b02d5eabd68b8cf06394a302d288e91f5eedde65db6630021f737.json b/.sqlx/query-adead45e1a6b02d5eabd68b8cf06394a302d288e91f5eedde65db6630021f737.json index bc4bdd3..d25b12e 100644 --- a/.sqlx/query-adead45e1a6b02d5eabd68b8cf06394a302d288e91f5eedde65db6630021f737.json +++ b/.sqlx/query-adead45e1a6b02d5eabd68b8cf06394a302d288e91f5eedde65db6630021f737.json @@ -6,9 +6,9 @@ "parameters": { "Left": [ "Varchar", + "Macaddr", "Varchar", - "Varchar", - "Varchar" + "Inet" ] }, "nullable": [] diff --git a/Cargo.lock b/Cargo.lock index 835335b..5d10375 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,7 +71,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" dependencies = [ - "nix", + "nix 0.27.1", "rand", ] @@ -826,6 +826,15 @@ dependencies = [ "hashbrown 0.14.3", ] +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + [[package]] name = "itertools" version = "0.12.1" @@ -912,6 +921,17 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "mac_address" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4863ee94f19ed315bf3bc00299338d857d4b5bc856af375cc97d237382ad3856" +dependencies = [ + "nix 0.23.2", + "serde", + "winapi", +] + [[package]] name = "matchers" version = "0.1.0" @@ -943,6 +963,15 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -975,6 +1004,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nix" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nix" version = "0.27.1" @@ -1709,7 +1751,9 @@ dependencies = [ "hashlink", "hex", "indexmap", + "ipnetwork", "log", + "mac_address", "memchr", "once_cell", "paste", @@ -1829,8 +1873,10 @@ dependencies = [ "hkdf", "hmac", "home", + "ipnetwork", "itoa", "log", + "mac_address", "md-5", "memchr", "once_cell", @@ -2360,6 +2406,8 @@ dependencies = [ "color-eyre", "config", "dashmap", + "ipnetwork", + "mac_address", "serde", "serde_json", "sqlx", diff --git a/Cargo.toml b/Cargo.toml index f4633c9..c320da1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,12 @@ time = { version = "0.3", features = ["macros"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" config = "0.14" -sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"]} +sqlx = { version = "0.7", features = ["postgres", "runtime-tokio", "ipnetwork", "mac_address"]} surge-ping = "0.8" axum-macros = "0.4" uuid = { version = "1.6", features = ["v4", "fast-rng"] } dashmap = "5.5" color-eyre = "0.6" thiserror = "1.0" +ipnetwork = "0.20.0" +mac_address = { version = "1.1.5", features = ["serde"] } diff --git a/migrations/20231009123228_devices.sql b/migrations/20231009123228_devices.sql index d36946c..6983ada 100644 --- a/migrations/20231009123228_devices.sql +++ b/migrations/20231009123228_devices.sql @@ -2,8 +2,8 @@ CREATE TABLE IF NOT EXISTS "devices" ( "id" VARCHAR(255) PRIMARY KEY NOT NULL, - "mac" VARCHAR(17) NOT NULL, + "mac" MACADDR NOT NULL, "broadcast_addr" VARCHAR(39) NOT NULL, - "ip" VARCHAR(39) NOT NULL, + "ip" INET NOT NULL, "times" BIGINT[] ) diff --git a/src/db.rs b/src/db.rs index 489a000..47e907d 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,13 +1,13 @@ use serde::Serialize; -use sqlx::{PgPool, postgres::PgPoolOptions}; +use sqlx::{PgPool, postgres::PgPoolOptions, types::{ipnetwork::IpNetwork, mac_address::MacAddress}}; use tracing::{debug, info}; #[derive(Serialize, Debug)] pub struct Device { pub id: String, - pub mac: String, + pub mac: MacAddress, pub broadcast_addr: String, - pub ip: String, + pub ip: IpNetwork, pub times: Option> } diff --git a/src/error.rs b/src/error.rs index 63b214e..66a61f4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,8 @@ use axum::http::header::ToStrError; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use axum::Json; +use ::ipnetwork::IpNetworkError; +use mac_address::MacParseError; use serde_json::json; use std::io; use tracing::error; @@ -29,6 +31,18 @@ pub enum Error { source: ToStrError, }, + #[error("string parse: {source}")] + IpParse { + #[from] + source: IpNetworkError, + }, + + #[error("mac parse: {source}")] + MacParse { + #[from] + source: MacParseError, + }, + #[error("io: {source}")] Io { #[from] @@ -57,6 +71,14 @@ impl IntoResponse for Error { error!("{source}"); (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") } + Self::MacParse { source } => { + error!("{source}"); + (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") + } + Self::IpParse { source } => { + error!("{source}"); + (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") + } }; let body = Json(json!({ "error": error_message, diff --git a/src/main.rs b/src/main.rs index 4ef129b..7d8c1da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,42 +1,44 @@ -use std::env; -use std::sync::Arc; -use axum::{Router, routing::post}; -use axum::routing::{get, put}; -use dashmap::DashMap; -use sqlx::PgPool; -use time::util::local_offset; -use tokio::sync::broadcast::{channel, Sender}; -use tracing::{info, level_filters::LevelFilter}; -use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*}; use crate::config::Config; use crate::db::init_db_pool; use crate::routes::device; use crate::routes::start::start; use crate::routes::status::status; -use crate::services::ping::{BroadcastCommands, StatusMap}; +use crate::services::ping::StatusMap; +use axum::routing::{get, put}; +use axum::{routing::post, Router}; +use dashmap::DashMap; +use services::ping::BroadcastCommand; +use sqlx::PgPool; +use tracing_subscriber::fmt::time::UtcTime; +use std::env; +use std::sync::Arc; +use tokio::sync::broadcast::{channel, Sender}; +use tracing::{info, level_filters::LevelFilter}; +use tracing_subscriber::{ + fmt, + prelude::*, + EnvFilter, +}; mod auth; mod config; -mod routes; -mod wol; mod db; mod error; +mod routes; mod services; +mod wol; #[tokio::main] async fn main() -> color_eyre::eyre::Result<()> { - color_eyre::install()?; + - unsafe { local_offset::set_soundness(local_offset::Soundness::Unsound); } let time_format = time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); - let loc = LocalTime::new(time_format); + let loc = UtcTime::new(time_format); tracing_subscriber::registry() - .with(fmt::layer() - .with_timer(loc) - ) + .with(fmt::layer().with_timer(loc)) .with( EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) @@ -56,8 +58,13 @@ async fn main() -> color_eyre::eyre::Result<()> { let (tx, _) = channel(32); let ping_map: StatusMap = DashMap::new(); - - let shared_state = Arc::new(AppState { db, config: config.clone(), ping_send: tx, ping_map }); + + let shared_state = Arc::new(AppState { + db, + config: config.clone(), + ping_send: tx, + ping_map, + }); let app = Router::new() .route("/start", post(start)) @@ -69,8 +76,7 @@ async fn main() -> color_eyre::eyre::Result<()> { let addr = config.serveraddr; info!("start server on {}", addr); - let listener = tokio::net::TcpListener::bind(addr) - .await?; + let listener = tokio::net::TcpListener::bind(addr).await?; axum::serve(listener, app).await?; Ok(()) @@ -79,6 +85,6 @@ async fn main() -> color_eyre::eyre::Result<()> { pub struct AppState { db: PgPool, config: Config, - ping_send: Sender, + ping_send: Sender, ping_map: StatusMap, } diff --git a/src/routes.rs b/src/routes.rs new file mode 100644 index 0000000..d5ab0d6 --- /dev/null +++ b/src/routes.rs @@ -0,0 +1,3 @@ +pub mod start; +pub mod device; +pub mod status; \ No newline at end of file diff --git a/src/routes/device.rs b/src/routes/device.rs index 5ca574a..2f0093d 100644 --- a/src/routes/device.rs +++ b/src/routes/device.rs @@ -4,9 +4,11 @@ use crate::error::Error; use axum::extract::State; use axum::http::HeaderMap; use axum::Json; +use mac_address::MacAddress; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use std::sync::Arc; +use sqlx::types::ipnetwork::IpNetwork; +use std::{sync::Arc, str::FromStr}; use tracing::{debug, info}; pub async fn get( @@ -14,7 +16,7 @@ pub async fn get( headers: HeaderMap, Json(payload): Json, ) -> Result, Error> { - info!("add device {}", payload.id); + info!("get device {}", payload.id); let secret = headers.get("authorization"); let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); if authorized { @@ -52,18 +54,21 @@ pub async fn put( "add device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip ); + let secret = headers.get("authorization"); let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); if authorized { + let ip = IpNetwork::from_str(&payload.ip)?; + let mac = MacAddress::from_str(&payload.mac)?; sqlx::query!( r#" INSERT INTO devices (id, mac, broadcast_addr, ip) VALUES ($1, $2, $3, $4); "#, payload.id, - payload.mac, + mac, payload.broadcast_addr, - payload.ip + ip ) .execute(&state.db) .await?; @@ -99,6 +104,8 @@ pub async fn post( let secret = headers.get("authorization"); let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); if authorized { + let ip = IpNetwork::from_str(&payload.ip)?; + let mac = MacAddress::from_str(&payload.mac)?; let device = sqlx::query_as!( Device, r#" @@ -106,9 +113,9 @@ pub async fn post( SET mac = $1, broadcast_addr = $2, ip = $3 WHERE id = $4 RETURNING id, mac, broadcast_addr, ip, times; "#, - payload.mac, + mac, payload.broadcast_addr, - payload.ip, + ip, payload.id ) .fetch_one(&state.db) diff --git a/src/routes/mod.rs b/src/routes/mod.rs deleted file mode 100644 index d5ab0d6..0000000 --- a/src/routes/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod start; -pub mod device; -pub mod status; \ No newline at end of file diff --git a/src/routes/start.rs b/src/routes/start.rs index ec4f98f..4888325 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs @@ -12,7 +12,6 @@ use std::sync::Arc; use tracing::{debug, info}; use uuid::Uuid; -#[axum_macros::debug_handler] pub async fn start( State(state): State>, headers: HeaderMap, @@ -41,45 +40,11 @@ pub async fn start( let _ = send_packet( bind_addr, &device.broadcast_addr, - &create_buffer(&device.mac)?, + &create_buffer(&device.mac.to_string())?, )?; let dev_id = device.id.clone(); let uuid = if payload.ping.is_some_and(|ping| ping) { - let mut uuid: Option = None; - for (key, value) in state.ping_map.clone() { - if value.ip == device.ip { - debug!("service already exists"); - uuid = Some(key); - break; - } - } - let uuid_gen = match uuid { - Some(u) => u, - None => Uuid::new_v4().to_string(), - }; - let uuid_genc = uuid_gen.clone(); - - tokio::spawn(async move { - debug!("init ping service"); - state.ping_map.insert( - uuid_gen.clone(), - PingValue { - ip: device.ip.clone(), - online: false, - }, - ); - - crate::services::ping::spawn( - state.ping_send.clone(), - &state.config, - device, - uuid_gen.clone(), - &state.ping_map, - &state.db, - ) - .await; - }); - Some(uuid_genc) + Some(setup_ping(state, device)) } else { None }; @@ -93,6 +58,45 @@ pub async fn start( } } +fn setup_ping(state: Arc, device: Device) -> String { + let mut uuid: Option = None; + for (key, value) in state.ping_map.clone() { + if value.ip == device.ip { + debug!("service already exists"); + uuid = Some(key); + break; + } + } + let uuid_gen = match uuid { + Some(u) => u, + None => Uuid::new_v4().to_string(), + }; + let uuid_ret = uuid_gen.clone(); + + debug!("init ping service"); + state.ping_map.insert( + uuid_gen.clone(), + PingValue { + ip: device.ip, + online: false, + }, + ); + + tokio::spawn(async move { + crate::services::ping::spawn( + state.ping_send.clone(), + &state.config, + device, + uuid_gen, + &state.ping_map, + &state.db, + ) + .await; + }); + + uuid_ret +} + #[derive(Deserialize)] pub struct Payload { id: String, diff --git a/src/routes/status.rs b/src/routes/status.rs index 31ef996..0e25f7d 100644 --- a/src/routes/status.rs +++ b/src/routes/status.rs @@ -1,10 +1,79 @@ -use std::sync::Arc; +use crate::services::ping::BroadcastCommand; +use crate::AppState; +use axum::extract::ws::{Message, WebSocket}; use axum::extract::{State, WebSocketUpgrade}; use axum::response::Response; -use crate::AppState; -use crate::services::ping::status_websocket; +use sqlx::PgPool; +use std::sync::Arc; +use tracing::{debug, trace}; -#[axum_macros::debug_handler] pub async fn status(State(state): State>, ws: WebSocketUpgrade) -> Response { - ws.on_upgrade(move |socket| status_websocket(socket, state)) + ws.on_upgrade(move |socket| websocket(socket, state)) +} + +pub async fn websocket(mut socket: WebSocket, state: Arc) { + trace!("wait for ws message (uuid)"); + let msg = socket.recv().await; + let uuid = msg.unwrap().unwrap().into_text().unwrap(); + + trace!("Search for uuid: {}", uuid); + + let eta = get_eta(&state.db).await; + let _ = socket + .send(Message::Text(format!("eta_{eta}_{uuid}"))) + .await; + + let device_exists = state.ping_map.contains_key(&uuid); + if device_exists { + let _ = socket + .send(receive_ping_broadcast(state.clone(), uuid).await) + .await; + } else { + debug!("didn't find any device"); + let _ = socket.send(Message::Text(format!("notfound_{uuid}"))).await; + }; + + let _ = socket.close().await; +} + +async fn receive_ping_broadcast(state: Arc, uuid: String) -> Message { + let pm = state.ping_map.clone().into_read_only(); + let device = pm.get(&uuid).expect("fatal error"); + debug!("got device: {} (online: {})", device.ip, device.online); + if device.online { + debug!("already started"); + Message::Text(BroadcastCommand::success(uuid).to_string()) + } else { + loop { + trace!("wait for tx message"); + let message = state + .ping_send + .subscribe() + .recv() + .await + .expect("fatal error"); + trace!("got message {:?}", message); + + if message.uuid != uuid { + continue; + } + trace!("message == uuid success"); + return Message::Text(message.to_string()); + } + } +} + +async fn get_eta(db: &PgPool) -> i64 { + let query = sqlx::query!(r#"SELECT times FROM devices;"#) + .fetch_one(db) + .await + .unwrap(); + + let times = if let Some(times) = query.times { + times + } else { + vec![0] + }; + + times.iter().sum::() / i64::try_from(times.len()).unwrap() } diff --git a/src/services.rs b/src/services.rs new file mode 100644 index 0000000..a766209 --- /dev/null +++ b/src/services.rs @@ -0,0 +1 @@ +pub mod ping; diff --git a/src/services/mod.rs b/src/services/mod.rs deleted file mode 100644 index a766209..0000000 --- a/src/services/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod ping; diff --git a/src/services/ping.rs b/src/services/ping.rs index 9b164c8..9191f86 100644 --- a/src/services/ping.rs +++ b/src/services/ping.rs @@ -1,59 +1,58 @@ -use std::str::FromStr; -use std::net::IpAddr; -use std::sync::Arc; - -use axum::extract::ws::WebSocket; -use axum::extract::ws::Message; +use crate::config::Config; +use crate::db::Device; use dashmap::DashMap; +use ipnetwork::IpNetwork; use sqlx::PgPool; +use std::fmt::Display; use time::{Duration, Instant}; use tokio::sync::broadcast::Sender; use tracing::{debug, error, trace}; -use crate::AppState; -use crate::config::Config; -use crate::db::Device; pub type StatusMap = DashMap; #[derive(Debug, Clone)] pub struct Value { - pub ip: String, - pub online: bool + pub ip: IpNetwork, + pub online: bool, } -pub async fn spawn(tx: Sender, config: &Config, device: Device, uuid: String, ping_map: &StatusMap, db: &PgPool) { +pub async fn spawn( + tx: Sender, + config: &Config, + device: Device, + uuid: String, + ping_map: &StatusMap, + db: &PgPool, +) { let timer = Instant::now(); let payload = [0; 8]; - let ping_ip = IpAddr::from_str(&device.ip).expect("bad ip"); - - let mut msg: Option = None; + let mut msg: Option = None; while msg.is_none() { - let ping = surge_ping::ping( - ping_ip, - &payload - ).await; + let ping = surge_ping::ping(device.ip.ip(), &payload).await; if let Err(ping) = ping { let ping_timeout = matches!(ping, surge_ping::SurgeError::Timeout { .. }); if !ping_timeout { error!("{}", ping.to_string()); - msg = Some(BroadcastCommands::Error(uuid.clone())); + msg = Some(BroadcastCommand::error(uuid.clone())); } if timer.elapsed() >= Duration::minutes(config.pingtimeout) { - msg = Some(BroadcastCommands::Timeout(uuid.clone())); + msg = Some(BroadcastCommand::timeout(uuid.clone())); } } else { - let (_, duration) = ping.map_err(|err| error!("{}", err.to_string())).expect("fatal error"); + let (_, duration) = ping + .map_err(|err| error!("{}", err.to_string())) + .expect("fatal error"); debug!("ping took {:?}", duration); - msg = Some(BroadcastCommands::Success(uuid.clone())); + msg = Some(BroadcastCommand::success(uuid.clone())); }; } let msg = msg.expect("fatal error"); let _ = tx.send(msg.clone()); - if let BroadcastCommands::Success(..) = msg { + if let BroadcastCommands::Success = msg.command { sqlx::query!( r#" UPDATE devices @@ -62,8 +61,17 @@ pub async fn spawn(tx: Sender, config: &Config, device: Devic "#, timer.elapsed().whole_seconds(), device.id - ).execute(db).await.unwrap(); - ping_map.insert(uuid.clone(), Value { ip: device.ip.clone(), online: true }); + ) + .execute(db) + .await + .unwrap(); + ping_map.insert( + uuid.clone(), + Value { + ip: device.ip, + online: true, + }, + ); tokio::time::sleep(tokio::time::Duration::from_secs(60)).await; } trace!("remove {} from ping_map", uuid); @@ -72,74 +80,48 @@ pub async fn spawn(tx: Sender, config: &Config, device: Devic #[derive(Clone, Debug, PartialEq)] pub enum BroadcastCommands { - Success(String), - Timeout(String), - Error(String), + Success, + Timeout, + Error, } -pub async fn status_websocket(mut socket: WebSocket, state: Arc) { - trace!("wait for ws message (uuid)"); - let msg = socket.recv().await; - let uuid = msg.unwrap().unwrap().into_text().unwrap(); - - trace!("Search for uuid: {}", uuid); - - let eta = get_eta(&state.db).await; - let _ = socket.send(Message::Text(format!("eta_{eta}_{uuid}"))).await; +#[derive(Clone, Debug, PartialEq)] +pub struct BroadcastCommand { + pub uuid: String, + pub command: BroadcastCommands, +} - let device_exists = state.ping_map.contains_key(&uuid); - if device_exists { - let _ = socket.send(process_device(state.clone(), uuid).await).await; - } else { - debug!("didn't find any device"); - let _ = socket.send(Message::Text(format!("notfound_{uuid}"))).await; - }; +impl Display for BroadcastCommand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let prefix = match self.command { + BroadcastCommands::Success => "start", + BroadcastCommands::Timeout => "timeout", + BroadcastCommands::Error => "error", + }; - let _ = socket.close().await; + f.write_str(format!("{prefix}_{}", self.uuid).as_str()) + } } -async fn get_eta(db: &PgPool) -> i64 { - let query = sqlx::query!( - r#"SELECT times FROM devices;"# - ).fetch_one(db).await.unwrap(); - - let times = match query.times { - None => { vec![0] }, - Some(t) => t, - }; - times.iter().sum::() / i64::try_from(times.len()).unwrap() +impl BroadcastCommand { + pub fn success(uuid: String) -> Self { + Self { + uuid, + command: BroadcastCommands::Success, + } + } -} + pub fn timeout(uuid: String) -> Self { + Self { + uuid, + command: BroadcastCommands::Timeout, + } + } -async fn process_device(state: Arc, uuid: String) -> Message { - let pm = state.ping_map.clone().into_read_only(); - let device = pm.get(&uuid).expect("fatal error"); - debug!("got device: {} (online: {})", device.ip, device.online); - if device.online { - debug!("already started"); - Message::Text(format!("start_{uuid}")) - } else { - loop { - trace!("wait for tx message"); - let message = state.ping_send.subscribe().recv().await.expect("fatal error"); - trace!("got message {:?}", message); - return match message { - BroadcastCommands::Success(msg_uuid) => { - if msg_uuid != uuid { continue; } - trace!("message == uuid success"); - Message::Text(format!("start_{uuid}")) - }, - BroadcastCommands::Timeout(msg_uuid) => { - if msg_uuid != uuid { continue; } - trace!("message == uuid timeout"); - Message::Text(format!("timeout_{uuid}")) - }, - BroadcastCommands::Error(msg_uuid) => { - if msg_uuid != uuid { continue; } - trace!("message == uuid error"); - Message::Text(format!("error_{uuid}")) - } - } + pub fn error(uuid: String) -> Self { + Self { + uuid, + command: BroadcastCommands::Error, } } } -- cgit v1.2.3 From 9058f191b69ecafc8fdeace227ac113412d03888 Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Sun, 25 Feb 2024 15:15:19 +0100 Subject: Closes #16. Impl auth as extractor --- ...1cbd787a2f2a30efc581ea075b7e86ae75458c9014.json | 46 +++++++++ ...86cffd2b4739d88b3061931562e07c3e8dd24e6359.json | 49 +++++++++ ...1b2849b2530be7bcdcbe14362077b3fd47cd711c89.json | 17 ++++ src/auth.rs | 30 ------ src/error.rs | 6 +- src/extractors.rs | 24 +++++ src/main.rs | 20 ++-- src/routes/device.rs | 113 ++++++++------------- src/routes/start.rs | 65 +++++------- 9 files changed, 219 insertions(+), 151 deletions(-) create mode 100644 .sqlx/query-a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014.json create mode 100644 .sqlx/query-e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359.json create mode 100644 .sqlx/query-edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89.json delete mode 100644 src/auth.rs create mode 100644 src/extractors.rs (limited to 'src/routes/device.rs') diff --git a/.sqlx/query-a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014.json b/.sqlx/query-a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014.json new file mode 100644 index 0000000..53608ed --- /dev/null +++ b/.sqlx/query-a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT id, mac, broadcast_addr, ip, times\n FROM devices\n WHERE id = $1;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "mac", + "type_info": "Macaddr" + }, + { + "ordinal": 2, + "name": "broadcast_addr", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "ip", + "type_info": "Inet" + }, + { + "ordinal": 4, + "name": "times", + "type_info": "Int8Array" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + true + ] + }, + "hash": "a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014" +} diff --git a/.sqlx/query-e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359.json b/.sqlx/query-e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359.json new file mode 100644 index 0000000..75ec121 --- /dev/null +++ b/.sqlx/query-e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359.json @@ -0,0 +1,49 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE devices\n SET mac = $1, broadcast_addr = $2, ip = $3 WHERE id = $4\n RETURNING id, mac, broadcast_addr, ip, times;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "mac", + "type_info": "Macaddr" + }, + { + "ordinal": 2, + "name": "broadcast_addr", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "ip", + "type_info": "Inet" + }, + { + "ordinal": 4, + "name": "times", + "type_info": "Int8Array" + } + ], + "parameters": { + "Left": [ + "Macaddr", + "Varchar", + "Inet", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + true + ] + }, + "hash": "e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359" +} diff --git a/.sqlx/query-edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89.json b/.sqlx/query-edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89.json new file mode 100644 index 0000000..2b17c9b --- /dev/null +++ b/.sqlx/query-edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO devices (id, mac, broadcast_addr, ip)\n VALUES ($1, $2, $3, $4);\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Macaddr", + "Varchar", + "Inet" + ] + }, + "nullable": [] + }, + "hash": "edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89" +} diff --git a/src/auth.rs b/src/auth.rs deleted file mode 100644 index 22f87e7..0000000 --- a/src/auth.rs +++ /dev/null @@ -1,30 +0,0 @@ -use axum::http::HeaderValue; -use tracing::{debug, trace}; -use crate::config::Config; -use crate::error::Error; - -pub fn auth(config: &Config, secret: Option<&HeaderValue>) -> Result { - debug!("auth request with secret {:?}", secret); - let res = if let Some(value) = secret { - trace!("auth value exists"); - let key = &config.apikey; - if value.to_str()? == key.as_str() { - debug!("successful auth"); - Response::Success - } else { - debug!("unsuccessful auth (wrong secret)"); - Response::WrongSecret - } - } else { - debug!("unsuccessful auth (no secret)"); - Response::MissingSecret - }; - Ok(res) -} - -#[derive(Debug)] -pub enum Response { - Success, - WrongSecret, - MissingSecret -} diff --git a/src/error.rs b/src/error.rs index 66a61f4..513b51b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,8 @@ +use ::ipnetwork::IpNetworkError; use axum::http::header::ToStrError; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use axum::Json; -use ::ipnetwork::IpNetworkError; use mac_address::MacParseError; use serde_json::json; use std::io; @@ -10,9 +10,6 @@ use tracing::error; #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("generic error")] - Generic, - #[error("db: {source}")] Db { #[from] @@ -54,7 +51,6 @@ impl IntoResponse for Error { fn into_response(self) -> Response { error!("{}", self.to_string()); let (status, error_message) = match self { - Self::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""), Self::Db { source } => { error!("{source}"); (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") diff --git a/src/extractors.rs b/src/extractors.rs new file mode 100644 index 0000000..4d441e9 --- /dev/null +++ b/src/extractors.rs @@ -0,0 +1,24 @@ +use axum::{ + extract::{Request, State}, + http::{HeaderMap, StatusCode}, + middleware::Next, + response::Response, +}; + +use crate::AppState; + +pub async fn auth( + State(state): State, + headers: HeaderMap, + request: Request, + next: Next, +) -> Result { + let secret = headers.get("authorization"); + match secret { + Some(token) if token == state.config.apikey.as_str() => { + let response = next.run(request).await; + Ok(response) + } + _ => Err(StatusCode::UNAUTHORIZED), + } +} diff --git a/src/main.rs b/src/main.rs index 7d8c1da..eae89f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,26 +4,23 @@ use crate::routes::device; use crate::routes::start::start; use crate::routes::status::status; use crate::services::ping::StatusMap; +use axum::middleware::from_fn_with_state; use axum::routing::{get, put}; use axum::{routing::post, Router}; use dashmap::DashMap; use services::ping::BroadcastCommand; use sqlx::PgPool; -use tracing_subscriber::fmt::time::UtcTime; use std::env; use std::sync::Arc; use tokio::sync::broadcast::{channel, Sender}; use tracing::{info, level_filters::LevelFilter}; -use tracing_subscriber::{ - fmt, - prelude::*, - EnvFilter, -}; +use tracing_subscriber::fmt::time::UtcTime; +use tracing_subscriber::{fmt, prelude::*, EnvFilter}; -mod auth; mod config; mod db; mod error; +mod extractors; mod routes; mod services; mod wol; @@ -31,7 +28,6 @@ mod wol; #[tokio::main] async fn main() -> color_eyre::eyre::Result<()> { color_eyre::install()?; - let time_format = time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); @@ -59,12 +55,12 @@ async fn main() -> color_eyre::eyre::Result<()> { let ping_map: StatusMap = DashMap::new(); - let shared_state = Arc::new(AppState { + let shared_state = AppState { db, config: config.clone(), ping_send: tx, ping_map, - }); + }; let app = Router::new() .route("/start", post(start)) @@ -72,7 +68,8 @@ async fn main() -> color_eyre::eyre::Result<()> { .route("/device", put(device::put)) .route("/device", post(device::post)) .route("/status", get(status)) - .with_state(shared_state); + .route_layer(from_fn_with_state(shared_state.clone(), extractors::auth)) + .with_state(Arc::new(shared_state)); let addr = config.serveraddr; info!("start server on {}", addr); @@ -82,6 +79,7 @@ async fn main() -> color_eyre::eyre::Result<()> { Ok(()) } +#[derive(Clone)] pub struct AppState { db: PgPool, config: Config, diff --git a/src/routes/device.rs b/src/routes/device.rs index 2f0093d..d39d98e 100644 --- a/src/routes/device.rs +++ b/src/routes/device.rs @@ -1,8 +1,6 @@ -use crate::auth::auth; use crate::db::Device; use crate::error::Error; use axum::extract::State; -use axum::http::HeaderMap; use axum::Json; use mac_address::MacAddress; use serde::{Deserialize, Serialize}; @@ -13,31 +11,24 @@ use tracing::{debug, info}; pub async fn get( State(state): State>, - headers: HeaderMap, Json(payload): Json, ) -> Result, Error> { info!("get device {}", payload.id); - let secret = headers.get("authorization"); - let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); - if authorized { - let device = sqlx::query_as!( - Device, - r#" - SELECT id, mac, broadcast_addr, ip, times - FROM devices - WHERE id = $1; - "#, - payload.id - ) - .fetch_one(&state.db) - .await?; + let device = sqlx::query_as!( + Device, + r#" + SELECT id, mac, broadcast_addr, ip, times + FROM devices + WHERE id = $1; + "#, + payload.id + ) + .fetch_one(&state.db) + .await?; - debug!("got device {:?}", device); + debug!("got device {:?}", device); - Ok(Json(json!(device))) - } else { - Err(Error::Generic) - } + Ok(Json(json!(device))) } #[derive(Deserialize)] @@ -47,7 +38,6 @@ pub struct GetDevicePayload { pub async fn put( State(state): State>, - headers: HeaderMap, Json(payload): Json, ) -> Result, Error> { info!( @@ -55,28 +45,22 @@ pub async fn put( payload.id, payload.mac, payload.broadcast_addr, payload.ip ); - let secret = headers.get("authorization"); - let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); - if authorized { - let ip = IpNetwork::from_str(&payload.ip)?; - let mac = MacAddress::from_str(&payload.mac)?; - sqlx::query!( - r#" - INSERT INTO devices (id, mac, broadcast_addr, ip) - VALUES ($1, $2, $3, $4); - "#, - payload.id, - mac, - payload.broadcast_addr, - ip - ) - .execute(&state.db) - .await?; + let ip = IpNetwork::from_str(&payload.ip)?; + let mac = MacAddress::from_str(&payload.mac)?; + sqlx::query!( + r#" + INSERT INTO devices (id, mac, broadcast_addr, ip) + VALUES ($1, $2, $3, $4); + "#, + payload.id, + mac, + payload.broadcast_addr, + ip + ) + .execute(&state.db) + .await?; - Ok(Json(json!(PutDeviceResponse { success: true }))) - } else { - Err(Error::Generic) - } + Ok(Json(json!(PutDeviceResponse { success: true }))) } #[derive(Deserialize)] @@ -94,37 +78,30 @@ pub struct PutDeviceResponse { pub async fn post( State(state): State>, - headers: HeaderMap, Json(payload): Json, ) -> Result, Error> { info!( "edit device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip ); - let secret = headers.get("authorization"); - let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); - if authorized { - let ip = IpNetwork::from_str(&payload.ip)?; - let mac = MacAddress::from_str(&payload.mac)?; - let device = sqlx::query_as!( - Device, - r#" - UPDATE devices - SET mac = $1, broadcast_addr = $2, ip = $3 WHERE id = $4 - RETURNING id, mac, broadcast_addr, ip, times; - "#, - mac, - payload.broadcast_addr, - ip, - payload.id - ) - .fetch_one(&state.db) - .await?; + let ip = IpNetwork::from_str(&payload.ip)?; + let mac = MacAddress::from_str(&payload.mac)?; + let device = sqlx::query_as!( + Device, + r#" + UPDATE devices + SET mac = $1, broadcast_addr = $2, ip = $3 WHERE id = $4 + RETURNING id, mac, broadcast_addr, ip, times; + "#, + mac, + payload.broadcast_addr, + ip, + payload.id + ) + .fetch_one(&state.db) + .await?; - Ok(Json(json!(device))) - } else { - Err(Error::Generic) - } + Ok(Json(json!(device))) } #[derive(Deserialize)] diff --git a/src/routes/start.rs b/src/routes/start.rs index 4888325..d4c0802 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs @@ -1,10 +1,8 @@ -use crate::auth::auth; use crate::db::Device; use crate::error::Error; use crate::services::ping::Value as PingValue; use crate::wol::{create_buffer, send_packet}; use axum::extract::State; -use axum::http::HeaderMap; use axum::Json; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; @@ -14,48 +12,41 @@ use uuid::Uuid; pub async fn start( State(state): State>, - headers: HeaderMap, Json(payload): Json, ) -> Result, Error> { info!("POST request"); - let secret = headers.get("authorization"); - let authorized = matches!(auth(&state.config, secret)?, crate::auth::Response::Success); - if authorized { - let device = sqlx::query_as!( - Device, - r#" - SELECT id, mac, broadcast_addr, ip, times - FROM devices - WHERE id = $1; - "#, - payload.id - ) - .fetch_one(&state.db) - .await?; + let device = sqlx::query_as!( + Device, + r#" + SELECT id, mac, broadcast_addr, ip, times + FROM devices + WHERE id = $1; + "#, + payload.id + ) + .fetch_one(&state.db) + .await?; - info!("starting {}", device.id); + info!("starting {}", device.id); - let bind_addr = "0.0.0.0:0"; + let bind_addr = "0.0.0.0:0"; - let _ = send_packet( - bind_addr, - &device.broadcast_addr, - &create_buffer(&device.mac.to_string())?, - )?; - let dev_id = device.id.clone(); - let uuid = if payload.ping.is_some_and(|ping| ping) { - Some(setup_ping(state, device)) - } else { - None - }; - Ok(Json(json!(Response { - id: dev_id, - boot: true, - uuid - }))) + let _ = send_packet( + bind_addr, + &device.broadcast_addr, + &create_buffer(&device.mac.to_string())?, + )?; + let dev_id = device.id.clone(); + let uuid = if payload.ping.is_some_and(|ping| ping) { + Some(setup_ping(state, device)) } else { - Err(Error::Generic) - } + None + }; + Ok(Json(json!(Response { + id: dev_id, + boot: true, + uuid + }))) } fn setup_ping(state: Arc, device: Device) -> String { -- cgit v1.2.3