tenebrous-dicebot/web-ui/crate/src/logic.rs

69 lines
2.2 KiB
Rust

use crate::{
api,
state::{Action, Claims, Room, WebUiDispatcher},
};
use jsonwebtoken::dangerous_insecure_decode;
pub(crate) type LogicResult = Result<Vec<Action>, Action>;
pub(crate) trait LogicResultExt {
/// Consumes self into the vec of Actions to apply to state,
/// either the list of successful actions, or a list containing
/// the error action.
fn actions(self) -> Vec<Action>;
}
impl LogicResultExt for LogicResult {
fn actions(self) -> Vec<Action> {
self.unwrap_or_else(|err_action| vec![err_action])
}
}
fn map_to_vec(action: Option<Action>) -> Vec<Action> {
action.map(|a| vec![a]).unwrap_or_default()
}
async fn refresh_ensured_jwt() -> Result<(String, Option<Action>), Action> {
api::auth::refresh_jwt()
.await
.map(|new_jwt| (new_jwt.clone(), Some(Action::UpdateJwt(new_jwt))))
.map_err(|_| Action::Logout)
}
async fn ensure_jwt(dispatch: &WebUiDispatcher) -> Result<(String, Option<Action>), Action> {
//TODO lots of clones here. can we avoid?
use jsonwebtoken::errors::ErrorKind;
let token = dispatch.state().jwt_token.as_deref().unwrap_or_default();
let validation: Result<Claims, _> = dangerous_insecure_decode(token).map(|data| data.claims);
//If valid, simply return token. If expired, attempt to refresh.
//Otherwise, bubble error.
let token_and_action = match validation {
Ok(_) => (token.to_owned(), None),
Err(e) if matches!(e.kind(), ErrorKind::ExpiredSignature) => refresh_ensured_jwt().await?,
Err(_) => return Err(Action::Logout), //TODO carry error inside Logout?
};
Ok(token_and_action)
}
pub(crate) async fn fetch_rooms(dispatch: &WebUiDispatcher) -> LogicResult {
let (jwt, jwt_update) = ensure_jwt(dispatch)
.await
.map(|(token, update)| (token, map_to_vec(update)))?;
let rooms: Vec<Action> = api::dicebot::rooms_for_user(&jwt, &dispatch.state().username)
.await
.map_err(|e| Action::ErrorMessage(e))?
.into_iter()
.map(|room| {
Action::AddRoom(Room {
room_id: room.room_id,
display_name: room.display_name,
})
})
.collect();
Ok(rooms.into_iter().chain(jwt_update).collect())
}