fix(backend): tcp error on closing + fmt

This commit is contained in:
sinavir 2024-10-13 20:11:10 +02:00
parent 84aaef1900
commit 700d967ff3
7 changed files with 111 additions and 95 deletions

View file

@ -1,14 +1,14 @@
use crate::model::DB; use axum::extract::{Request, State};
use axum::{ use axum::http::StatusCode;
extract::{Request, State}, use axum::middleware::Next;
http::StatusCode, use axum::response::Response;
middleware::Next, use axum_extra::headers;
response::Response, use axum_extra::typed_header::TypedHeader;
};
use axum_extra::{headers, typed_header::TypedHeader};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::model::DB;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
struct Claims { struct Claims {
sub: String, sub: String,
@ -32,7 +32,7 @@ fn check_token(token: &str, jwt: &str) -> Option<User> {
match decoded_token { match decoded_token {
Ok(token_data) => { Ok(token_data) => {
let user = token_data.claims.user; let user = token_data.claims.user;
let is_cof =token_data.claims.is_cof; let is_cof = token_data.claims.is_cof;
if token_data.claims.scope == "modify" { if token_data.claims.scope == "modify" {
tracing::info!("Successful auth {user:?}"); tracing::info!("Successful auth {user:?}");
Some(User { name: user, is_cof }) Some(User { name: user, is_cof })
@ -41,7 +41,10 @@ fn check_token(token: &str, jwt: &str) -> Option<User> {
None None
} }
} }
Err(err) => {tracing::debug!("Failed decoding token: {err:?}"); None}, Err(err) => {
tracing::debug!("Failed decoding token: {err:?}");
None
}
} }
} }
@ -69,9 +72,9 @@ pub async fn jwt_middleware_cof(
let token = auth.token(); let token = auth.token();
if let Some(user) = check_token(token, &state.static_state.jwt_key) { if let Some(user) = check_token(token, &state.static_state.jwt_key) {
if user.is_cof { if user.is_cof {
request.extensions_mut().insert(user); request.extensions_mut().insert(user);
return Ok(next.run(request).await) return Ok(next.run(request).await);
}; };
}; };
Err(StatusCode::FORBIDDEN) Err(StatusCode::FORBIDDEN)
} }

View file

@ -1,15 +1,16 @@
use crate::authorization::User;
use crate::model::{ColorArray, DMXAtom, DMXBeam, DMXBeamChange, DMXColorAtom, DMXColor, DB};
use axum::{
debug_handler,
extract::{Path, State},
http::StatusCode,
response::{sse::Event, IntoResponse, Sse},
Extension, Json,
};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tokio_stream::StreamExt;
use tokio_stream::{self as stream}; use axum::extract::{Path, State};
use axum::http::StatusCode;
use axum::response::sse::Event;
use axum::response::{IntoResponse, Sse};
use axum::{debug_handler, Extension, Json};
use tokio_stream::{
StreamExt, {self as stream},
};
use crate::authorization::User;
use crate::model::{ColorArray, DMXAtom, DMXBeam, DMXBeamChange, DMXColor, DMXColorAtom, DB};
#[debug_handler] #[debug_handler]
pub async fn healthcheck_handler() -> impl IntoResponse { pub async fn healthcheck_handler() -> impl IntoResponse {
@ -33,7 +34,12 @@ pub async fn batch_edit_value_handler(
Json(body): Json<Vec<DMXColorAtom>>, Json(body): Json<Vec<DMXColorAtom>>,
) -> Result<(), StatusCode> { ) -> Result<(), StatusCode> {
let mut lock = db.mut_state.write().await; let mut lock = db.mut_state.write().await;
if lock.ratelimit_info.get(&user).map(|d| d.elapsed() <= Duration::from_millis(500)).unwrap_or(false){ if lock
.ratelimit_info
.get(&user)
.map(|d| d.elapsed() <= Duration::from_millis(500))
.unwrap_or(false)
{
return Err(StatusCode::TOO_MANY_REQUESTS); return Err(StatusCode::TOO_MANY_REQUESTS);
} }
for i in &body { for i in &body {
@ -103,13 +109,11 @@ pub async fn edit_motor_value_handler(
let _ = db let _ = db
.static_state .static_state
.change_channel .change_channel
.send(DMXAtom::Motor(lock.dmx.motor)) .send(DMXAtom::Motor(lock.dmx.motor));
;
Ok(()) Ok(())
} }
#[debug_handler] #[debug_handler]
pub async fn sse_handler(State(db): State<DB>) -> impl IntoResponse { pub async fn sse_handler(State(db): State<DB>) -> impl IntoResponse {
let rx = db.static_state.change_channel.subscribe(); let rx = db.static_state.change_channel.subscribe();
@ -120,12 +124,16 @@ pub async fn sse_handler(State(db): State<DB>) -> impl IntoResponse {
data = lock.dmx.colors.clone(); data = lock.dmx.colors.clone();
motor = lock.dmx.motor.clone(); motor = lock.dmx.motor.clone();
} }
let init_data = data.into_iter().enumerate().map(|(i, x)| { let init_data = data
Ok(DMXAtom::Color(DMXColorAtom { .into_iter()
address: i, .enumerate()
value: x, .map(|(i, x)| {
})) Ok(DMXAtom::Color(DMXColorAtom {
}).chain(std::iter::once(Ok(DMXAtom::Motor(motor)))); address: i,
value: x,
}))
})
.chain(std::iter::once(Ok(DMXAtom::Motor(motor))));
let init = stream::iter(init_data); let init = stream::iter(init_data);
let stream = init let stream = init
.chain(stream::wrappers::BroadcastStream::new(rx)) .chain(stream::wrappers::BroadcastStream::new(rx))

View file

@ -2,23 +2,24 @@ mod authorization;
mod handler; mod handler;
mod model; mod model;
mod route; mod route;
mod state_recorder;
mod socket_listener; mod socket_listener;
mod state_recorder;
use dotenvy; use dotenvy;
use crate::state_recorder::record_state;
use crate::socket_listener::listen_socket;
use route::create_router; use route::create_router;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use crate::socket_listener::listen_socket;
use crate::state_recorder::record_state;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
tracing_subscriber::registry() tracing_subscriber::registry()
.with( .with(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { tracing_subscriber::EnvFilter::try_from_default_env()
"info,axum::rejection=trace".into() .unwrap_or_else(|_| "info,axum::rejection=trace".into()),
}),
) )
.with(tracing_subscriber::fmt::layer()) .with(tracing_subscriber::fmt::layer())
.init(); .init();
@ -37,11 +38,7 @@ async fn main() {
tracing::debug!("Trying to bind at {bind}"); tracing::debug!("Trying to bind at {bind}");
let listener = tokio::net::TcpListener::bind(bind).await.unwrap(); let listener = tokio::net::TcpListener::bind(bind).await.unwrap();
let app = create_router(db.clone()).layer(TraceLayer::new_for_http());
let app = create_router(db.clone())
.layer(
TraceLayer::new_for_http()
);
tracing::info!("🚀 Server started successfully"); tracing::info!("🚀 Server started successfully");
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
} }

View file

@ -1,13 +1,15 @@
use crate::authorization::User;
use dotenvy;
use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::Instant; use std::time::Instant;
use dotenvy;
use serde::{Deserialize, Serialize};
use tokio::sync::{broadcast, RwLock}; use tokio::sync::{broadcast, RwLock};
use crate::authorization::User;
#[derive(Debug, Default, Deserialize, Serialize, Copy, Clone)] #[derive(Debug, Default, Deserialize, Serialize, Copy, Clone)]
pub struct DMXColor { pub struct DMXColor {
pub red: u8, pub red: u8,
@ -123,7 +125,7 @@ pub fn make_db() -> DB {
.as_ref(), .as_ref(),
) )
.expect("an JSON string"), .expect("an JSON string"),
change_channel: broadcast::Sender::new(5*state_size), change_channel: broadcast::Sender::new(5 * state_size),
save_path, save_path,
}, },
mut_state, mut_state,

View file

@ -1,10 +1,11 @@
use crate::authorization::{jwt_middleware, jwt_middleware_cof}; use axum::handler::Handler;
use crate::handler; use axum::routing::get;
use crate::model; use axum::{middleware, Router};
use axum::{handler::Handler, middleware};
use axum::{routing::get, Router};
use tower_http::cors::{Any, CorsLayer}; use tower_http::cors::{Any, CorsLayer};
use crate::authorization::{jwt_middleware, jwt_middleware_cof};
use crate::{handler, model};
pub fn create_router(db: model::DB) -> Router { pub fn create_router(db: model::DB) -> Router {
let cors = CorsLayer::new() let cors = CorsLayer::new()
// allow requests from any origin // allow requests from any origin

View file

@ -1,47 +1,51 @@
use tokio::task;
use axum::extract::State; use axum::extract::State;
use serde::Deserialize; use serde::Deserialize;
use tokio::io::AsyncBufReadExt; use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::io::BufReader;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use crate::{handler, model }; use tokio::task;
use crate::{handler, model};
pub async fn listen_socket(db: model::DB) { pub async fn listen_socket(db: model::DB) {
let binding = dotenvy::var("BIND_TCP").unwrap_or(String::from("127.0.0.1:1235")); let binding = dotenvy::var("BIND_TCP").unwrap_or(String::from("127.0.0.1:1235"));
tracing::debug!("Trying to bind at {binding}"); tracing::debug!("Trying to bind at {binding}");
let listener = TcpListener::bind(binding) let listener = TcpListener::bind(binding)
.await .await
.expect("Failed to listen for direct connection."); .expect("Failed to listen for direct connection.");
loop { loop {
match listener.accept().await { match listener.accept().await {
Ok((socket, addr)) => { Ok((socket, addr)) => {
let db_socket = db.clone(); let db_socket = db.clone();
let mut buf_reader = BufReader::new(socket); let mut buf_reader = BufReader::new(socket);
let mut buf = String::new(); let mut buf = String::new();
tracing::debug!("Accepted {addr:?}"); tracing::debug!("Accepted {addr:?}");
task::spawn(async move { task::spawn(async move {
loop { loop {
buf.clear(); buf.clear();
let _ = buf_reader.read_line(&mut buf).await; match buf_reader.read_line(&mut buf).await {
match model::DMXBeamChange::deserialize( Ok(0) => break,
&mut serde_json::Deserializer::from_str(&buf), Ok(_) => (),
) { Err(e) => tracing::error!("Error while reading tcp stream: {e:?}"),
Ok(data) => handler::edit_motor_value_handler( };
State(db_socket.clone()), match model::DMXBeamChange::deserialize(
axum::Json(data), &mut serde_json::Deserializer::from_str(&buf),
) ) {
.await Ok(data) => handler::edit_motor_value_handler(
.unwrap_or(()), State(db_socket.clone()),
Err(e) => tracing::error!("Error reading incoming data: {e:?}"), axum::Json(data),
} )
.await
.unwrap_or(()),
Err(e) => tracing::error!("Error reading incoming data: {e:?}"),
} }
}); }
} tracing::debug!("Closing tcp connection {addr:?}");
Err(e) => { });
tracing::error!("Failed to get client: {e:?}"); }
} Err(e) => {
}; tracing::error!("Failed to get client: {e:?}");
} }
};
} }
}

View file

@ -1,7 +1,8 @@
use std::fs::File;
use serde_json::to_writer; use serde_json::to_writer;
use std::fs::File;
use tokio::time::{sleep, Duration}; use tokio::time::{sleep, Duration};
use crate::model; use crate::model;
pub async fn record_state(db_to_save: model::DB) { pub async fn record_state(db_to_save: model::DB) {