diff --git a/crates/app/storyteller_web/src/http_server/endpoints/categories/list_my_categories.rs b/crates/app/storyteller_web/src/http_server/endpoints/categories/list_my_categories.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/crates/app/storyteller_web/src/http_server/endpoints/categories/mod.rs b/crates/app/storyteller_web/src/http_server/endpoints/categories/mod.rs index 521f4cf0e0..6005bf61fb 100644 --- a/crates/app/storyteller_web/src/http_server/endpoints/categories/mod.rs +++ b/crates/app/storyteller_web/src/http_server/endpoints/categories/mod.rs @@ -1,7 +1,5 @@ pub mod assign_tts_category; pub mod create_category; pub mod delete_category; -pub mod edit_category; -pub mod list_my_categories; pub mod list_tts_categories; pub mod list_tts_model_assigned_categories; \ No newline at end of file diff --git a/crates/app/storyteller_web/src/http_server/endpoints/categories/edit_category.rs b/crates/app/storyteller_web/src/http_server/endpoints/moderation/categories/edit_category.rs similarity index 100% rename from crates/app/storyteller_web/src/http_server/endpoints/categories/edit_category.rs rename to crates/app/storyteller_web/src/http_server/endpoints/moderation/categories/edit_category.rs diff --git a/crates/app/storyteller_web/src/http_server/endpoints/moderation/categories/list_tts_categories_for_moderation.rs b/crates/app/storyteller_web/src/http_server/endpoints/moderation/categories/list_tts_categories_for_moderation.rs new file mode 100644 index 0000000000..63f4ca8cdd --- /dev/null +++ b/crates/app/storyteller_web/src/http_server/endpoints/moderation/categories/list_tts_categories_for_moderation.rs @@ -0,0 +1,180 @@ +use actix_http::Error; +use actix_http::http::header; +use actix_web::HttpResponseBuilder; +use actix_web::cookie::Cookie; +use actix_web::error::ResponseError; +use actix_web::http::StatusCode; +use actix_web::web::{Path, Json}; +use actix_web::{Responder, web, HttpResponse, error, HttpRequest}; +use chrono::{DateTime, Utc}; +use crate::database::enums::record_visibility::RecordVisibility; +use crate::database::queries::query_w2l_template::select_w2l_template_by_token; +use crate::database::query_builders::list_categories_query_builder::ListCategoriesQueryBuilder; +use crate::http_server::web_utils::ip_address::get_request_ip; +use crate::http_server::web_utils::response_error_helpers::to_simple_json_error; +use crate::http_server::web_utils::response_success_helpers::simple_json_success; +use crate::server_state::ServerState; +use crate::util::email_to_gravatar::email_to_gravatar; +use crate::util::markdown_to_html::markdown_to_html; +use crate::util::random_crockford_token::random_crockford_token; +use crate::util::random_prefix_crockford_token::random_prefix_crockford_token; +use crate::validations::check_for_slurs::contains_slurs; +use http_server_common::response::serialize_as_json_error::serialize_as_json_error; +use log::{info, warn, log}; +use regex::Regex; +use sqlx::MySqlPool; +use sqlx::error::DatabaseError; +use sqlx::error::Error::Database; +use sqlx::mysql::MySqlDatabaseError; +use std::fmt; +use std::sync::Arc; + +// =============== Success Response =============== + +#[derive(Serialize)] +pub struct ListTtsCategoriesForModerationResponse { + pub success: bool, + pub categories: Vec, +} + +#[derive(Serialize)] +pub struct CategoryForModeration { + pub category_token: String, + + pub model_type: String, // TODO: ENUM + + pub maybe_super_category_token: Option, + + pub can_directly_have_models: bool, + pub can_have_subcategories: bool, + pub can_only_mods_apply: bool, + + pub name: String, + pub maybe_dropdown_name: Option, + + pub creator_user_token: Option, + pub creator_username: Option, + pub creator_display_name: Option, + + // Moderator fields + pub is_mod_approved: Option, + pub maybe_mod_comments: Option, + + pub created_at: DateTime, + pub updated_at: DateTime, + pub deleted_at: Option>, +} + +// =============== Error Response =============== + +#[derive(Debug, Serialize)] +pub enum ListTtsCategoriesForModerationError { + NotAuthorized, + ServerError, +} + +impl ResponseError for ListTtsCategoriesForModerationError { + fn status_code(&self) -> StatusCode { + match *self { + ListTtsCategoriesForModerationError::NotAuthorized => StatusCode::UNAUTHORIZED, + ListTtsCategoriesForModerationError::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 ListTtsCategoriesForModerationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +// =============== Handler =============== + +pub async fn list_tts_categories_for_moderation_handler( + http_request: HttpRequest, + 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); + ListTtsCategoriesForModerationError::ServerError + })?; + + let user_session = match maybe_user_session { + Some(session) => session, + None => { + warn!("not logged in"); + return Err(ListTtsCategoriesForModerationError::NotAuthorized); + } + }; + + // TODO: We don't have a permission for categories, so we use this as a proxy. + if !user_session.can_ban_users { + warn!("no permission to edit categories"); + return Err(ListTtsCategoriesForModerationError::NotAuthorized); + } + + let query_builder = ListCategoriesQueryBuilder::new() + .show_deleted(true) + .show_unapproved(true) + .scope_model_type(Some("tts")); + + let query_result = + query_builder.perform_query(&server_state.mysql_pool).await; + + let results = match query_result { + Ok(results) => results, + Err(err) => { + warn!("DB error: {:?}", err); + return Err(ListTtsCategoriesForModerationError::ServerError); + } + }; + + warn!("Number of categories: {:?}", results.categories.len()); + + let mut categories = results.categories.iter() + .map(|c| { + CategoryForModeration { + category_token: c.category_token.clone(), + model_type: c.model_type.clone(), + maybe_super_category_token: c.maybe_super_category_token.clone(), + can_directly_have_models: c.can_directly_have_models, + can_have_subcategories: c.can_have_subcategories, + can_only_mods_apply: c.can_only_mods_apply, + name: c.name.clone(), + maybe_dropdown_name:c.maybe_dropdown_name.clone(), + creator_user_token: c.creator_user_token.clone(), + creator_username: c.creator_username.clone(), + creator_display_name: c.creator_display_name.clone(), + is_mod_approved: c.is_mod_approved.clone(), + maybe_mod_comments: c.maybe_mod_comments.clone(), + created_at: c.created_at.clone(), + updated_at: c.updated_at.clone(), + deleted_at: c.deleted_at.clone() + } + }) + .collect::>(); + + // TODO: Sort by dropdown name too. + categories.sort_by(|c1, c2| c1.name.cmp(&c2.name)); + + let response = ListTtsCategoriesForModerationResponse { + success: true, + categories, + }; + + let body = serde_json::to_string(&response) + .map_err(|e| ListTtsCategoriesForModerationError::ServerError)?; + + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) +} diff --git a/crates/app/storyteller_web/src/http_server/endpoints/moderation/categories/mod.rs b/crates/app/storyteller_web/src/http_server/endpoints/moderation/categories/mod.rs new file mode 100644 index 0000000000..cf77590623 --- /dev/null +++ b/crates/app/storyteller_web/src/http_server/endpoints/moderation/categories/mod.rs @@ -0,0 +1,2 @@ +pub mod edit_category; +pub mod list_tts_categories_for_moderation; \ No newline at end of file diff --git a/crates/app/storyteller_web/src/http_server/endpoints/moderation/mod.rs b/crates/app/storyteller_web/src/http_server/endpoints/moderation/mod.rs index afd7a69415..95f5febebb 100644 --- a/crates/app/storyteller_web/src/http_server/endpoints/moderation/mod.rs +++ b/crates/app/storyteller_web/src/http_server/endpoints/moderation/mod.rs @@ -1,4 +1,5 @@ pub mod approval; +pub mod categories; pub mod ip_bans; pub mod jobs; pub mod stats; diff --git a/crates/app/storyteller_web/src/routes.rs b/crates/app/storyteller_web/src/routes.rs index 1fb83b5267..6a24de5fdd 100644 --- a/crates/app/storyteller_web/src/routes.rs +++ b/crates/app/storyteller_web/src/routes.rs @@ -1,9 +1,13 @@ -use actix_web::{App, web, HttpResponse}; use actix_http::body::MessageBody; use actix_service::ServiceFactory; use actix_web::dev::{ServiceRequest, ServiceResponse}; use actix_web::error::Error; +use actix_web::{App, web, HttpResponse}; +use crate::http_server::endpoints::categories::assign_tts_category::assign_tts_category_handler; use crate::http_server::endpoints::categories::create_category::create_category_handler; +use crate::http_server::endpoints::categories::delete_category::delete_category_handler; +use crate::http_server::endpoints::categories::list_tts_categories::list_tts_categories_handler; +use crate::http_server::endpoints::categories::list_tts_model_assigned_categories::list_tts_model_assigned_categories_handler; use crate::http_server::endpoints::events::list_events::list_events_handler; use crate::http_server::endpoints::leaderboard::get_leaderboard::leaderboard_handler; use crate::http_server::endpoints::misc::default_route_404::default_route_404; @@ -11,6 +15,7 @@ use crate::http_server::endpoints::misc::enable_alpha_easy_handler::enable_alpha use crate::http_server::endpoints::misc::enable_alpha_handler::enable_alpha_handler; use crate::http_server::endpoints::misc::root_index::get_root_index; use crate::http_server::endpoints::moderation::approval::pending_w2l_templates::get_pending_w2l_templates_handler; +use crate::http_server::endpoints::moderation::categories::edit_category::edit_category_handler; use crate::http_server::endpoints::moderation::ip_bans::add_ip_ban::add_ip_ban_handler; use crate::http_server::endpoints::moderation::ip_bans::delete_ip_ban::delete_ip_ban_handler; use crate::http_server::endpoints::moderation::ip_bans::get_ip_ban::get_ip_ban_handler; @@ -60,11 +65,7 @@ use crate::http_server::endpoints::w2l::get_w2l_template_use_count::get_w2l_temp use crate::http_server::endpoints::w2l::get_w2l_upload_template_job_status::get_w2l_upload_template_job_status_handler; use crate::http_server::endpoints::w2l::list_w2l_templates::list_w2l_templates_handler; use crate::http_server::endpoints::w2l::set_w2l_template_mod_approval::set_w2l_template_mod_approval_handler; -use crate::http_server::endpoints::categories::delete_category::delete_category_handler; -use crate::http_server::endpoints::categories::edit_category::edit_category_handler; -use crate::http_server::endpoints::categories::list_tts_categories::list_tts_categories_handler; -use crate::http_server::endpoints::categories::assign_tts_category::assign_tts_category_handler; -use crate::http_server::endpoints::categories::list_tts_model_assigned_categories::list_tts_model_assigned_categories_handler; +use crate::http_server::endpoints::moderation::categories::list_tts_categories_for_moderation::list_tts_categories_for_moderation_handler; pub fn add_routes (app: App) -> App where @@ -235,6 +236,19 @@ fn add_moderator_routes (app: App) -> App .route(web::head().to(|| HttpResponse::Ok())) ) ) + .service( + web::scope("/categories") + .service( + web::resource("/{token}/edit") + .route(web::post().to(edit_category_handler)) + .route(web::head().to(|| HttpResponse::Ok())) + ) + .service( + web::resource("/tts/list") + .route(web::get().to(list_tts_categories_for_moderation_handler)) + .route(web::head().to(|| HttpResponse::Ok())) + ) + ) ) } @@ -419,11 +433,6 @@ fn add_category_routes (app: App) -> App .route(web::post().to(create_category_handler)) .route(web::head().to(|| HttpResponse::Ok())) ) - .service( - web::resource("/{token}/edit") - .route(web::post().to(edit_category_handler)) - .route(web::head().to(|| HttpResponse::Ok())) - ) .service( web::resource("/{token}/delete") .route(web::post().to(delete_category_handler))