use std::fmt::Debug; use std::sync::Arc; use serde::de::DeserializeOwned; use serde::Serialize; use warp::filters::BoxedFilter; use warp::Filter; use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam}; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::APIResponse; use geoffrey_models::models::settings::GeoffreySettings; use geoffrey_models::models::token::{Permissions, Token}; use geoffrey_models::models::CommandLevel; use crate::api_endpoint::{ApiEndpoint, RequestType}; use crate::commands::add_item::AddItem; use crate::commands::add_location::AddLocation; use crate::commands::delete::Delete; use crate::commands::edit::Edit; use crate::commands::find::FindCommand; use crate::commands::info::InfoCommand; use crate::commands::link::LinkCommand; use crate::commands::register::Register; use crate::commands::remove_item::RemoveItem; use crate::commands::report_out_of_stock::ReportOutOfStock; use crate::commands::restock::Restock; use crate::commands::selling::Selling; use crate::commands::set_portal::SetPortal; use crate::context::Context; use crate::helper::{get_player_from_req, get_token_from_req}; use crate::Result; pub mod add_item; pub mod add_location; pub mod add_token; pub mod delete; pub mod edit; pub mod find; pub mod info; pub mod link; pub mod register; pub mod remove_item; pub mod report_out_of_stock; pub mod restock; pub mod selling; pub mod set_portal; pub mod settings; pub trait Command: ApiEndpoint { type Req: GeoffreyParam + 'static; type Resp: Serialize + DeserializeOwned + Send + Debug; fn command_level() -> CommandLevel; fn run_command(ctx: Arc, req: &Self::Req, user: Option) -> Result; fn validate_parameters(_: &Self::Req, _: &GeoffreySettings) -> Result<()> { Ok(()) } fn user_is_authorized(token: &Option, user: &Option) -> Result<()> { if let Some(token) = token { if !match Self::command_level() { CommandLevel::MOD => token.check_permission(Permissions::ModCommand), CommandLevel::ADMIN => token.check_permission(Permissions::Admin), _ => token.check_permission(Permissions::Command), } { return Err(GeoffreyAPIError::TokenNotAuthorized); } if Self::command_level() == CommandLevel::ALL { Ok(()) } else if let Some(user) = user { if user.auth_level >= Self::command_level() { Ok(()) } else { Err(GeoffreyAPIError::PermissionInsufficient) } } else { Err(GeoffreyAPIError::PlayerNotRegistered) } } else { Err(GeoffreyAPIError::TokenNotAuthorized) } } } pub fn handle_command( ctx: Arc, req: CommandRequest, ) -> Result { log::info!("Running command {}", T::endpoint_name()); log::debug!("User: {:?} Request params: {:?}", req.user_id, req.params); let user = get_player_from_req::(&ctx.db, &req)?; let token = get_token_from_req(&ctx.db, &req)?; match T::user_is_authorized(&token, &user) { Ok(_) => { T::validate_parameters(&req.params, &ctx.cfg.geoffrey_settings)?; T::run_command(ctx, &req.params, user) } Err(e) => Err(e), } } #[allow(clippy::needless_return)] pub fn create_command_filter(ctx: Arc) -> BoxedFilter<(impl warp::Reply,)> { let filter = warp::path(T::endpoint_name()) .and(warp::any().map(move || ctx.clone())) .and(warp::body::json()) .map(|ctx: Arc, req: CommandRequest| { let reply = handle_command::(ctx, req); if let Ok(reply) = reply { log::debug!("Successfully processed command"); warp::reply::json(&APIResponse::Response::(reply)) } else { let e = reply.err().unwrap(); let msg = e.to_string(); log::warn!("Got error when processing command '{:?}': {}", e, msg); warp::reply::json(&APIResponse::::Error { error: e, msg }) } }); if T::request_type() == RequestType::POST { return filter.and(warp::post()).boxed(); } else { return filter.and(warp::get()).boxed(); } } pub fn command_filter( ctx: Arc, ) -> impl Filter + Clone { warp::path("command").and( create_command_filter::(ctx.clone()) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx.clone())) .or(create_command_filter::(ctx)), ) }