From 799c891475d899c1a856938a664354487fc121bb Mon Sep 17 00:00:00 2001 From: sinavir Date: Thu, 10 Oct 2024 13:27:37 +0200 Subject: [PATCH] feat(cof): Add special role and motorized fixture support --- backend/src/authorization.rs | 13 ++++--- backend/src/handler.rs | 69 +++++++++++++++++++++++++++--------- backend/src/model.rs | 63 +++++++++++++++++++++----------- backend/src/route.rs | 7 ++++ frontend/frontend/views.py | 1 + 5 files changed, 112 insertions(+), 41 deletions(-) diff --git a/backend/src/authorization.rs b/backend/src/authorization.rs index 24a594a..f5b4995 100644 --- a/backend/src/authorization.rs +++ b/backend/src/authorization.rs @@ -13,11 +13,15 @@ use serde::{Deserialize, Serialize}; struct Claims { sub: String, scope: String, - user: User, + user: String, + is_cof: bool, } #[derive(Eq, PartialEq, Hash, Debug, Serialize, Deserialize, Clone)] -pub struct User(String); +pub struct User { + name: String, + pub is_cof: bool, +} fn check_token(token: &str, jwt: &str) -> Option { let decoded_token = decode::( @@ -27,10 +31,11 @@ fn check_token(token: &str, jwt: &str) -> Option { ); match decoded_token { Ok(token_data) => { - let user =token_data.claims.user; + let user = token_data.claims.user; + let is_cof =token_data.claims.is_cof; if token_data.claims.scope == "modify" { tracing::info!("Successful auth {user:?}"); - Some(user) + Some(User { name: user, is_cof }) } else { tracing::debug!("Failed auth: {user:?} don't have modify scope"); None diff --git a/backend/src/handler.rs b/backend/src/handler.rs index 1714388..5757f4d 100644 --- a/backend/src/handler.rs +++ b/backend/src/handler.rs @@ -1,5 +1,5 @@ use crate::authorization::User; -use crate::model::{DMXArray, DMXAtom, DMXColor, DB}; +use crate::model::{ColorArray, DMXAtom, DMXBeam, DMXBeamChange, DMXColorAtom, DMXColor, DB}; use axum::{ debug_handler, extract::{Path, State}, @@ -30,18 +30,18 @@ pub async fn list_values_handler(State(db): State) -> impl IntoResponse { pub async fn batch_edit_value_handler( State(db): State, Extension(user): Extension, - Json(body): Json>, + Json(body): Json>, ) -> Result<(), StatusCode> { 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){ return Err(StatusCode::TOO_MANY_REQUESTS); } for i in &body { - check_id(i.address, &lock.dmx)?; + check_id(i.address, &lock.dmx.colors)?; } for i in &body { - lock.dmx[i.address] = i.value; - match db.static_state.color_change_channel.send(*i) { + lock.dmx.colors[i.address] = i.value; + match db.static_state.change_channel.send(DMXAtom::Color(*i)) { Ok(_) => (), Err(_) => (), } @@ -56,8 +56,8 @@ pub async fn get_value_handler( State(db): State, ) -> Result { let lock = db.mut_state.read().await; - check_id(id, &lock.dmx)?; - Ok(Json(lock.dmx[id])) + check_id(id, &lock.dmx.colors)?; + Ok(Json(lock.dmx.colors[id])) } #[debug_handler] @@ -67,12 +67,12 @@ pub async fn edit_value_handler( Json(body): Json, ) -> Result<(), StatusCode> { let mut lock = db.mut_state.write().await; - check_id(id, &lock.dmx)?; - lock.dmx[id] = body; + check_id(id, &lock.dmx.colors)?; + lock.dmx.colors[id] = body; match db .static_state - .color_change_channel - .send(DMXAtom::new(id, body)) + .change_channel + .send(DMXAtom::Color(DMXColorAtom::new(id, body))) { Ok(_) => (), Err(_) => (), @@ -81,20 +81,55 @@ pub async fn edit_value_handler( Ok(()) } +#[debug_handler] +pub async fn get_motor_value_handler( + State(db): State, +) -> Result { + let lock = db.mut_state.read().await; + Ok(Json(lock.dmx.motor)) +} + +#[debug_handler] +pub async fn edit_motor_value_handler( + State(db): State, + Extension(user): Extension, + Json(body): Json, +) -> Result<(), StatusCode> { + if !user.is_cof { + return Err(StatusCode::FORBIDDEN); + } + let mut lock = db.mut_state.write().await; + lock.dmx.motor = DMXBeam { + pan: body.pan.unwrap_or(lock.dmx.motor.pan), + tilt: body.tilt.unwrap_or(lock.dmx.motor.tilt), + focus: body.focus.unwrap_or(lock.dmx.motor.focus), + }; + let _ = db + .static_state + .change_channel + .send(DMXAtom::Motor(lock.dmx.motor)) + ; + + Ok(()) +} + + #[debug_handler] pub async fn sse_handler(State(db): State) -> impl IntoResponse { - let rx = db.static_state.color_change_channel.subscribe(); + let rx = db.static_state.change_channel.subscribe(); let data: Vec; + let motor: DMXBeam; { let lock = db.mut_state.read().await; - data = lock.dmx.clone(); + data = lock.dmx.colors.clone(); + motor = lock.dmx.motor.clone(); } let init_data = data.into_iter().enumerate().map(|(i, x)| { - Ok(DMXAtom { + Ok(DMXAtom::Color(DMXColorAtom { address: i, value: x, - }) - }); + })) + }).chain(std::iter::once(Ok(DMXAtom::Motor(motor)))); let init = stream::iter(init_data); let stream = init .chain(stream::wrappers::BroadcastStream::new(rx)) @@ -110,7 +145,7 @@ pub async fn sse_handler(State(db): State) -> impl IntoResponse { ) } -fn check_id(id: usize, val: &DMXArray) -> Result<(), StatusCode> { +fn check_id(id: usize, val: &ColorArray) -> Result<(), StatusCode> { if id >= val.len() { return Err(StatusCode::NOT_FOUND); }; diff --git a/backend/src/model.rs b/backend/src/model.rs index 762ba98..de82a59 100644 --- a/backend/src/model.rs +++ b/backend/src/model.rs @@ -8,39 +8,66 @@ use std::sync::Arc; use std::time::Instant; use tokio::sync::{broadcast, RwLock}; -#[derive(Debug, Deserialize, Serialize, Copy, Clone)] +#[derive(Debug, Default, Deserialize, Serialize, Copy, Clone)] pub struct DMXColor { pub red: u8, pub green: u8, pub blue: u8, } -pub type DMXArray = Vec; +#[derive(Debug, Default, Deserialize, Serialize, Copy, Clone)] +pub struct DMXBeam { + pub pan: u8, + pub tilt: u8, + pub focus: u8, +} + +#[derive(Debug, Default, Deserialize, Serialize, Copy, Clone)] +pub struct DMXBeamChange { + pub pan: Option, + pub tilt: Option, + pub focus: Option, +} + +pub type ColorArray = Vec; #[derive(Debug, Deserialize, Serialize, Copy, Clone)] -pub struct DMXAtom { +#[serde(tag = "type")] +pub enum DMXAtom { + Color(DMXColorAtom), + Motor(DMXBeam), +} + +#[derive(Debug, Deserialize, Serialize, Copy, Clone)] +pub struct DMXColorAtom { pub address: usize, pub value: DMXColor, } -impl DMXAtom { - pub fn new(address: usize, value: DMXColor) -> DMXAtom { - DMXAtom { address, value } +impl DMXColorAtom { + pub fn new(address: usize, value: DMXColor) -> DMXColorAtom { + DMXColorAtom { address, value } } } +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct DMXState { + pub colors: ColorArray, + pub motor: DMXBeam, +} + pub struct AppState { - pub dmx: DMXArray, + pub dmx: DMXState, pub ratelimit_info: HashMap, } impl AppState { pub fn new(size: usize, save_path: &str) -> AppState { - let data: Result = + let data: Result = match fs::File::open(save_path).map(std::io::BufReader::new) { Ok(read) => serde_json::from_reader(read) - .map(|mut v: DMXArray| { - v.resize( + .map(|mut v: DMXState| { + v.colors.resize( size, DMXColor { red: 0, @@ -58,14 +85,10 @@ impl AppState { Err(()) } }; - let dmx = data.unwrap_or(vec![ - DMXColor { - red: 0, - green: 0, - blue: 0, - }; - size - ]); + let dmx = data.unwrap_or(DMXState { + colors: vec![DMXColor::default(); size], + motor: DMXBeam::default(), + }); AppState { dmx, @@ -76,7 +99,7 @@ impl AppState { pub struct StaticState { pub jwt_key: String, - pub color_change_channel: broadcast::Sender, + pub change_channel: broadcast::Sender, pub save_path: String, } @@ -100,7 +123,7 @@ pub fn make_db() -> DB { .as_ref(), ) .expect("an JSON string"), - color_change_channel: broadcast::Sender::new(512), + change_channel: broadcast::Sender::new(512), save_path, }, mut_state, diff --git a/backend/src/route.rs b/backend/src/route.rs index 1dacd74..9774cfe 100644 --- a/backend/src/route.rs +++ b/backend/src/route.rs @@ -60,6 +60,13 @@ pub fn create_router() -> Router { .layer(middleware::from_fn_with_state(db.clone(), jwt_middleware)), ), ) + .route( + "/api/motor", + get(handler::get_motor_value_handler).post( + handler::edit_motor_value_handler + .layer(middleware::from_fn_with_state(db.clone(), jwt_middleware)), + ), + ) .layer(cors) .with_state(db) } diff --git a/frontend/frontend/views.py b/frontend/frontend/views.py index d8657b1..47479c8 100644 --- a/frontend/frontend/views.py +++ b/frontend/frontend/views.py @@ -47,6 +47,7 @@ class TokenView(LoginRequiredMixin, View): "exp": datetime.now(tz=timezone.utc) + timedelta(hours=9), "sub": "ragb", "user": self.request.user.username, + "is_cof": self.requests.user.groups.filter(name="cof").exists(), "scope": "modify", }, settings.JWT_SECRET,