aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFxQnLr <[email protected]>2024-02-15 17:17:30 +0100
committerFxQnLr <[email protected]>2024-02-15 17:17:30 +0100
commit3bc7cf8ed36016ca3da9438a98f4fe8b8e6f9e61 (patch)
tree48a38a52ebfc41fadab439b9a87fae3d43f78790
parentc663810817183c8f92a4279236ca84d271365088 (diff)
downloadwebol-3bc7cf8ed36016ca3da9438a98f4fe8b8e6f9e61.tar
webol-3bc7cf8ed36016ca3da9438a98f4fe8b8e6f9e61.tar.gz
webol-3bc7cf8ed36016ca3da9438a98f4fe8b8e6f9e61.zip
Closes #10 & #12. Added `thiserror` crate and changed to `IntoSocketAddr` for easier usage and error handling
-rw-r--r--Cargo.lock3
-rw-r--r--Cargo.toml3
-rw-r--r--src/auth.rs16
-rw-r--r--src/error.rs65
-rw-r--r--src/routes/device.rs12
-rw-r--r--src/routes/start.rs9
-rw-r--r--src/wol.rs18
7 files changed, 76 insertions, 50 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f95052b..835335b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2353,7 +2353,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
2353 2353
2354[[package]] 2354[[package]]
2355name = "webol" 2355name = "webol"
2356version = "0.3.1" 2356version = "0.3.2"
2357dependencies = [ 2357dependencies = [
2358 "axum", 2358 "axum",
2359 "axum-macros", 2359 "axum-macros",
@@ -2364,6 +2364,7 @@ dependencies = [
2364 "serde_json", 2364 "serde_json",
2365 "sqlx", 2365 "sqlx",
2366 "surge-ping", 2366 "surge-ping",
2367 "thiserror",
2367 "time", 2368 "time",
2368 "tokio", 2369 "tokio",
2369 "tracing", 2370 "tracing",
diff --git a/Cargo.toml b/Cargo.toml
index 0fb250a..f4633c9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "webol" 2name = "webol"
3version = "0.3.1" 3version = "0.3.2"
4edition = "2021" 4edition = "2021"
5 5
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -20,3 +20,4 @@ axum-macros = "0.4"
20uuid = { version = "1.6", features = ["v4", "fast-rng"] } 20uuid = { version = "1.6", features = ["v4", "fast-rng"] }
21dashmap = "5.5" 21dashmap = "5.5"
22color-eyre = "0.6" 22color-eyre = "0.6"
23thiserror = "1.0"
diff --git a/src/auth.rs b/src/auth.rs
index feca652..eb4d1bf 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -9,7 +9,7 @@ pub fn auth(config: &Config, secret: Option<&HeaderValue>) -> Result<bool, Error
9 if let Some(value) = secret { 9 if let Some(value) = secret {
10 trace!("value exists"); 10 trace!("value exists");
11 let key = &config.apikey; 11 let key = &config.apikey;
12 if value.to_str().map_err(Error::HeaderToStr)? == key.as_str() { 12 if value.to_str()? == key.as_str() {
13 debug!("successful auth"); 13 debug!("successful auth");
14 Ok(true) 14 Ok(true)
15 } else { 15 } else {
@@ -22,11 +22,17 @@ pub fn auth(config: &Config, secret: Option<&HeaderValue>) -> Result<bool, Error
22 } 22 }
23} 23}
24 24
25#[derive(Debug)] 25#[derive(Debug, thiserror::Error)]
26pub enum Error { 26pub enum Error {
27 #[error("wrong secret")]
27 WrongSecret, 28 WrongSecret,
29 #[error("missing secret")]
28 MissingSecret, 30 MissingSecret,
29 HeaderToStr(ToStrError) 31 #[error("parse error: {source}")]
32 HeaderToStr {
33 #[from]
34 source: ToStrError
35 }
30} 36}
31 37
32impl Error { 38impl Error {
@@ -34,8 +40,8 @@ impl Error {
34 match self { 40 match self {
35 Self::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"), 41 Self::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"),
36 Self::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"), 42 Self::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"),
37 Self::HeaderToStr(err) => { 43 Self::HeaderToStr { source } => {
38 error!("server error: {}", err.to_string()); 44 error!("auth: {}", source);
39 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 45 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
40 }, 46 },
41 } 47 }
diff --git a/src/error.rs b/src/error.rs
index 56d6c52..4f1bedd 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,44 +1,59 @@
1use std::io; 1use crate::auth::Error as AuthError;
2use axum::http::StatusCode; 2use axum::http::StatusCode;
3use axum::Json;
4use axum::response::{IntoResponse, Response}; 3use axum::response::{IntoResponse, Response};
4use axum::Json;
5use serde_json::json; 5use serde_json::json;
6use std::io;
6use tracing::error; 7use tracing::error;
7use crate::auth::Error as AuthError;
8 8
9#[derive(Debug)] 9#[derive(Debug, thiserror::Error)]
10pub enum Error { 10pub enum Error {
11 #[error("generic error")]
11 Generic, 12 Generic,
12 Auth(AuthError), 13
13 DB(sqlx::Error), 14 #[error("auth: {source}")]
14 IpParse(<std::net::IpAddr as std::str::FromStr>::Err), 15 Auth {
15 BufferParse(std::num::ParseIntError), 16 #[from]
16 Broadcast(io::Error), 17 source: AuthError,
18 },
19
20 #[error("db: {source}")]
21 Db {
22 #[from]
23 source: sqlx::Error,
24 },
25
26 #[error("buffer parse: {source}")]
27 ParseInt {
28 #[from]
29 source: std::num::ParseIntError,
30 },
31
32 #[error("io: {source}")]
33 Io {
34 #[from]
35 source: io::Error,
36 },
17} 37}
18 38
19impl IntoResponse for Error { 39impl IntoResponse for Error {
20 fn into_response(self) -> Response { 40 fn into_response(self) -> Response {
41 error!("{}", self.to_string());
21 let (status, error_message) = match self { 42 let (status, error_message) = match self {
22 Self::Auth(err) => { 43 Self::Auth { source } => source.get(),
23 err.get()
24 },
25 Self::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""), 44 Self::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""),
26 Self::IpParse(err) => { 45 Self::Db { source } => {
27 error!("server error: {}", err.to_string()); 46 error!("{source}");
28 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
29 },
30 Self::DB(err) => {
31 error!("server error: {}", err.to_string());
32 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 47 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
33 }, 48 }
34 Self::Broadcast(err) => { 49 Self::Io { source } => {
35 error!("server error: {}", err.to_string()); 50 error!("{source}");
36 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 51 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
37 }, 52 }
38 Self::BufferParse(err) => { 53 Self::ParseInt { source } => {
39 error!("server error: {}", err.to_string()); 54 error!("{source}");
40 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 55 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
41 }, 56 }
42 }; 57 };
43 let body = Json(json!({ 58 let body = Json(json!({
44 "error": error_message, 59 "error": error_message,
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;
12pub async fn get(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<GetDevicePayload>) -> Result<Json<Value>, Error> { 12pub async fn get(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<GetDevicePayload>) -> Result<Json<Value>, Error> {
13 info!("add device {}", payload.id); 13 info!("add device {}", payload.id);
14 let secret = headers.get("authorization"); 14 let secret = headers.get("authorization");
15 if auth(&state.config, secret).map_err(Error::Auth)? { 15 if auth(&state.config, secret)? {
16 let device = sqlx::query_as!( 16 let device = sqlx::query_as!(
17 Device, 17 Device,
18 r#" 18 r#"
@@ -21,7 +21,7 @@ pub async fn get(State(state): State<Arc<crate::AppState>>, headers: HeaderMap,
21 WHERE id = $1; 21 WHERE id = $1;
22 "#, 22 "#,
23 payload.id 23 payload.id
24 ).fetch_one(&state.db).await.map_err(Error::DB)?; 24 ).fetch_one(&state.db).await?;
25 25
26 debug!("got device {:?}", device); 26 debug!("got device {:?}", device);
27 27
@@ -39,7 +39,7 @@ pub struct GetDevicePayload {
39pub async fn put(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PutDevicePayload>) -> Result<Json<Value>, Error> { 39pub async fn put(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PutDevicePayload>) -> Result<Json<Value>, Error> {
40 info!("add device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); 40 info!("add device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip);
41 let secret = headers.get("authorization"); 41 let secret = headers.get("authorization");
42 if auth(&state.config, secret).map_err(Error::Auth)? { 42 if auth(&state.config, secret)? {
43 sqlx::query!( 43 sqlx::query!(
44 r#" 44 r#"
45 INSERT INTO devices (id, mac, broadcast_addr, ip) 45 INSERT INTO devices (id, mac, broadcast_addr, ip)
@@ -49,7 +49,7 @@ pub async fn put(State(state): State<Arc<crate::AppState>>, headers: HeaderMap,
49 payload.mac, 49 payload.mac,
50 payload.broadcast_addr, 50 payload.broadcast_addr,
51 payload.ip 51 payload.ip
52 ).execute(&state.db).await.map_err(Error::DB)?; 52 ).execute(&state.db).await?;
53 53
54 Ok(Json(json!(PutDeviceResponse { success: true }))) 54 Ok(Json(json!(PutDeviceResponse { success: true })))
55 } else { 55 } else {
@@ -73,7 +73,7 @@ pub struct PutDeviceResponse {
73pub async fn post(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PostDevicePayload>) -> Result<Json<Value>, Error> { 73pub async fn post(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PostDevicePayload>) -> Result<Json<Value>, Error> {
74 info!("edit device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); 74 info!("edit device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip);
75 let secret = headers.get("authorization"); 75 let secret = headers.get("authorization");
76 if auth(&state.config, secret).map_err(Error::Auth)? { 76 if auth(&state.config, secret)? {
77 let device = sqlx::query_as!( 77 let device = sqlx::query_as!(
78 Device, 78 Device,
79 r#" 79 r#"
@@ -85,7 +85,7 @@ pub async fn post(State(state): State<Arc<crate::AppState>>, headers: HeaderMap,
85 payload.broadcast_addr, 85 payload.broadcast_addr,
86 payload.ip, 86 payload.ip,
87 payload.id 87 payload.id
88 ).fetch_one(&state.db).await.map_err(Error::DB)?; 88 ).fetch_one(&state.db).await?;
89 89
90 Ok(Json(json!(device))) 90 Ok(Json(json!(device)))
91 } else { 91 } else {
diff --git a/src/routes/start.rs b/src/routes/start.rs
index ce95bf3..66b7cb4 100644
--- a/src/routes/start.rs
+++ b/src/routes/start.rs
@@ -20,7 +20,7 @@ pub async fn start(
20) -> Result<Json<Value>, Error> { 20) -> Result<Json<Value>, Error> {
21 info!("POST request"); 21 info!("POST request");
22 let secret = headers.get("authorization"); 22 let secret = headers.get("authorization");
23 let authorized = auth(&state.config, secret).map_err(Error::Auth)?; 23 let authorized = auth(&state.config, secret)?;
24 if authorized { 24 if authorized {
25 let device = sqlx::query_as!( 25 let device = sqlx::query_as!(
26 Device, 26 Device,
@@ -32,16 +32,15 @@ pub async fn start(
32 payload.id 32 payload.id
33 ) 33 )
34 .fetch_one(&state.db) 34 .fetch_one(&state.db)
35 .await 35 .await?;
36 .map_err(Error::DB)?;
37 36
38 info!("starting {}", device.id); 37 info!("starting {}", device.id);
39 38
40 let bind_addr = "0.0.0.0:0"; 39 let bind_addr = "0.0.0.0:0";
41 40
42 let _ = send_packet( 41 let _ = send_packet(
43 &bind_addr.parse().map_err(Error::IpParse)?, 42 bind_addr,
44 &device.broadcast_addr.parse().map_err(Error::IpParse)?, 43 &device.broadcast_addr,
45 &create_buffer(&device.mac)?, 44 &create_buffer(&device.mac)?,
46 )?; 45 )?;
47 let dev_id = device.id.clone(); 46 let dev_id = device.id.clone();
diff --git a/src/wol.rs b/src/wol.rs
index 83c0ee6..31cf350 100644
--- a/src/wol.rs
+++ b/src/wol.rs
@@ -1,4 +1,4 @@
1use std::net::{SocketAddr, UdpSocket}; 1use std::net::{ToSocketAddrs, UdpSocket};
2 2
3use crate::error::Error; 3use crate::error::Error;
4 4
@@ -11,8 +11,8 @@ pub fn create_buffer(mac_addr: &str) -> Result<Vec<u8>, Error> {
11 let mut mac = Vec::new(); 11 let mut mac = Vec::new();
12 let sp = mac_addr.split(':'); 12 let sp = mac_addr.split(':');
13 for f in sp { 13 for f in sp {
14 mac.push(u8::from_str_radix(f, 16).map_err(Error::BufferParse)?); 14 mac.push(u8::from_str_radix(f, 16)?);
15 }; 15 }
16 let mut buf = vec![255; 6]; 16 let mut buf = vec![255; 6];
17 for _ in 0..16 { 17 for _ in 0..16 {
18 for i in &mac { 18 for i in &mac {
@@ -23,8 +23,12 @@ pub fn create_buffer(mac_addr: &str) -> Result<Vec<u8>, Error> {
23} 23}
24 24
25/// Sends a buffer on UDP broadcast 25/// Sends a buffer on UDP broadcast
26pub fn send_packet(bind_addr: &SocketAddr, broadcast_addr: &SocketAddr, buffer: &[u8]) -> Result<usize, Error> { 26pub fn send_packet<A: ToSocketAddrs>(
27 let socket = UdpSocket::bind(bind_addr).map_err(Error::Broadcast)?; 27 bind_addr: A,
28 socket.set_broadcast(true).map_err(Error::Broadcast)?; 28 broadcast_addr: A,
29 socket.send_to(buffer, broadcast_addr).map_err(Error::Broadcast) 29 buffer: &[u8],
30) -> Result<usize, Error> {
31 let socket = UdpSocket::bind(bind_addr)?;
32 socket.set_broadcast(true)?;
33 Ok(socket.send_to(buffer, broadcast_addr)?)
30} 34}