diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 13 | ||||
-rw-r--r-- | src/routes/mod.rs | 3 | ||||
-rw-r--r-- | src/routes/start.rs | 24 | ||||
-rw-r--r-- | src/routes/status.rs | 12 | ||||
-rw-r--r-- | src/services/ping.rs | 79 |
5 files changed, 96 insertions, 35 deletions
diff --git a/src/main.rs b/src/main.rs index 124c44e..854b59d 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use std::collections::HashMap; | ||
1 | use std::env; | 2 | use std::env; |
2 | use std::sync::Arc; | 3 | use std::sync::Arc; |
3 | use axum::{Router, routing::post}; | 4 | use axum::{Router, routing::post}; |
@@ -5,13 +6,14 @@ use axum::routing::{get, put}; | |||
5 | use sqlx::PgPool; | 6 | use sqlx::PgPool; |
6 | use time::util::local_offset; | 7 | use time::util::local_offset; |
7 | use tokio::sync::broadcast::{channel, Sender}; | 8 | use tokio::sync::broadcast::{channel, Sender}; |
9 | use tokio::sync::Mutex; | ||
8 | use tracing::{info, level_filters::LevelFilter}; | 10 | use tracing::{info, level_filters::LevelFilter}; |
9 | use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*}; | 11 | use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*}; |
10 | use crate::config::SETTINGS; | 12 | use crate::config::SETTINGS; |
11 | use crate::db::init_db_pool; | 13 | use crate::db::init_db_pool; |
12 | use crate::routes::device::{get_device, post_device, put_device}; | 14 | use crate::routes::device::{get_device, post_device, put_device}; |
13 | use crate::routes::start::start; | 15 | use crate::routes::start::start; |
14 | use crate::services::ping::ws_ping; | 16 | use crate::routes::status::status; |
15 | 17 | ||
16 | mod auth; | 18 | mod auth; |
17 | mod config; | 19 | mod config; |
@@ -47,15 +49,17 @@ async fn main() { | |||
47 | sqlx::migrate!().run(&db).await.unwrap(); | 49 | sqlx::migrate!().run(&db).await.unwrap(); |
48 | 50 | ||
49 | let (tx, _) = channel(32); | 51 | let (tx, _) = channel(32); |
52 | |||
53 | let ping_map: HashMap<String, (String, bool)> = HashMap::new(); | ||
50 | 54 | ||
51 | let shared_state = Arc::new(AppState { db, ping_send: tx }); | 55 | let shared_state = Arc::new(AppState { db, ping_send: tx, ping_map: Arc::new(Mutex::new(ping_map)) }); |
52 | 56 | ||
53 | let app = Router::new() | 57 | let app = Router::new() |
54 | .route("/start", post(start)) | 58 | .route("/start", post(start)) |
55 | .route("/device", get(get_device)) | 59 | .route("/device", get(get_device)) |
56 | .route("/device", put(put_device)) | 60 | .route("/device", put(put_device)) |
57 | .route("/device", post(post_device)) | 61 | .route("/device", post(post_device)) |
58 | .route("/status", get(ws_ping)) | 62 | .route("/status", get(status)) |
59 | .with_state(shared_state); | 63 | .with_state(shared_state); |
60 | 64 | ||
61 | let addr = SETTINGS.get_string("serveraddr").unwrap_or("0.0.0.0:7229".to_string()); | 65 | let addr = SETTINGS.get_string("serveraddr").unwrap_or("0.0.0.0:7229".to_string()); |
@@ -69,4 +73,5 @@ async fn main() { | |||
69 | pub struct AppState { | 73 | pub struct AppState { |
70 | db: PgPool, | 74 | db: PgPool, |
71 | ping_send: Sender<String>, | 75 | ping_send: Sender<String>, |
72 | } | 76 | ping_map: Arc<Mutex<HashMap<String, (String, bool)>>>, |
77 | } \ No newline at end of file | ||
diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 12fbfab..d5ab0d6 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs | |||
@@ -1,2 +1,3 @@ | |||
1 | pub mod start; | 1 | pub mod start; |
2 | pub mod device; \ No newline at end of file | 2 | pub mod device; |
3 | pub mod status; \ No newline at end of file | ||
diff --git a/src/routes/start.rs b/src/routes/start.rs index 863ef16..45e7ec8 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs | |||
@@ -4,15 +4,18 @@ use serde::{Deserialize, Serialize}; | |||
4 | use std::sync::Arc; | 4 | use std::sync::Arc; |
5 | use axum::extract::State; | 5 | use axum::extract::State; |
6 | use serde_json::{json, Value}; | 6 | use serde_json::{json, Value}; |
7 | use tracing::{debug, info}; | 7 | use tracing::{debug, info, warn}; |
8 | use uuid::Uuid; | ||
8 | use crate::auth::auth; | 9 | use crate::auth::auth; |
9 | use crate::config::SETTINGS; | 10 | use crate::config::SETTINGS; |
10 | use crate::wol::{create_buffer, send_packet}; | 11 | use crate::wol::{create_buffer, send_packet}; |
11 | use crate::db::Device; | 12 | use crate::db::Device; |
12 | use crate::error::WebolError; | 13 | use crate::error::WebolError; |
13 | 14 | ||
15 | #[axum_macros::debug_handler] | ||
14 | pub async fn start(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<StartPayload>) -> Result<Json<Value>, WebolError> { | 16 | pub async fn start(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<StartPayload>) -> Result<Json<Value>, WebolError> { |
15 | info!("POST request"); | 17 | info!("POST request"); |
18 | warn!("{:?}", state.ping_map); | ||
16 | let secret = headers.get("authorization"); | 19 | let secret = headers.get("authorization"); |
17 | let authorized = auth(secret).map_err(WebolError::Auth)?; | 20 | let authorized = auth(secret).map_err(WebolError::Auth)?; |
18 | if authorized { | 21 | if authorized { |
@@ -38,14 +41,20 @@ pub async fn start(State(state): State<Arc<crate::AppState>>, headers: HeaderMap | |||
38 | create_buffer(&device.mac)? | 41 | create_buffer(&device.mac)? |
39 | )?; | 42 | )?; |
40 | 43 | ||
41 | if payload.ping.is_some_and(|ping| ping) { | 44 | let uuid = if payload.ping.is_some_and(|ping| ping) { |
42 | debug!("ping true"); | 45 | let uuid_gen = Uuid::new_v4().to_string(); |
43 | tokio::spawn(async move { | 46 | let uuid_genc = uuid_gen.clone(); |
47 | tokio::spawn(async move{ | ||
44 | debug!("Init ping service"); | 48 | debug!("Init ping service"); |
45 | crate::services::ping::spawn(state.ping_send.clone()).await | 49 | state.ping_map.lock().await.insert(uuid_gen, ("192.168.178.94".to_string(), false)); |
50 | |||
51 | warn!("{:?}", state.ping_map); | ||
52 | |||
53 | crate::services::ping::spawn(state.ping_send.clone(), "192.168.178.94".to_string()).await; | ||
46 | }); | 54 | }); |
47 | }; | 55 | Some(uuid_genc) |
48 | Ok(Json(json!(StartResponse { id: device.id, boot: true }))) | 56 | } else { None }; |
57 | Ok(Json(json!(StartResponse { id: device.id, boot: true, uuid }))) | ||
49 | } else { | 58 | } else { |
50 | Err(WebolError::Generic) | 59 | Err(WebolError::Generic) |
51 | } | 60 | } |
@@ -61,4 +70,5 @@ pub struct StartPayload { | |||
61 | struct StartResponse { | 70 | struct StartResponse { |
62 | id: String, | 71 | id: String, |
63 | boot: bool, | 72 | boot: bool, |
73 | uuid: Option<String>, | ||
64 | } | 74 | } |
diff --git a/src/routes/status.rs b/src/routes/status.rs new file mode 100644 index 0000000..cdecf6a --- /dev/null +++ b/src/routes/status.rs | |||
@@ -0,0 +1,12 @@ | |||
1 | use std::sync::Arc; | ||
2 | use axum::extract::{State, WebSocketUpgrade}; | ||
3 | use axum::response::Response; | ||
4 | use serde::Deserialize; | ||
5 | use crate::AppState; | ||
6 | use crate::services::ping::status_websocket; | ||
7 | |||
8 | #[axum_macros::debug_handler] | ||
9 | pub async fn status(State(state): State<Arc<AppState>>, ws: WebSocketUpgrade) -> Response { | ||
10 | // TODO: remove unwrap | ||
11 | ws.on_upgrade(move |socket| status_websocket(socket, state.ping_send.clone(), state.ping_map.clone())) | ||
12 | } \ No newline at end of file | ||
diff --git a/src/services/ping.rs b/src/services/ping.rs index ff328a5..e3d465d 100644 --- a/src/services/ping.rs +++ b/src/services/ping.rs | |||
@@ -1,26 +1,29 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | use std::collections::HashMap; | ||
2 | use std::sync::Arc; | 3 | use std::sync::Arc; |
3 | 4 | ||
4 | use axum::{extract::{WebSocketUpgrade, ws::WebSocket, State}, response::Response}; | 5 | use axum::extract::{ws::WebSocket}; |
6 | use axum::extract::ws::Message; | ||
5 | use tokio::sync::broadcast::{Sender}; | 7 | use tokio::sync::broadcast::{Sender}; |
6 | use tracing::{debug, error, trace}; | 8 | use tokio::sync::Mutex; |
9 | use tracing::{debug, error, trace, warn}; | ||
7 | 10 | ||
8 | use crate::{error::WebolError, AppState}; | 11 | use crate::error::WebolError; |
9 | 12 | ||
10 | pub async fn spawn(tx: Sender<String>) -> Result<(), WebolError> { | 13 | pub async fn spawn(tx: Sender<String>, ip: String) -> Result<(), WebolError> { |
11 | let payload = [0; 8]; | 14 | let payload = [0; 8]; |
12 | 15 | ||
13 | let mut cont = true; | 16 | let mut cont = true; |
14 | while cont { | 17 | while cont { |
15 | let ping = surge_ping::ping( | 18 | let ping = surge_ping::ping( |
16 | "127.0.0.1".parse().map_err(WebolError::IpParse)?, | 19 | ip.parse().map_err(WebolError::IpParse)?, |
17 | &payload | 20 | &payload |
18 | ).await; | 21 | ).await; |
19 | 22 | ||
20 | if let Err(ping) = ping { | 23 | if let Err(ping) = ping { |
21 | cont = matches!(ping, surge_ping::SurgeError::Timeout { .. }); | 24 | cont = matches!(ping, surge_ping::SurgeError::Timeout { .. }); |
22 | 25 | ||
23 | debug!("{}", cont); | 26 | // debug!("{}", cont); |
24 | 27 | ||
25 | if !cont { | 28 | if !cont { |
26 | return Err(ping).map_err(WebolError::Ping) | 29 | return Err(ping).map_err(WebolError::Ping) |
@@ -31,29 +34,59 @@ pub async fn spawn(tx: Sender<String>) -> Result<(), WebolError> { | |||
31 | debug!("Ping took {:?}", duration); | 34 | debug!("Ping took {:?}", duration); |
32 | cont = false; | 35 | cont = false; |
33 | // FIXME: remove unwrap | 36 | // FIXME: remove unwrap |
34 | tx.send("Got ping".to_string()).unwrap(); | 37 | // FIXME: if error: SendError because no listener, then handle the entry directly |
38 | tx.send(ip.clone()); | ||
35 | }; | 39 | }; |
36 | } | 40 | } |
37 | 41 | ||
38 | Ok(()) | 42 | Ok(()) |
39 | } | 43 | } |
40 | 44 | ||
41 | // TODO: Status to routes, websocket here | ||
42 | pub async fn ws_ping(State(state): State<Arc<AppState>>, ws: WebSocketUpgrade) -> Response { | ||
43 | ws.on_upgrade(move |socket| handle_socket(socket, state.ping_send.clone())) | ||
44 | } | ||
45 | |||
46 | // FIXME: Handle commands through enum | 45 | // FIXME: Handle commands through enum |
47 | async fn handle_socket(mut socket: WebSocket, tx: Sender<String>) { | 46 | pub async fn status_websocket(mut socket: WebSocket, tx: Sender<String>, ping_map: Arc<Mutex<HashMap<String, (String, bool)>>>) { |
48 | // TODO: Understand Cow | 47 | warn!("{:?}", ping_map); |
49 | while let message = tx.subscribe().recv().await.unwrap() { | 48 | |
50 | trace!("GOT = {}", message); | 49 | let mut uuid: Option<String> = None; |
51 | if &message == "Got ping" { | 50 | |
52 | break; | 51 | trace!("wait for ws message (uuid)"); |
52 | let msg = socket.recv().await; | ||
53 | uuid = Some(msg.unwrap().unwrap().into_text().unwrap()); | ||
54 | |||
55 | let uuid = uuid.unwrap(); | ||
56 | |||
57 | trace!("Search for uuid: {:?}", uuid); | ||
58 | |||
59 | let device = ping_map.lock().await.get(&uuid).unwrap().to_owned(); | ||
60 | |||
61 | trace!("got device: {:?}", device); | ||
62 | |||
63 | match device.1 { | ||
64 | true => { | ||
65 | debug!("already started"); | ||
66 | socket.send(Message::Text(format!("start_{}", uuid))).await.unwrap(); | ||
67 | socket.close().await.unwrap(); | ||
68 | }, | ||
69 | false => { | ||
70 | let ip = device.0.to_owned(); | ||
71 | let mut i = 0; | ||
72 | loop{ | ||
73 | trace!("{}", i); | ||
74 | // TODO: Check if older than 10 minutes, close if true | ||
75 | trace!("wait for tx message"); | ||
76 | let message = tx.subscribe().recv().await.unwrap(); | ||
77 | trace!("GOT = {}", message); | ||
78 | if message == ip { | ||
79 | trace!("message == ip"); | ||
80 | break; | ||
81 | } | ||
82 | i += 1; | ||
83 | }; | ||
84 | |||
85 | socket.send(Message::Text(format!("start_{}", uuid))).await.unwrap(); | ||
86 | socket.close().await.unwrap(); | ||
87 | tokio::time::sleep(tokio::time::Duration::from_secs(60)).await; | ||
88 | ping_map.lock().await.remove(&uuid); | ||
89 | warn!("{:?}", ping_map); | ||
53 | } | 90 | } |
54 | }; | 91 | } |
55 | match socket.send(axum::extract::ws::Message::Close(Some(axum::extract::ws::CloseFrame { code: 4000, reason: Cow::Owned("started".to_owned()) }))).await.map_err(WebolError::Axum) { | ||
56 | Ok(..) => (), | ||
57 | Err(err) => { error!("Server Error: {:?}", err) } | ||
58 | }; | ||
59 | } \ No newline at end of file | 92 | } \ No newline at end of file |