diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/mod.rs index 2893bf89ce..1883621d7a 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/mod.rs @@ -1,6 +1,8 @@ pub mod delete_media_file; pub mod drain_multipart_request; pub mod get_media_file; +pub mod delete_media_file; +pub mod update_media_file; pub mod list_featured_media_files; pub mod list_media_files; pub mod list_media_files_for_user; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/update_media_file.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/update_media_file.rs new file mode 100644 index 0000000000..4d099b8bbf --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/media_files/update_media_file.rs @@ -0,0 +1,154 @@ +use std::fmt; +use std::sync::Arc; + +use actix_web::{HttpRequest, HttpResponse, web}; +use actix_web::error::ResponseError; +use actix_web::http::StatusCode; +use actix_web::web::Path; +use log::{error, log, warn}; +use enums::common::visibility::Visibility; + +use http_server_common::request::get_request_ip::get_request_ip; +use http_server_common::response::response_success_helpers::simple_json_success; +use http_server_common::response::serialize_as_json_error::serialize_as_json_error; +use mysql_queries::queries::media_files::get_media_file::get_media_file; +use mysql_queries::queries::media_files::update_media_file::{update_media_file, UpdateMediaFileArgs}; +use tokens::tokens::media_files::MediaFileToken; + +use crate::server_state::ServerState; + +#[derive(Deserialize)] +pub struct UpdateMediaFileRequest { + pub creator_set_visibility: Option, +} + +#[derive(Serialize)] +pub struct UpdateMediaFileResponse { + pub success: bool, +} + +/// For the URL PathInfo +#[derive(Deserialize)] +pub struct UpdateMediaFilePathInfo { + token: MediaFileToken, +} + +// =============== Error Response =============== + +#[derive(Debug, Serialize)] +pub enum UpdateMediaFileError { + BadInput(String), + NotFound, + NotAuthorized, + ServerError, +} + +impl ResponseError for UpdateMediaFileError { + fn status_code(&self) -> StatusCode { + match *self { + UpdateMediaFileError::BadInput(_) => StatusCode::BAD_REQUEST, + UpdateMediaFileError::NotFound => StatusCode::NOT_FOUND, + UpdateMediaFileError::NotAuthorized => StatusCode::UNAUTHORIZED, + UpdateMediaFileError::ServerError => StatusCode::INTERNAL_SERVER_ERROR, + } + } + + fn error_response(&self) -> HttpResponse { + serialize_as_json_error(self) + } +} + +// NB: Not using derive_more::Display since Clion doesn't understand it. +impl fmt::Display for UpdateMediaFileError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +// =============== Handler =============== + +pub async fn update_media_file_handler( + http_request: HttpRequest, + path: Path, + request: web::Json, + server_state: web::Data>) -> Result +{ + let maybe_user_session = server_state + .session_checker + .maybe_get_user_session(&http_request, &server_state.mysql_pool) + .await + .map_err(|e| { + warn!("Session checker error: {:?}", e); + UpdateMediaFileError::ServerError + })?; + + let user_session = match maybe_user_session { + Some(session) => session, + None => { + warn!("not logged in"); + return Err(UpdateMediaFileError::NotAuthorized); + } + }; + + let media_file_token = path.token.clone(); + let is_mod = user_session.can_ban_users; + + let media_file_lookup_result = get_media_file( + &path.token, + is_mod, + &server_state.mysql_pool, + ).await; + + let media_file = match media_file_lookup_result { + Ok(Some(media_file)) => media_file, + Ok(None) => { + warn!("MediaFile not found: {:?}", media_file_token); + return Err(UpdateMediaFileError::NotFound); + }, + Err(err) => { + warn!("Error looking up media_file: {:?}", err); + return Err(UpdateMediaFileError::ServerError); + } + }; + + let is_creator = media_file.maybe_creator_user_token + .is_some_and(|t| t.as_str() == &user_session.user_token); + + if !is_creator && !is_mod { + warn!("user is not allowed to edit this media_file: {}", user_session.user_token); + return Err(UpdateMediaFileError::NotAuthorized); + } + + let mut creator_set_visibility = Visibility::Public; + + + if let Some(visibility) = request.creator_set_visibility.as_deref() { + creator_set_visibility = Visibility::from_str(visibility) + .map_err(|_| UpdateMediaFileError::BadInput("bad record visibility".to_string()))?; + } + + let ip_address = get_request_ip(&http_request); + let mut maybe_mod_user_token = None; + + if is_mod { + maybe_mod_user_token = Some(user_session.user_token.clone()); + } + let query_result = update_media_file( + UpdateMediaFileArgs { + media_file_token: &media_file_token.clone(), + creator_set_visibility: &creator_set_visibility, + maybe_mod_user_token: maybe_mod_user_token.as_deref(), + mysql_pool: &server_state.mysql_pool + } + ).await; + + match query_result { + Ok(_) => {}, + Err(err) => { + warn!("Update MediaFile DB error: {:?}", err); + return Err(UpdateMediaFileError::ServerError); + } + }; + + Ok(simple_json_success()) +} diff --git a/crates/service/web/storyteller_web/src/routes.rs b/crates/service/web/storyteller_web/src/routes.rs index 7e9569dad2..79c10e153a 100644 --- a/crates/service/web/storyteller_web/src/routes.rs +++ b/crates/service/web/storyteller_web/src/routes.rs @@ -45,6 +45,7 @@ use crate::http_server::endpoints::investor_demo::enable_demo_mode_handler::enab use crate::http_server::endpoints::leaderboard::get_leaderboard::leaderboard_handler; use crate::http_server::endpoints::media_files::delete_media_file::delete_media_file_handler; use crate::http_server::endpoints::media_files::get_media_file::get_media_file_handler; +use crate::http_server::endpoints::media_files::update_media_file::update_media_file_handler; use crate::http_server::endpoints::media_files::list_featured_media_files::list_featured_media_files_handler; use crate::http_server::endpoints::media_files::list_media_files::list_media_files_handler; use crate::http_server::endpoints::media_files::list_media_files_for_user::list_media_files_for_user_handler; @@ -1021,6 +1022,8 @@ fn add_media_file_routes (app: App) -> App .route(web::delete().to(delete_media_file_handler)) .route(web::head().to(|| HttpResponse::Ok())) ) + .service(web::resource("/file/{token}/update") + .route(web::post().to(update_media_file_handler)) .service(web::resource("/list") .route(web::get().to(list_media_files_handler)) .route(web::head().to(|| HttpResponse::Ok()))