summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock117
-rw-r--r--Cargo.toml3
-rw-r--r--src/auth.rs21
-rw-r--r--src/error.rs37
-rw-r--r--src/main.rs19
-rw-r--r--src/routes/device.rs8
-rw-r--r--src/routes/start.rs21
-rw-r--r--src/services/mod.rs1
-rw-r--r--src/services/ping.rs53
-rw-r--r--src/wol.rs17
10 files changed, 260 insertions, 37 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 09f5744..bf813bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -89,6 +89,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
89dependencies = [ 89dependencies = [
90 "async-trait", 90 "async-trait",
91 "axum-core", 91 "axum-core",
92 "base64 0.21.4",
92 "bitflags 1.3.2", 93 "bitflags 1.3.2",
93 "bytes", 94 "bytes",
94 "futures-util", 95 "futures-util",
@@ -107,8 +108,10 @@ dependencies = [
107 "serde_json", 108 "serde_json",
108 "serde_path_to_error", 109 "serde_path_to_error",
109 "serde_urlencoded", 110 "serde_urlencoded",
111 "sha1",
110 "sync_wrapper", 112 "sync_wrapper",
111 "tokio", 113 "tokio",
114 "tokio-tungstenite",
112 "tower", 115 "tower",
113 "tower-layer", 116 "tower-layer",
114 "tower-service", 117 "tower-service",
@@ -294,6 +297,12 @@ dependencies = [
294] 297]
295 298
296[[package]] 299[[package]]
300name = "data-encoding"
301version = "2.4.0"
302source = "registry+https://github.com/rust-lang/crates.io-index"
303checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
304
305[[package]]
297name = "der" 306name = "der"
298version = "0.7.8" 307version = "0.7.8"
299source = "registry+https://github.com/rust-lang/crates.io-index" 308source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -517,6 +526,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
517checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" 526checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
518 527
519[[package]] 528[[package]]
529name = "glob"
530version = "0.3.1"
531source = "registry+https://github.com/rust-lang/crates.io-index"
532checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
533
534[[package]]
520name = "hashbrown" 535name = "hashbrown"
521version = "0.12.3" 536version = "0.12.3"
522source = "registry+https://github.com/rust-lang/crates.io-index" 537source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -843,6 +858,12 @@ dependencies = [
843] 858]
844 859
845[[package]] 860[[package]]
861name = "no-std-net"
862version = "0.6.0"
863source = "registry+https://github.com/rust-lang/crates.io-index"
864checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
865
866[[package]]
846name = "nom" 867name = "nom"
847version = "7.1.3" 868version = "7.1.3"
848source = "registry+https://github.com/rust-lang/crates.io-index" 869source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1115,6 +1136,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1115checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" 1136checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
1116 1137
1117[[package]] 1138[[package]]
1139name = "pnet_base"
1140version = "0.33.0"
1141source = "registry+https://github.com/rust-lang/crates.io-index"
1142checksum = "872e46346144ebf35219ccaa64b1dffacd9c6f188cd7d012bd6977a2a838f42e"
1143dependencies = [
1144 "no-std-net",
1145]
1146
1147[[package]]
1148name = "pnet_macros"
1149version = "0.33.0"
1150source = "registry+https://github.com/rust-lang/crates.io-index"
1151checksum = "2a780e80005c2e463ec25a6e9f928630049a10b43945fea83207207d4a7606f4"
1152dependencies = [
1153 "proc-macro2",
1154 "quote",
1155 "regex",
1156 "syn 1.0.109",
1157]
1158
1159[[package]]
1160name = "pnet_macros_support"
1161version = "0.33.0"
1162source = "registry+https://github.com/rust-lang/crates.io-index"
1163checksum = "e6d932134f32efd7834eb8b16d42418dac87086347d1bc7d142370ef078582bc"
1164dependencies = [
1165 "pnet_base",
1166]
1167
1168[[package]]
1169name = "pnet_packet"
1170version = "0.33.0"
1171source = "registry+https://github.com/rust-lang/crates.io-index"
1172checksum = "8bde678bbd85cb1c2d99dc9fc596e57f03aa725f84f3168b0eaf33eeccb41706"
1173dependencies = [
1174 "glob",
1175 "pnet_base",
1176 "pnet_macros",
1177 "pnet_macros_support",
1178]
1179
1180[[package]]
1118name = "powerfmt" 1181name = "powerfmt"
1119version = "0.2.0" 1182version = "0.2.0"
1120source = "registry+https://github.com/rust-lang/crates.io-index" 1183source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1694,6 +1757,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1694checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 1757checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
1695 1758
1696[[package]] 1759[[package]]
1760name = "surge-ping"
1761version = "0.8.0"
1762source = "registry+https://github.com/rust-lang/crates.io-index"
1763checksum = "af341b2be485d647b5dc4cfb2da99efac35b5c95748a08fb7233480fedc5ead3"
1764dependencies = [
1765 "hex",
1766 "parking_lot",
1767 "pnet_packet",
1768 "rand",
1769 "socket2 0.5.5",
1770 "thiserror",
1771 "tokio",
1772 "tracing",
1773]
1774
1775[[package]]
1697name = "syn" 1776name = "syn"
1698version = "1.0.109" 1777version = "1.0.109"
1699source = "registry+https://github.com/rust-lang/crates.io-index" 1778source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1850,6 +1929,18 @@ dependencies = [
1850] 1929]
1851 1930
1852[[package]] 1931[[package]]
1932name = "tokio-tungstenite"
1933version = "0.20.1"
1934source = "registry+https://github.com/rust-lang/crates.io-index"
1935checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
1936dependencies = [
1937 "futures-util",
1938 "log",
1939 "tokio",
1940 "tungstenite",
1941]
1942
1943[[package]]
1853name = "toml" 1944name = "toml"
1854version = "0.5.11" 1945version = "0.5.11"
1855source = "registry+https://github.com/rust-lang/crates.io-index" 1946source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1956,6 +2047,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1956checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" 2047checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
1957 2048
1958[[package]] 2049[[package]]
2050name = "tungstenite"
2051version = "0.20.1"
2052source = "registry+https://github.com/rust-lang/crates.io-index"
2053checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
2054dependencies = [
2055 "byteorder",
2056 "bytes",
2057 "data-encoding",
2058 "http",
2059 "httparse",
2060 "log",
2061 "rand",
2062 "sha1",
2063 "thiserror",
2064 "url",
2065 "utf-8",
2066]
2067
2068[[package]]
1959name = "typenum" 2069name = "typenum"
1960version = "1.17.0" 2070version = "1.17.0"
1961source = "registry+https://github.com/rust-lang/crates.io-index" 2071source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2012,6 +2122,12 @@ dependencies = [
2012] 2122]
2013 2123
2014[[package]] 2124[[package]]
2125name = "utf-8"
2126version = "0.7.6"
2127source = "registry+https://github.com/rust-lang/crates.io-index"
2128checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
2129
2130[[package]]
2015name = "valuable" 2131name = "valuable"
2016version = "0.1.0" 2132version = "0.1.0"
2017source = "registry+https://github.com/rust-lang/crates.io-index" 2133source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2054,6 +2170,7 @@ dependencies = [
2054 "serde", 2170 "serde",
2055 "serde_json", 2171 "serde_json",
2056 "sqlx", 2172 "sqlx",
2173 "surge-ping",
2057 "time", 2174 "time",
2058 "tokio", 2175 "tokio",
2059 "tracing", 2176 "tracing",
diff --git a/Cargo.toml b/Cargo.toml
index e772810..9bdc4da 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
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
7 7
8[dependencies] 8[dependencies]
9axum = { version = "0.6.20", features = ["headers"] } 9axum = { version = "0.6.20", features = ["headers", "ws"] }
10tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"] } 10tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"] }
11tracing = "0.1.37" 11tracing = "0.1.37"
12tracing-subscriber = { version = "0.3.17", features = ["env-filter", "local-time", "time"] } 12tracing-subscriber = { version = "0.3.17", features = ["env-filter", "local-time", "time"] }
@@ -16,3 +16,4 @@ serde_json = "1.0.107"
16config = "0.13.3" 16config = "0.13.3"
17once_cell = "1.18.0" 17once_cell = "1.18.0"
18sqlx = { version = "0.7.1", features = ["postgres", "runtime-tokio"]} 18sqlx = { version = "0.7.1", features = ["postgres", "runtime-tokio"]}
19surge-ping = "0.8.0"
diff --git a/src/auth.rs b/src/auth.rs
index 0fffa60..e4b1c2f 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -1,8 +1,8 @@
1use std::error::Error;
2use axum::headers::HeaderValue; 1use axum::headers::HeaderValue;
3use axum::http::StatusCode; 2use axum::http::StatusCode;
3use axum::http::header::ToStrError;
4use tracing::{debug, error, trace}; 4use tracing::{debug, error, trace};
5use crate::auth::AuthError::{MissingSecret, ServerError, WrongSecret}; 5use crate::auth::AuthError::{MissingSecret, WrongSecret};
6use crate::config::SETTINGS; 6use crate::config::SETTINGS;
7 7
8pub fn auth(secret: Option<&HeaderValue>) -> Result<bool, AuthError> { 8pub fn auth(secret: Option<&HeaderValue>) -> Result<bool, AuthError> {
@@ -11,8 +11,8 @@ pub fn auth(secret: Option<&HeaderValue>) -> Result<bool, AuthError> {
11 trace!("value exists"); 11 trace!("value exists");
12 let key = SETTINGS 12 let key = SETTINGS
13 .get_string("apikey") 13 .get_string("apikey")
14 .map_err(|err| ServerError(Box::new(err)))?; 14 .map_err(AuthError::Config)?;
15 if value.to_str().map_err(|err| ServerError(Box::new(err)))? == key.as_str() { 15 if value.to_str().map_err(AuthError::HeaderToStr)? == key.as_str() {
16 debug!("successful auth"); 16 debug!("successful auth");
17 Ok(true) 17 Ok(true)
18 } else { 18 } else {
@@ -29,15 +29,20 @@ pub fn auth(secret: Option<&HeaderValue>) -> Result<bool, AuthError> {
29pub enum AuthError { 29pub enum AuthError {
30 WrongSecret, 30 WrongSecret,
31 MissingSecret, 31 MissingSecret,
32 ServerError(Box<dyn Error>), 32 Config(config::ConfigError),
33 HeaderToStr(ToStrError)
33} 34}
34 35
35impl AuthError { 36impl AuthError {
36 pub fn get(self) -> (StatusCode, &'static str) { 37 pub fn get(self) -> (StatusCode, &'static str) {
37 match self { 38 match self {
38 AuthError::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"), 39 Self::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"),
39 AuthError::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"), 40 Self::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"),
40 AuthError::ServerError(err) => { 41 Self::Config(err) => {
42 error!("server error: {}", err.to_string());
43 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
44 },
45 Self::HeaderToStr(err) => {
41 error!("server error: {}", err.to_string()); 46 error!("server error: {}", err.to_string());
42 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 47 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
43 }, 48 },
diff --git a/src/error.rs b/src/error.rs
index db2fc86..f143ee9 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,4 +1,5 @@
1use std::error::Error; 1use std::error::Error;
2use std::io;
2use axum::http::StatusCode; 3use axum::http::StatusCode;
3use axum::Json; 4use axum::Json;
4use axum::response::{IntoResponse, Response}; 5use axum::response::{IntoResponse, Response};
@@ -8,21 +9,45 @@ use crate::auth::AuthError;
8 9
9#[derive(Debug)] 10#[derive(Debug)]
10pub enum WebolError { 11pub enum WebolError {
11 Auth(AuthError),
12 Generic, 12 Generic,
13 Server(Box<dyn Error>), 13 Auth(AuthError),
14 Ping(surge_ping::SurgeError),
15 DB(sqlx::Error),
16 IpParse(<std::net::IpAddr as std::str::FromStr>::Err),
17 BufferParse(std::num::ParseIntError),
18 Broadcast(io::Error),
19 Axum(axum::Error)
14} 20}
15 21
16impl IntoResponse for WebolError { 22impl IntoResponse for WebolError {
17 fn into_response(self) -> Response { 23 fn into_response(self) -> Response {
18 let (status, error_message) = match self { 24 let (status, error_message) = match self {
19 WebolError::Auth(err) => err.get(), 25 Self::Auth(err) => err.get(),
20 WebolError::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""), 26 Self::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""),
21 WebolError::Server(err) => { 27 Self::Ping(err) => {
28 error!("Ping: {}", err.source().unwrap());
29 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
30 },
31 Self::IpParse(err) => {
32 error!("server error: {}", err.to_string());
33 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
34 },
35 Self::DB(err) => {
36 error!("server error: {}", err.to_string());
37 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
38 },
39 Self::Broadcast(err) => {
40 error!("server error: {}", err.to_string());
41 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
42 },
43 Self::BufferParse(err) => {
44 error!("server error: {}", err.to_string());
45 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
46 },
47 Self::Axum(err) => {
22 error!("server error: {}", err.to_string()); 48 error!("server error: {}", err.to_string());
23 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 49 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
24 }, 50 },
25
26 }; 51 };
27 let body = Json(json!({ 52 let body = Json(json!({
28 "error": error_message, 53 "error": error_message,
diff --git a/src/main.rs b/src/main.rs
index ce12cf6..9c31ec8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,12 +4,14 @@ use axum::{Router, routing::post};
4use axum::routing::{get, put}; 4use axum::routing::{get, put};
5use sqlx::PgPool; 5use sqlx::PgPool;
6use time::util::local_offset; 6use time::util::local_offset;
7use tokio::sync::mpsc::{self, Sender};
7use tracing::{info, level_filters::LevelFilter}; 8use tracing::{info, level_filters::LevelFilter};
8use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*}; 9use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*};
9use crate::config::SETTINGS; 10use crate::config::SETTINGS;
10use crate::db::init_db_pool; 11use crate::db::init_db_pool;
11use crate::routes::device::{get_device, post_device, put_device}; 12use crate::routes::device::{get_device, post_device, put_device};
12use crate::routes::start::start; 13use crate::routes::start::start;
14use crate::services::ping::ws_ping;
13 15
14mod auth; 16mod auth;
15mod config; 17mod config;
@@ -17,6 +19,7 @@ mod routes;
17mod wol; 19mod wol;
18mod db; 20mod db;
19mod error; 21mod error;
22mod services;
20 23
21#[tokio::main] 24#[tokio::main]
22async fn main() { 25async fn main() {
@@ -43,13 +46,23 @@ async fn main() {
43 let db = init_db_pool().await; 46 let db = init_db_pool().await;
44 sqlx::migrate!().run(&db).await.unwrap(); 47 sqlx::migrate!().run(&db).await.unwrap();
45 48
46 let shared_state = Arc::new(AppState { db }); 49 let (tx, mut rx) = mpsc::channel(32);
50
51 // FIXME: once_cell? or just static mutable
52 tokio::spawn( async move {
53 while let Some(message) = rx.recv().await {
54 println!("GOT = {}", message);
55 }
56 });
57
58 let shared_state = Arc::new(AppState { db, ping_send: tx });
47 59
48 let app = Router::new() 60 let app = Router::new()
49 .route("/start", post(start)) 61 .route("/start", post(start))
50 .route("/device", get(get_device)) 62 .route("/device", get(get_device))
51 .route("/device", put(put_device)) 63 .route("/device", put(put_device))
52 .route("/device", post(post_device)) 64 .route("/device", post(post_device))
65 .route("/status", get(ws_ping))
53 .with_state(shared_state); 66 .with_state(shared_state);
54 67
55 let addr = SETTINGS.get_string("serveraddr").unwrap_or("0.0.0.0:7229".to_string()); 68 let addr = SETTINGS.get_string("serveraddr").unwrap_or("0.0.0.0:7229".to_string());
@@ -61,5 +74,7 @@ async fn main() {
61} 74}
62 75
63pub struct AppState { 76pub struct AppState {
64 db: PgPool 77 db: PgPool,
78 ping_send: Sender<String>,
79 // ping_receive: Receiver<String>
65} 80}
diff --git a/src/routes/device.rs b/src/routes/device.rs
index 025c7d0..248d1e0 100644
--- a/src/routes/device.rs
+++ b/src/routes/device.rs
@@ -21,7 +21,7 @@ pub async fn get_device(State(state): State<Arc<crate::AppState>>, headers: Head
21 WHERE id = $1; 21 WHERE id = $1;
22 "#, 22 "#,
23 payload.id 23 payload.id
24 ).fetch_one(&state.db).await.map_err(|err| WebolError::Server(Box::new(err)))?; 24 ).fetch_one(&state.db).await.map_err(WebolError::DB)?;
25 25
26 Ok(Json(json!(device))) 26 Ok(Json(json!(device)))
27 } else { 27 } else {
@@ -46,7 +46,7 @@ pub async fn put_device(State(state): State<Arc<crate::AppState>>, headers: Head
46 payload.id, 46 payload.id,
47 payload.mac, 47 payload.mac,
48 payload.broadcast_addr 48 payload.broadcast_addr
49 ).execute(&state.db).await.map_err(|err| WebolError::Server(Box::new(err)))?; 49 ).execute(&state.db).await.map_err(WebolError::DB)?;
50 50
51 Ok(Json(json!(PutDeviceResponse { success: true }))) 51 Ok(Json(json!(PutDeviceResponse { success: true })))
52 } else { 52 } else {
@@ -80,7 +80,7 @@ pub async fn post_device(State(state): State<Arc<crate::AppState>>, headers: Hea
80 payload.mac, 80 payload.mac,
81 payload.broadcast_addr, 81 payload.broadcast_addr,
82 payload.id 82 payload.id
83 ).fetch_one(&state.db).await.map_err(|err| WebolError::Server(Box::new(err)))?; 83 ).fetch_one(&state.db).await.map_err(WebolError::DB)?;
84 84
85 Ok(Json(json!(device))) 85 Ok(Json(json!(device)))
86 } else { 86 } else {
@@ -93,4 +93,4 @@ pub struct PostDevicePayload {
93 id: String, 93 id: String,
94 mac: String, 94 mac: String,
95 broadcast_addr: String, 95 broadcast_addr: String,
96} \ No newline at end of file 96}
diff --git a/src/routes/start.rs b/src/routes/start.rs
index 163d58c..b45fe5b 100644
--- a/src/routes/start.rs
+++ b/src/routes/start.rs
@@ -14,7 +14,8 @@ use crate::error::WebolError;
14pub async fn start(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<StartPayload>) -> Result<Json<Value>, WebolError> { 14pub async fn start(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<StartPayload>) -> Result<Json<Value>, WebolError> {
15 info!("POST request"); 15 info!("POST request");
16 let secret = headers.get("authorization"); 16 let secret = headers.get("authorization");
17 if auth(secret).map_err(WebolError::Auth)? { 17 let authorized = auth(secret).map_err(WebolError::Auth)?;
18 if authorized {
18 let device = sqlx::query_as!( 19 let device = sqlx::query_as!(
19 Device, 20 Device,
20 r#" 21 r#"
@@ -23,7 +24,7 @@ pub async fn start(State(state): State<Arc<crate::AppState>>, headers: HeaderMap
23 WHERE id = $1; 24 WHERE id = $1;
24 "#, 25 "#,
25 payload.id 26 payload.id
26 ).fetch_one(&state.db).await.map_err(|err| WebolError::Server(Box::new(err)))?; 27 ).fetch_one(&state.db).await.map_err(WebolError::DB)?;
27 28
28 info!("starting {}", device.id); 29 info!("starting {}", device.id);
29 30
@@ -32,10 +33,14 @@ pub async fn start(State(state): State<Arc<crate::AppState>>, headers: HeaderMap
32 .unwrap_or("0.0.0.0:1111".to_string()); 33 .unwrap_or("0.0.0.0:1111".to_string());
33 34
34 let _ = send_packet( 35 let _ = send_packet(
35 &bind_addr.parse().map_err(|err| WebolError::Server(Box::new(err)))?, 36 &bind_addr.parse().map_err(WebolError::IpParse)?,
36 &device.broadcast_addr.parse().map_err(|err| WebolError::Server(Box::new(err)))?, 37 &device.broadcast_addr.parse().map_err(WebolError::IpParse)?,
37 create_buffer(&device.mac).map_err(|err| WebolError::Server(Box::new(err)))? 38 create_buffer(&device.mac)?
38 ).map_err(|err| WebolError::Server(Box::new(err))); 39 )?;
40
41 if payload.ping.is_some_and(|ping| ping) {
42 tokio::spawn(async move {crate::services::ping::spawn(state.ping_send.clone()).await});
43 }
39 Ok(Json(json!(StartResponse { id: device.id, boot: true }))) 44 Ok(Json(json!(StartResponse { id: device.id, boot: true })))
40 } else { 45 } else {
41 Err(WebolError::Generic) 46 Err(WebolError::Generic)
@@ -45,11 +50,11 @@ pub async fn start(State(state): State<Arc<crate::AppState>>, headers: HeaderMap
45#[derive(Deserialize)] 50#[derive(Deserialize)]
46pub struct StartPayload { 51pub struct StartPayload {
47 id: String, 52 id: String,
48 _test: Option<bool>, 53 ping: Option<bool>,
49} 54}
50 55
51#[derive(Serialize)] 56#[derive(Serialize)]
52struct StartResponse { 57struct StartResponse {
53 id: String, 58 id: String,
54 boot: bool, 59 boot: bool,
55} \ No newline at end of file 60}
diff --git a/src/services/mod.rs b/src/services/mod.rs
new file mode 100644
index 0000000..a766209
--- /dev/null
+++ b/src/services/mod.rs
@@ -0,0 +1 @@
pub mod ping;
diff --git a/src/services/ping.rs b/src/services/ping.rs
new file mode 100644
index 0000000..6e710ec
--- /dev/null
+++ b/src/services/ping.rs
@@ -0,0 +1,53 @@
1use std::sync::Arc;
2
3use axum::{extract::{WebSocketUpgrade, ws::WebSocket, State}, response::Response};
4use tokio::sync::mpsc::Sender;
5use tracing::{debug, error};
6
7use crate::{error::WebolError, AppState};
8
9pub async fn spawn(tx: Sender<String>) -> Result<(), WebolError> {
10 let payload = [0; 8];
11
12 let mut cont = true;
13 while cont {
14 let ping = surge_ping::ping(
15 "192.168.178.28".parse().map_err(WebolError::IpParse)?,
16 &payload
17 ).await;
18
19 if let Err(ping) = ping {
20 cont = matches!(ping, surge_ping::SurgeError::Timeout { .. });
21
22 debug!("{}", cont);
23
24 if !cont {
25 return Err(ping).map_err(WebolError::Ping)
26 }
27
28 } else {
29 let (_, duration) = ping.unwrap();
30 debug!("Ping took {:?}", duration);
31 cont = false;
32 // FIXME: remove unwrap
33 tx.send("Got ping".to_string()).await.unwrap();
34 };
35 }
36
37 Ok(())
38}
39
40pub async fn ws_ping(ws: WebSocketUpgrade, State(_state): State<Arc<AppState>>) -> Response {
41 ws.on_upgrade(handle_socket)
42}
43
44// FIXME: Handle commands through enum
45async fn handle_socket(mut socket: WebSocket) {
46 // TODO: Understand Cow
47
48 // match socket.send(axum::extract::ws::Message::Close(Some(CloseFrame { code: 4000, reason: Cow::Owned("started".to_owned()) }))).await.map_err(WebolError::Axum) {
49 match socket.send(axum::extract::ws::Message::Text("started".to_string())).await.map_err(WebolError::Axum) {
50 Ok(..) => (),
51 Err(err) => { error!("Server Error: {:?}", err) }
52 };
53}
diff --git a/src/wol.rs b/src/wol.rs
index 80b66cd..0cdcae3 100644
--- a/src/wol.rs
+++ b/src/wol.rs
@@ -1,16 +1,17 @@
1use std::net::{SocketAddr, UdpSocket}; 1use std::net::{SocketAddr, UdpSocket};
2use std::num::ParseIntError; 2
3use crate::error::WebolError;
3 4
4/// Creates the magic packet from a mac address 5/// Creates the magic packet from a mac address
5/// 6///
6/// # Panics 7/// # Panics
7/// 8///
8/// Panics if `mac_addr` is an invalid mac 9/// Panics if `mac_addr` is an invalid mac
9pub fn create_buffer(mac_addr: &str) -> Result<Vec<u8>, ParseIntError> { 10pub fn create_buffer(mac_addr: &str) -> Result<Vec<u8>, WebolError> {
10 let mut mac = Vec::new(); 11 let mut mac = Vec::new();
11 let sp = mac_addr.split(':'); 12 let sp = mac_addr.split(':');
12 for f in sp { 13 for f in sp {
13 mac.push(u8::from_str_radix(f, 16)?); 14 mac.push(u8::from_str_radix(f, 16).map_err(WebolError::BufferParse)?)
14 }; 15 };
15 let mut buf = vec![255; 6]; 16 let mut buf = vec![255; 6];
16 for _ in 0..16 { 17 for _ in 0..16 {
@@ -22,8 +23,8 @@ pub fn create_buffer(mac_addr: &str) -> Result<Vec<u8>, ParseIntError> {
22} 23}
23 24
24/// Sends a buffer on UDP broadcast 25/// Sends a buffer on UDP broadcast
25pub fn send_packet(bind_addr: &SocketAddr, broadcast_addr: &SocketAddr, buffer: Vec<u8>) -> Result<usize, std::io::Error> { 26pub fn send_packet(bind_addr: &SocketAddr, broadcast_addr: &SocketAddr, buffer: Vec<u8>) -> Result<usize, WebolError> {
26 let socket = UdpSocket::bind(bind_addr)?; 27 let socket = UdpSocket::bind(bind_addr).map_err(WebolError::Broadcast)?;
27 socket.set_broadcast(true)?; 28 socket.set_broadcast(true).map_err(WebolError::Broadcast)?;
28 socket.send_to(&buffer, broadcast_addr) 29 socket.send_to(&buffer, broadcast_addr).map_err(WebolError::Broadcast)
29} \ No newline at end of file 30}