wip on moderation of templates

This commit is contained in:
Brandon Thomas committed 2021-06-07 00:32:15 -04:00
1 parent 9495798583
commit 13e2e32ca5
10 files changed
+567 -195

No files matched your search

+207 -194
View File
@@ -490,6 +490,213 @@
]
}
},
"1c35b51bf807a3cd27b9deef2278c066626215e631c651cabb0fff56675f3624": {
"query": "\nSELECT\n w2l.token as template_token,\n w2l.template_type,\n w2l.creator_user_token,\n users.username as creator_username,\n users.display_name as creator_display_name,\n w2l.updatable_slug,\n w2l.title,\n w2l.frame_width,\n w2l.frame_height,\n w2l.duration_millis,\n w2l.maybe_public_bucket_preview_image_object_name,\n w2l.maybe_public_bucket_preview_video_object_name,\n w2l.is_mod_approved,\n w2l.created_at,\n w2l.updated_at\nFROM w2l_templates as w2l\nJOIN users\nON users.token = w2l.creator_user_token\nWHERE w2l.updatable_slug = ?\nAND w2l.deleted_at IS NULL\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "template_token",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4229
},
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 1,
"name": "template_type",
"type_info": {
"type": "String",
"flags": {
"bits": 393
},
"char_set": 224,
"max_size": 28
}
},
{
"ordinal": 2,
"name": "creator_user_token",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4233
},
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 3,
"name": "creator_username",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4229
},
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 4,
"name": "creator_display_name",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4225
},
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 5,
"name": "updatable_slug",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4229
},
"char_set": 224,
"max_size": 256
}
},
{
"ordinal": 6,
"name": "title",
"type_info": {
"type": "String",
"flags": {
"bits": 4225
},
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 7,
"name": "frame_width",
"type_info": {
"type": "Long",
"flags": {
"bits": 1
},
"char_set": 63,
"max_size": 10
}
},
{
"ordinal": 8,
"name": "frame_height",
"type_info": {
"type": "Long",
"flags": {
"bits": 1
},
"char_set": 63,
"max_size": 10
}
},
{
"ordinal": 9,
"name": "duration_millis",
"type_info": {
"type": "Long",
"flags": {
"bits": 1
},
"char_set": 63,
"max_size": 10
}
},
{
"ordinal": 10,
"name": "maybe_public_bucket_preview_image_object_name",
"type_info": {
"type": "VarString",
"flags": {
"bits": 128
},
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 11,
"name": "maybe_public_bucket_preview_video_object_name",
"type_info": {
"type": "VarString",
"flags": {
"bits": 128
},
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 12,
"name": "is_mod_approved",
"type_info": {
"type": "Tiny",
"flags": {
"bits": 0
},
"char_set": 63,
"max_size": 1
}
},
{
"ordinal": 13,
"name": "created_at",
"type_info": {
"type": "Timestamp",
"flags": {
"bits": 1153
},
"char_set": 63,
"max_size": 19
}
},
{
"ordinal": 14,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
"flags": {
"bits": 9345
},
"char_set": 63,
"max_size": 19
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
true,
true,
true,
false,
false
]
}
},
"215f82f4034ccb47bfa000c3fead66c3c9f7d5caa905aa99a702d396aeeebbc6": {
"query": "\nINSERT INTO tts_model_upload_jobs\nSET\n token = ?,\n uuid_idempotency_token = ?,\n creator_user_token = ?,\n creator_ip_address = ?,\n creator_set_visibility = ?,\n title = ?,\n tts_model_type = ?,\n maybe_subject_token = ?,\n maybe_actor_subject_token = ?,\n download_url = ?,\n download_url_type = ?,\n status = \"pending\"\n ",
"describe": {
@@ -2034,200 +2241,6 @@
]
}
},
"4995b1688dab1a25fa16ea59d6282b052be5c4ed6bf740a0840e24c9ad67e55a": {
"query": "\nSELECT\n w2l.token as template_token,\n w2l.template_type,\n w2l.creator_user_token,\n users.username as creator_username,\n users.display_name as creator_display_name,\n w2l.updatable_slug,\n w2l.title,\n w2l.frame_width,\n w2l.frame_height,\n w2l.duration_millis,\n w2l.maybe_public_bucket_preview_image_object_name,\n w2l.maybe_public_bucket_preview_video_object_name,\n w2l.created_at,\n w2l.updated_at\nFROM w2l_templates as w2l\nJOIN users\nON users.token = w2l.creator_user_token\nWHERE w2l.updatable_slug = ?\nAND w2l.deleted_at IS NULL\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "template_token",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4229
},
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 1,
"name": "template_type",
"type_info": {
"type": "String",
"flags": {
"bits": 393
},
"char_set": 224,
"max_size": 28
}
},
{
"ordinal": 2,
"name": "creator_user_token",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4233
},
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 3,
"name": "creator_username",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4229
},
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 4,
"name": "creator_display_name",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4225
},
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 5,
"name": "updatable_slug",
"type_info": {
"type": "VarString",
"flags": {
"bits": 4229
},
"char_set": 224,
"max_size": 256
}
},
{
"ordinal": 6,
"name": "title",
"type_info": {
"type": "String",
"flags": {
"bits": 4225
},
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 7,
"name": "frame_width",
"type_info": {
"type": "Long",
"flags": {
"bits": 1
},
"char_set": 63,
"max_size": 10
}
},
{
"ordinal": 8,
"name": "frame_height",
"type_info": {
"type": "Long",
"flags": {
"bits": 1
},
"char_set": 63,
"max_size": 10
}
},
{
"ordinal": 9,
"name": "duration_millis",
"type_info": {
"type": "Long",
"flags": {
"bits": 1
},
"char_set": 63,
"max_size": 10
}
},
{
"ordinal": 10,
"name": "maybe_public_bucket_preview_image_object_name",
"type_info": {
"type": "VarString",
"flags": {
"bits": 128
},
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 11,
"name": "maybe_public_bucket_preview_video_object_name",
"type_info": {
"type": "VarString",
"flags": {
"bits": 128
},
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 12,
"name": "created_at",
"type_info": {
"type": "Timestamp",
"flags": {
"bits": 1153
},
"char_set": 63,
"max_size": 19
}
},
{
"ordinal": 13,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
"flags": {
"bits": 9345
},
"char_set": 63,
"max_size": 19
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
true,
true,
false,
false
]
}
},
"4b4b6c2b8306cd5b6b1fe28fd492de5e346f0a53c0efc4f0eec57c4238860764": {
"query": "\nSELECT\n tts_results.token as tts_result_token,\n\n tts_results.model_token as tts_model_token,\n tts_results.inference_text as inference_text,\n\n users.token as maybe_creator_user_token,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n\n tts_results.file_size_bytes,\n tts_results.duration_millis,\n tts_results.created_at,\n tts_results.updated_at\n\nFROM tts_results\nLEFT OUTER JOIN tts_models\n ON tts_results.model_token = tts_models.token\nLEFT OUTER JOIN users\n ON tts_results.maybe_creator_user_token = users.token\nWHERE\n tts_results.deleted_at IS NULL\n AND tts_results.is_mod_hidden_from_public IS FALSE\n AND tts_models.is_mod_disabled IS FALSE\n ",
"describe": {
+1
View File
@@ -5,4 +5,5 @@ pub mod list_tts_models;
pub mod list_w2l_inference_results;
pub mod list_w2l_templates;
pub mod query_user_profile;
pub mod query_w2l_template;
pub mod sessions;
+182
View File
@@ -0,0 +1,182 @@
use anyhow::anyhow;
use chrono::{DateTime, Utc};
use crate::AnyhowResult;
use crate::database_helpers::boolean_converters::nullable_i8_to_optional_bool;
use crate::database_helpers::boolean_converters::i8_to_bool;
use derive_more::{Display, 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::sync::Arc;
// TODO: This duplicates the get_w2l_template_handler.
#[derive(Serialize)]
pub struct W2lTemplateRecordForResponse {
pub template_token: String,
pub template_type: String,
pub creator_user_token: String,
pub creator_username: String,
pub creator_display_name: String,
pub updatable_slug: String,
pub title: String,
pub frame_width: u32,
pub frame_height: u32,
pub duration_millis: u32,
pub maybe_image_object_name: Option<String>,
pub maybe_video_object_name: Option<String>,
pub is_mod_public_listing_approved: Option<bool>,
pub is_mod_disabled: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub deleted_at: Option<DateTime<Utc>>,
}
#[derive(Serialize)]
pub struct W2lTemplateRecordRaw {
pub template_token: String,
pub template_type: String,
pub creator_user_token: String,
pub creator_username: String,
pub creator_display_name: String,
pub updatable_slug: String,
pub title: String,
pub frame_width: i32,
pub frame_height: i32,
pub duration_millis: i32,
pub maybe_public_bucket_preview_image_object_name: Option<String>,
pub maybe_public_bucket_preview_video_object_name: Option<String>,
pub is_mod_public_listing_approved: Option<i8>,
pub is_mod_disabled: i8,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub deleted_at: Option<DateTime<Utc>>,
}
pub async fn select_w2l_template_by_token(
w2l_template_token: &str,
can_see_deleted: bool,
mysql_pool: &MySqlPool
) -> AnyhowResult<Option<W2lTemplateRecordForResponse>> {
let maybe_record = if can_see_deleted {
select_including_deleted(w2l_template_token, mysql_pool).await
} else {
select_without_deleted(w2l_template_token, mysql_pool).await
};
let template : W2lTemplateRecordRaw = match maybe_record {
Ok(template) => template,
Err(ref err) => {
match err {
RowNotFound => {
warn!("w2l template not found: {:?}", &err);
return Ok(None);
},
_ => {
warn!("w2l template query error: {:?}", &err);
return Err(anyhow!("database error"));
}
}
}
};
let template_for_response = W2lTemplateRecordForResponse {
template_token: template.template_token.clone(),
template_type: template.template_type.clone(),
creator_user_token: template.creator_user_token.clone(),
creator_username: template.creator_username.clone(),
creator_display_name: template.creator_display_name.clone(),
updatable_slug: template.updatable_slug.clone(),
title: template.title.clone(),
frame_width: if template.frame_width > 0 { template.frame_width as u32 } else { 0 },
frame_height: if template.frame_height > 0 { template.frame_height as u32 } else { 0 },
duration_millis: if template.duration_millis > 0 { template.duration_millis as u32 } else { 0 },
maybe_image_object_name: template.maybe_public_bucket_preview_image_object_name.clone(),
maybe_video_object_name: template.maybe_public_bucket_preview_video_object_name.clone(),
is_mod_public_listing_approved: nullable_i8_to_optional_bool(template.is_mod_public_listing_approved),
is_mod_disabled: i8_to_bool(template.is_mod_disabled),
created_at: template.created_at.clone(),
updated_at: template.updated_at.clone(),
deleted_at: template.deleted_at.clone(),
};
Ok(Some(template_for_response))
}
async fn select_including_deleted(
w2l_template_token: &str,
mysql_pool: &MySqlPool
) -> Result<W2lTemplateRecordRaw, sqlx::Error> {
sqlx::query_as!(
W2lTemplateRecordRaw,
r#"
SELECT
w2l.token as template_token,
w2l.template_type,
w2l.creator_user_token,
users.username as creator_username,
users.display_name as creator_display_name,
w2l.updatable_slug,
w2l.title,
w2l.frame_width,
w2l.frame_height,
w2l.duration_millis,
w2l.maybe_public_bucket_preview_image_object_name,
w2l.maybe_public_bucket_preview_video_object_name,
w2l.is_mod_approved as is_mod_public_listing_approved,
w2l.is_mod_disabled,
w2l.created_at,
w2l.updated_at,
w2l.deleted_at
FROM w2l_templates as w2l
JOIN users
ON users.token = w2l.creator_user_token
WHERE w2l.token = ?
"#,
w2l_template_token
)
.fetch_one(mysql_pool)
.await // TODO: This will return error if it doesn't exist
}
async fn select_without_deleted(
w2l_template_token: &str,
mysql_pool: &MySqlPool
) -> Result<W2lTemplateRecordRaw, sqlx::Error> {
sqlx::query_as!(
W2lTemplateRecordRaw,
r#"
SELECT
w2l.token as template_token,
w2l.template_type,
w2l.creator_user_token,
users.username as creator_username,
users.display_name as creator_display_name,
w2l.updatable_slug,
w2l.title,
w2l.frame_width,
w2l.frame_height,
w2l.duration_millis,
w2l.maybe_public_bucket_preview_image_object_name,
w2l.maybe_public_bucket_preview_video_object_name,
w2l.is_mod_approved as is_mod_public_listing_approved,
w2l.is_mod_disabled,
w2l.created_at,
w2l.updated_at,
w2l.deleted_at
FROM w2l_templates as w2l
JOIN users
ON users.token = w2l.creator_user_token
WHERE w2l.token = ?
AND w2l.deleted_at IS NULL
"#,
w2l_template_token
)
.fetch_one(mysql_pool)
.await // TODO: This will return error if it doesn't exist
}
@@ -27,6 +27,8 @@ use sqlx::mysql::MySqlDatabaseError;
use std::sync::Arc;
use crate::database_helpers::boolean_converters::nullable_i8_to_optional_bool;
// TODO: This is duplicated in query_w2l_template
/// For the URL PathInfo
#[derive(Deserialize)]
pub struct GetW2lTemplatePathInfo {
+2 -1
View File
@@ -5,4 +5,5 @@ pub mod get_w2l_inference_job_status;
pub mod get_w2l_result;
pub mod get_w2l_template;
pub mod get_w2l_upload_template_job_status;
pub mod list_w2l_templates;
pub mod list_w2l_templates;
pub mod set_w2l_template_mod_approval;
@@ -0,0 +1,169 @@
use actix_http::Error;
use actix_http::http::header;
use actix_web::cookie::Cookie;
use actix_web::dev::HttpResponseBuilder;
use actix_web::error::ResponseError;
use actix_web::http::StatusCode;
use actix_web::web::Path;
use actix_web::{Responder, web, HttpResponse, error, HttpRequest};
use crate::common_queries::query_w2l_template::select_w2l_template_by_token;
use crate::database_helpers::enums::{DownloadUrlType, CreatorSetVisibility, W2lTemplateType};
use crate::http_server::web_utils::ip_address::get_request_ip;
use crate::server_state::ServerState;
use crate::util::random_crockford_token::random_crockford_token;
use crate::validations::model_uploads::validate_model_title;
use crate::validations::passwords::validate_passwords;
use crate::validations::username::validate_username;
use derive_more::{Display, Error};
use log::{info, warn, log};
use regex::Regex;
use sqlx::error::DatabaseError;
use sqlx::error::Error::Database;
use sqlx::mysql::MySqlDatabaseError;
use std::sync::Arc;
/// For the URL PathInfo
#[derive(Deserialize)]
pub struct SetW2lTemplateModApprovalPathInfo {
w2l_template_token: String,
}
#[derive(Deserialize)]
pub struct SetW2lTemplateModApprovalRequest {
is_approved: bool,
}
#[derive(Serialize)]
pub struct SetW2lTemplateModApprovalSuccessResponse {
pub success: bool,
}
#[derive(Serialize)]
pub struct ErrorResponse {
pub success: bool,
pub error_reason: String,
}
#[derive(Debug, Display)]
pub enum SetW2lTemplateModApprovalError {
BadInput(String),
NotAuthorized,
NotFound,
ServerError,
}
impl ResponseError for SetW2lTemplateModApprovalError {
fn status_code(&self) -> StatusCode {
match *self {
SetW2lTemplateModApprovalError::BadInput(_) => StatusCode::BAD_REQUEST,
SetW2lTemplateModApprovalError::NotAuthorized => StatusCode::UNAUTHORIZED,
SetW2lTemplateModApprovalError::NotFound => StatusCode::NOT_FOUND,
SetW2lTemplateModApprovalError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
let error_reason = match self {
SetW2lTemplateModApprovalError::BadInput(reason) => reason.to_string(),
SetW2lTemplateModApprovalError::NotAuthorized => "unauthorized".to_string(),
SetW2lTemplateModApprovalError::NotFound => "not found".to_string(),
SetW2lTemplateModApprovalError::ServerError => "server error".to_string(),
};
let response = ErrorResponse {
success: false,
error_reason,
};
let body = match serde_json::to_string(&response) {
Ok(json) => json,
Err(_) => "{}".to_string(),
};
HttpResponseBuilder::new(self.status_code())
.set_header(header::CONTENT_TYPE, "application/json")
.body(body)
}
}
pub async fn set_w2l_template_mod_approval_handler(
http_request: HttpRequest,
path: Path<SetW2lTemplateModApprovalPathInfo>,
request: web::Json<SetW2lTemplateModApprovalRequest>,
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, SetW2lTemplateModApprovalError>
{
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);
SetW2lTemplateModApprovalError::ServerError
})?;
let user_session = match maybe_user_session {
Some(session) => session,
None => {
warn!("not logged in");
return Err(SetW2lTemplateModApprovalError::NotAuthorized);
}
};
if !user_session.can_approve_w2l_templates {
warn!("user is not allowed to approve templates: {}", user_session.user_token);
return Err(SetW2lTemplateModApprovalError::NotAuthorized);
}
let template_query_result = select_w2l_template_by_token(
&path.w2l_template_token,
true, // Only mods can perform this action
&server_state.mysql_pool,
).await;
let w2l_template = match template_query_result {
Err(e) => {
warn!("query error: {:?}", e);
return Err(SetW2lTemplateModApprovalError::ServerError);
}
Ok(None) => return Err(SetW2lTemplateModApprovalError::NotFound),
Ok(Some(template)) => template,
};
let ip_address = get_request_ip(&http_request);
let query_result = sqlx::query!(
r#"
UPDATE w2l_templates
SET
is_mod_approved = ?,
maybe_mod_user_token = ?
WHERE
token = ?
LIMIT 1
"#,
&request.is_approved,
&user_session.user_token,
&path.w2l_template_token,
)
.execute(&server_state.mysql_pool)
.await;
match query_result {
Ok(_) => {},
Err(err) => {
warn!("Update w2l mod approval status DB error: {:?}", err);
return Err(SetW2lTemplateModApprovalError::ServerError);
}
};
let response = SetW2lTemplateModApprovalSuccessResponse {
success: true,
};
let body = serde_json::to_string(&response)
.map_err(|e| SetW2lTemplateModApprovalError::ServerError)?;
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(body))
}
+1
View File
@@ -10,6 +10,7 @@
pub mod buckets;
pub mod common_queries;
pub mod database_helpers;
pub mod job_queries;
pub mod script_execution;
pub mod shared_constants;
+1
View File
@@ -10,6 +10,7 @@
pub mod buckets;
pub mod common_queries;
pub mod database_helpers;
pub mod job_queries;
pub mod script_execution;
pub mod shared_constants;
+1
View File
@@ -10,6 +10,7 @@
pub mod buckets;
pub mod common_queries;
pub mod database_helpers;
pub mod job_queries;
pub mod script_execution;
pub mod shared_constants;
+1
View File
@@ -10,6 +10,7 @@
pub mod buckets;
pub mod common_queries;
pub mod database_helpers;
pub mod job_queries;
pub mod script_execution;
pub mod shared_constants;